当长度大于 4GB 时 mmap 失败
(正确的代码位于“更新 5”中)
在此示例 C 代码中,我尝试将内存范围从 0x100000000 映射到 0x200000000:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
int main(void)
{
uint64_t* rr_addr = 0;
uint64_t i = 17179869184;
printf("\nsizeof(size_t): %llu\n", sizeof(size_t));
printf("(uint64_t)0x100000000: %llx\n", (uint64_t)0x100000000);
printf("1L << 33: %llx\n", 1L << 33);
rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
printf("rr_addr: %p, %llu \n", rr_addr, rr_addr);
if (rr_addr == MAP_FAILED) {
perror("mmap error");
}
return 0;
}
在不同的系统(Linux、gcc)上,我得到不同的结果:
结果 1:< /strong>
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
rr_addr: 0xffffffffffffffff, 18446744073709551615
mmap error: Cannot allocate memory
系统信息 (Fedora 14):
Linux localhost.localdomain 2.6.35.10-74.fc14.x86_64 #1 SMP Thu Dec 23 16:04:50 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux
gcc (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4)
glibc: 2.12.90-21
结果 2:
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
rr_addr: 0x400000000, 17179869184
系统信息 (Fedora 12):
Linux wiles 2.6.32.13 #2 SMP Fri Sep 10 01:29:43 HKT 2010 x86_64 x86_64 x86_64 GNU/Linux
gcc (GCC) 4.4.4 20100630 (Red Hat 4.4.4-10)
glibc verison: 2.11.2-1
我期望“结果 2”。也许我的代码有问题。
请帮帮我。
更新1:如果 mmap 失败,则会打印 errno。
更新 3:将 mmap 调用更改为这些行后:
char *cmd[20];
sprintf(cmd, "pmap -x %i", getpid());
printf("%s\n", cmd);
system(cmd);
rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
printf("%s\n", cmd);
system(cmd);
结果:
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
pmap -x 5618
5618: ./test
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- test
0000000000600000 4 4 4 rw--- test
00007f1cc941e000 1640 280 0 r-x-- libc-2.12.90.so
00007f1cc95b8000 2044 0 0 ----- libc-2.12.90.so
00007f1cc97b7000 16 16 16 r---- libc-2.12.90.so
00007f1cc97bb000 4 4 4 rw--- libc-2.12.90.so
00007f1cc97bc000 24 16 16 rw--- [ anon ]
00007f1cc97c2000 132 108 0 r-x-- ld-2.12.90.so
00007f1cc99c6000 12 12 12 rw--- [ anon ]
00007f1cc99e0000 8 8 8 rw--- [ anon ]
00007f1cc99e2000 4 4 4 r---- ld-2.12.90.so
00007f1cc99e3000 4 4 4 rw--- ld-2.12.90.so
00007f1cc99e4000 4 4 4 rw--- [ anon ]
00007fffa0da8000 132 8 8 rw--- [ stack ]
00007fffa0dff000 4 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 4040 476 80
pmap -x 5618
5618: ./test
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- test
0000000000600000 4 4 4 rw--- test
00007f1cc941e000 1640 280 0 r-x-- libc-2.12.90.so
00007f1cc95b8000 2044 0 0 ----- libc-2.12.90.so
00007f1cc97b7000 16 16 16 r---- libc-2.12.90.so
00007f1cc97bb000 4 4 4 rw--- libc-2.12.90.so
00007f1cc97bc000 24 16 16 rw--- [ anon ]
00007f1cc97c2000 132 108 0 r-x-- ld-2.12.90.so
00007f1cc99c6000 12 12 12 rw--- [ anon ]
00007f1cc99e0000 8 8 8 rw--- [ anon ]
00007f1cc99e2000 4 4 4 r---- ld-2.12.90.so
00007f1cc99e3000 4 4 4 rw--- ld-2.12.90.so
00007f1cc99e4000 4 4 4 rw--- [ anon ]
00007fffa0da8000 132 8 8 rw--- [ stack ]
00007fffa0dff000 4 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 4040 476 80
rr_addr: 0xffffffffffffffff, 18446744073709551615
mmap error: Cannot allocate memory
更新 4:添加 "system("ulimit -m -v");"在调用 mmap 之前: ulimit 的输出是:
max memory size (kbytes, -m) unlimited
virtual memory (kbytes, -v) unlimited
除了 pid 之外,其他输出与“Update 3”相同(仍然失败)。
更新5:更新后的代码适用于两个系统:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
int main(void)
{
uint64_t* rr_addr = 0;
uint64_t i = 17179869184;
uint64_t len = 0;
char cmd[20];
printf("\nsizeof(size_t): %llu\n", sizeof(size_t));
len = (1UL << 32);
printf("len: %llx\n", len);
snprintf(cmd, sizeof cmd, "pmap -x %i", getpid());
printf("%s\n", cmd);
system(cmd);
system("ulimit -m -v");
rr_addr = mmap((void*)i, len, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
printf("%s\n", cmd);
system(cmd);
printf("rr_addr: %p, %llu \n", rr_addr, rr_addr);
if (rr_addr == MAP_FAILED) {
perror("mmap error");
}
return 0;
}
@caf 给出了正确的答案:将 MAP_NORESERVE 标志添加到 mmap 可以解决此问题。详细原因在caf的回答中。非常感谢咖啡馆和所有这些给予的帮助!
(The correct code is in 'Update 5')
I tried to map a range of memory from 0x100000000 to 0x200000000 in this example C code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
int main(void)
{
uint64_t* rr_addr = 0;
uint64_t i = 17179869184;
printf("\nsizeof(size_t): %llu\n", sizeof(size_t));
printf("(uint64_t)0x100000000: %llx\n", (uint64_t)0x100000000);
printf("1L << 33: %llx\n", 1L << 33);
rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
printf("rr_addr: %p, %llu \n", rr_addr, rr_addr);
if (rr_addr == MAP_FAILED) {
perror("mmap error");
}
return 0;
}
On different systems (Linux, gcc), I get different results:
Result 1:
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
rr_addr: 0xffffffffffffffff, 18446744073709551615
mmap error: Cannot allocate memory
System info (Fedora 14):
Linux localhost.localdomain 2.6.35.10-74.fc14.x86_64 #1 SMP Thu Dec 23 16:04:50 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux
gcc (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4)
glibc: 2.12.90-21
Result 2:
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
rr_addr: 0x400000000, 17179869184
System info (Fedora 12):
Linux wiles 2.6.32.13 #2 SMP Fri Sep 10 01:29:43 HKT 2010 x86_64 x86_64 x86_64 GNU/Linux
gcc (GCC) 4.4.4 20100630 (Red Hat 4.4.4-10)
glibc verison: 2.11.2-1
I expect "Result 2". Maybe there is something wrong with my code.
Please help me out.
Update 1: errno is printed out if mmap fails.
Update 3: after changing the mmap call to these lines:
char *cmd[20];
sprintf(cmd, "pmap -x %i", getpid());
printf("%s\n", cmd);
system(cmd);
rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
printf("%s\n", cmd);
system(cmd);
Result:
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
pmap -x 5618
5618: ./test
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- test
0000000000600000 4 4 4 rw--- test
00007f1cc941e000 1640 280 0 r-x-- libc-2.12.90.so
00007f1cc95b8000 2044 0 0 ----- libc-2.12.90.so
00007f1cc97b7000 16 16 16 r---- libc-2.12.90.so
00007f1cc97bb000 4 4 4 rw--- libc-2.12.90.so
00007f1cc97bc000 24 16 16 rw--- [ anon ]
00007f1cc97c2000 132 108 0 r-x-- ld-2.12.90.so
00007f1cc99c6000 12 12 12 rw--- [ anon ]
00007f1cc99e0000 8 8 8 rw--- [ anon ]
00007f1cc99e2000 4 4 4 r---- ld-2.12.90.so
00007f1cc99e3000 4 4 4 rw--- ld-2.12.90.so
00007f1cc99e4000 4 4 4 rw--- [ anon ]
00007fffa0da8000 132 8 8 rw--- [ stack ]
00007fffa0dff000 4 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 4040 476 80
pmap -x 5618
5618: ./test
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- test
0000000000600000 4 4 4 rw--- test
00007f1cc941e000 1640 280 0 r-x-- libc-2.12.90.so
00007f1cc95b8000 2044 0 0 ----- libc-2.12.90.so
00007f1cc97b7000 16 16 16 r---- libc-2.12.90.so
00007f1cc97bb000 4 4 4 rw--- libc-2.12.90.so
00007f1cc97bc000 24 16 16 rw--- [ anon ]
00007f1cc97c2000 132 108 0 r-x-- ld-2.12.90.so
00007f1cc99c6000 12 12 12 rw--- [ anon ]
00007f1cc99e0000 8 8 8 rw--- [ anon ]
00007f1cc99e2000 4 4 4 r---- ld-2.12.90.so
00007f1cc99e3000 4 4 4 rw--- ld-2.12.90.so
00007f1cc99e4000 4 4 4 rw--- [ anon ]
00007fffa0da8000 132 8 8 rw--- [ stack ]
00007fffa0dff000 4 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 4040 476 80
rr_addr: 0xffffffffffffffff, 18446744073709551615
mmap error: Cannot allocate memory
Update 4: add "system("ulimit -m -v");" just before calling mmap:
The output of ulimit is:
max memory size (kbytes, -m) unlimited
virtual memory (kbytes, -v) unlimited
The other output is the same as 'Update 3' (still fails) except the pid.
Update 5: the updated code which works on both systems:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
int main(void)
{
uint64_t* rr_addr = 0;
uint64_t i = 17179869184;
uint64_t len = 0;
char cmd[20];
printf("\nsizeof(size_t): %llu\n", sizeof(size_t));
len = (1UL << 32);
printf("len: %llx\n", len);
snprintf(cmd, sizeof cmd, "pmap -x %i", getpid());
printf("%s\n", cmd);
system(cmd);
system("ulimit -m -v");
rr_addr = mmap((void*)i, len, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
printf("%s\n", cmd);
system(cmd);
printf("rr_addr: %p, %llu \n", rr_addr, rr_addr);
if (rr_addr == MAP_FAILED) {
perror("mmap error");
}
return 0;
}
The right answer is given by @caf: adding the MAP_NORESERVE flag to mmap solves this problem. Details of the reason are in caf's answer. Thanks a lot caf and all these give kind help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您实际上没有配置明显超过 8G 的交换区,则该大型映射可能会失败。
您可以将
MAP_NORESERVE
标志添加到mmap()
中,告诉它不要预先为映射保留任何交换空间。If you do not actually have significantly more than 8G of swap configured, then that large mapping is likely to fail.
You can add the
MAP_NORESERVE
flag tommap()
to tell it not to reserve any swap space for the mapping up front.有多少可用物理内存? Linux 有两种不同的地址空间分配模式:写入时分配内存(即过量使用模式)或地址空间分配时分配内存。您可以通过读取 procfs 中的两个文件进行检查:
如果 overcommit_memory 不是 0,则每个地址空间分配都必须由物理内存(RAM + 交换空间)支持,如果 overcommit_memory 是 > 0,则内存被过度使用,即内核会愉快地分配地址空间,但只有当数据写入分配的地址空间时才会分配内存。然后,内存不会分配给完整的保留地址空间,而只会分配给那些被触及的页面。这有点像预订机票:航空公司通常出售的机票多于航班上的座位数,并且预计并非所有预订的乘客都会真正出现。现在您可能想知道,如果所有程序都使用完整空间会发生什么……那么一些令人讨厌的事情就会发生:Linux Out Of Memory Killer 会对您的系统造成严重破坏,并很可能杀死您最需要的进程,因为这是神秘的启发法。
overcommit_ratio 告诉内核
在过度使用模式下物理内存可以被过度使用的比率,即可以分配比物理内存多多少的地址空间。
在非过量使用模式下要保留多少备用内存
所以也许过量使用模式只是在系统之间有所不同。
How much physical memory is there available? Linux has two distinct modes for address space allocation: Memory allocation on write (i.e. overcommit mode) or memory allocation on address space allocation. You can check by reading two files in procfs:
If overcommit_memory is not 0, then every address space allocation must be backed by physical memory (RAM + swap space), if overcommit_memory is 0, then memory is overcommited, i.e. the kernel will happily hand out address space, but the memory will be only allocated if data is writen to the allocated address space. And then memory is not allocated for the full reserved address space, but only for those pages that are touched. This is kinda like booking a flight ticket: Airlines usually sell more tickets than there are seats on a flight, expecting not all booked passengers will actually show up. Now you may wonder, what happens if all programs make use of the full space… Well then some nasty thing kicks in: The Linux Out Of Memory Killer will wreak havoc on your system and very likely kill those processes you need the most, due to it's arcane heuristics.
overcommit_ratio tells the kernel
in overcommit mode to which ratio physical memory may be overcommited, i.e. how much more address space may be handed out, than there is physical memory.
in non-overcommit-mode how much spare memory to keep
So maybe the overcommit mode just differs between the systems.
刚刚在 Fedora 13 上运行您的代码,它生成结果 2。
当 mmap() 返回 MAP_FAILED (-1) 时检查 errno。您还可以在 mmap 调用之前和之后粘贴以下行,以查看进程的虚拟地址空间中是否有空间
4GB 区域:
更新:
上面实际上打印了子进程的map。正确代码:
Just ran your code on Fedora 13 and it produces result 2.
Check errno when mmap() returns MAP_FAILED (-1). You can also stick the following line before and after mmap call to see if you've got space in the virtual address space of the process for
a 4GB region:
Update:
The above actually prints the map of the child process. Correct code:
由于您尝试映射到特定地址,因此当您调用
mmap
时,它将取决于进程的当前内存布局。满足请求的地址的策略取决于系统,Linux 手册页有一些“提示”。因此,也许在第一种情况下,进程的虚拟地址空间中根本没有足够的空间来满足请求,因为该范围内已经存在另一个映射。
检查这是否与此相关的一个好主意是检查当您不给出
addr
提示时是否成功。Since you try to map to a specific address, it will depend on the current memory layout for your process when you call
mmap
. The strategy at which address the request is fulfill is system dependent, the linux man page says something of a "hint".So maybe in the first case there simply not enough room in the virtual address space of your process to fulfill the request, since there is already another mapping in the way in that range.
A good idea to check if this is related to that would be to check if you succeed when you don't give the
addr
hint.也许您遇到了资源限制?尝试添加
system("ulimit -m -v");
以打印出可能分配的内存和地址空间量。编辑:好吧,我没有主意了。对不起。清除代码中的错误和警告后,我有这个源:
和这个输出:
以及我的系统的详细信息:
Maybe you are running into resource limits? Try adding
system("ulimit -m -v");
to print out the amount of memory and address space that may be allocated.EDIT: Well, I'm out of ideas. Sorry. After cleaning up the errors and warnings in the code, I have this source:
and this output:
And details of my system: