Consider the following program:
#include <sys/mman.h>
#include <stdlib.h>
#include <errno.h>
int
main()
{
errno = 0;
mlockall(MCL_FUTURE);
char *a = malloc(1);
if (!a)
exit(errno);
munlockall();
exit(0);
}
When running as a normal user I get:
~ ./a.out
~ echo $?
11
From /usr/include/asm-generic/errno-base.h
:
#define EAGAIN 11 /* Try again */
When running it as root or when passing MCL_FUTURE | MCL_CURRENT
MCL_FUTURE | MCL_CURRENT
it runs successfully. I assumed either the permissions were insufficient or the flags were wrong, but neither EPERM nor EINVAL was returned.
That error is not specified in the man page of neither functions, nor in the POSIX specification for mlockall. Placing a printf after mlockall reveals that it is malloc who is setting errno.
And even more odd, malloc doesn't seem to set EAGAIN (or I'm looking in the wrong place):
/usr/src/glibc/glibc-2.19/malloc grep -r . -e EAGAIN
So what's the deal?
~ uname -r 18:15:04
3.16-2-486
~ gcc --version 18:15:05
gcc (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
~ ldd --version 18:15:11
ldd (Debian GLIBC 2.19-18+deb8u1) 2.19
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
~ 18:15:15
Your mlockall()
call asks for all future memory allocations to be locked. However, the OS sets a maximum amount of memory that can be locked by any one unprivileged process. You can query this amount with getrlimit(RLIMIT_MEMLOCK,...)
. On my system it is 65536 bytes.
Now when I run your program on my system, using strace(1)
to see what system calls are made, I get the following:
mlockall(MCL_FUTURE) = 0
brk(0) = 0x2318000
brk(0x2339000) = 0x2318000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
exit_group(11) = ?
So malloc
first uses brk
to try to allocate 135168 bytes ( 0x2339000-0x2318000
). This fails because it exceeds the lock limit, and so brk
leaves the "break point" (the top of the process's data segment) unchanged. (See the note in the brk(2)
man page about the differing conventions between the C library and kernel versions of brk()
.)
malloc
then tries instead to allocate 1048576 bytes using mmap
. This also fails (as it exceeds 65536 bytes), and here we see the EAGAIN
error code being returned. The man page for mmap(2)
documents that errno
is set to EAGAIN
if "The file has been locked, or too much memory has been locked", the latter of which is exactly the case here. malloc
, like many library functions, will pass through the errno
value left by system calls that it makes, so EAGAIN
is what you see when malloc
returns.
(The extra mmap
calls with PROT_NONE
seem to be intended to reserve some address space for future use, and help ensure that future allocations are aligned in an appropriate way. See malloc/arena.c
in the glibc source for gory details. They fail too in this case, but that's not so relevant.)
So in short, the issue is that malloc
tries to ask the OS for a significantly larger amount of memory than you, the user, requested. This is for efficiency, since in most cases you are going to go on to allocate more small chunks of memory, and you don't want to make a system call for each one. But this amount exceeds the limit for locked memory, so it fails. EAGAIN
is the error code set by the mmap
system call in this case.
Perhaps the malloc
man page should mention this possible errno
setting, but it's pretty common that higher-level library functions don't describe all the possible ways errno
could be set by the underlying system calls. (For instance, fprintf(3)
calls write(2)
, which could set errno
to ENOSPC
if the disk is full, but you won't find any mention of that in the fprintf(3)
man page.) You're just supposed to know.
If you want to use mlockall(MCL_FUTURE)
, then you probably can't plan to allocate memory using malloc(3)
. You'll have to get it manually from sbrk(2)
or mmap(2)
, and of course, plan to keep it under the appropriate limit or fail gracefully. This is pretty inconvenient and restrictive, so if you have a need for some locked memory, and you're not root, you probably want to just use mlock(2)
on sufficiently small objects instead.
What's the return value from mlockall()
?
Per the POSIX standard :
[EAGAIN]
Some or all of the memory identified by the operation could not be locked when the call was made.
Per the Linux man page :
EAGAIN Some or all of the specified address range could not be locked.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.