Mmap не работает, если длина превышает 4 ГБ
(Правильный код в "Обновлении 5")
Я попытался отобразить диапазон памяти от 0x100000000 до 0x200000000 в этом примере кода C:
#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:
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 не работает.
Обновление 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
Другой вывод такой же, как "Обновление 3" (по-прежнему не удается), за исключением pid.
Обновление 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 решает эту проблему. Подробности причины в ответе кафе. Большое спасибо, кафе, и все они оказывают добрую помощь!
5 ответов
Если на самом деле настроено не более 8G подкачки, то такое большое сопоставление, скорее всего, не удастся.
Вы можете добавить MAP_NORESERVE
флаг для mmap()
сказать ему не резервировать место подкачки для отображения перед.
Сколько физической памяти там доступно? В Linux есть два различных режима выделения адресного пространства: выделение памяти при записи (т. Е. Режим overcommit) или выделение памяти при выделении адресного пространства. Вы можете проверить, прочитав два файла в procfs:
cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/overcommit_ratio
Если overcommit_memory не равно 0, то каждое выделение адресного пространства должно поддерживаться физической памятью (RAM + пространство подкачки), если overcommit_memory равно 0, то память перегружается, то есть ядро с радостью раздает адресное пространство, но память будет только выделяется, если данные записываются в выделенное адресное пространство. И тогда память выделяется не для полного зарезервированного адресного пространства, а только для тех страниц, к которым обращаются. Это похоже на бронирование авиабилета: авиакомпании обычно продают больше билетов, чем мест на рейсе, ожидая, что не все забронированные пассажиры действительно появятся. Теперь вы можете задаться вопросом, что произойдет, если все программы будут использовать все пространство… Ну, тогда начнутся некоторые неприятные вещи: Убийца нехватки памяти в Linux вызовет хаос в вашей системе и, скорее всего, убьет те процессы, которые вам нужны больше всего, из-за это тайная эвристика.
overcommit_ratio сообщает ядру
в режиме overcommit, в котором соотношение физической памяти может быть чрезмерно загружено, то есть, сколько может быть выделено больше адресного пространства, чем физической памяти.
в режиме non-overcommit сколько свободной памяти оставить
Так что, возможно, режим overcommit просто различается в разных системах.
Просто запустил ваш код на Fedora 13 и он выдает результат 2.
Проверьте errno, когда mmap() возвращает MAP_FAILED (-1). Вы также можете вставить следующую строку до и после вызова mmap, чтобы увидеть, есть ли у вас место в виртуальном адресном пространстве процесса для области 4 ГБ:
system("pmap -x $$");
Обновление: выше фактически печатает карту дочернего процесса. Правильный код:
char buf[0x100];
snprintf(buf, sizeof buf, "pmap -x %u", (unsigned)getpid());
system(buf);
Может быть, вы сталкиваетесь с ограниченностью ресурсов? Попробуйте добавить system("ulimit -m -v");
распечатать объем памяти и адресного пространства, которые могут быть выделены.
РЕДАКТИРОВАТЬ: Ну, у меня нет идей. Сожалею. После очистки ошибок и предупреждений в коде, у меня есть этот источник:
#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): %lu\n", sizeof(size_t));
printf("(uint64_t)0x100000000: %lx\n", (uint64_t)0x100000000);
printf("1L << 33: %lx\n", 1L << 33);
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);
printf("rr_addr: %p, %lu \n", rr_addr, rr_addr);
if (rr_addr == MAP_FAILED) {
perror("mmap error");
}
return 0;
}
и этот вывод:
sizeof(size_t): 8
(uint64_t)0x100000000: 100000000
1L << 33: 200000000
pmap -x 23819
23819: ./zhiqiang
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 0 4 0 r-x-- zhiqiang
0000000000600000 0 4 4 r---- zhiqiang
0000000000601000 0 4 4 rw--- zhiqiang
00007f37b3c27000 0 260 0 r-x-- libc-2.12.1.so
00007f37b3da1000 0 0 0 ----- libc-2.12.1.so
00007f37b3fa0000 0 16 16 r---- libc-2.12.1.so
00007f37b3fa4000 0 4 4 rw--- libc-2.12.1.so
00007f37b3fa5000 0 12 12 rw--- [ anon ]
00007f37b3faa000 0 108 0 r-x-- ld-2.12.1.so
00007f37b41aa000 0 12 12 rw--- [ anon ]
00007f37b41c7000 0 12 12 rw--- [ anon ]
00007f37b41ca000 0 4 4 r---- ld-2.12.1.so
00007f37b41cb000 0 4 4 rw--- ld-2.12.1.so
00007f37b41cc000 0 4 4 rw--- [ anon ]
00007fff70cf8000 0 12 12 rw--- [ stack ]
00007fff70dff000 0 4 0 r-x-- [ anon ]
ffffffffff600000 0 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 3912 464 88
pmap -x 23819
23819: ./zhiqiang
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 0 4 0 r-x-- zhiqiang
0000000000600000 0 4 4 r---- zhiqiang
0000000000601000 0 4 4 rw--- zhiqiang
0000000400000000 0 0 0 rw--- [ anon ]
00007f37b3c27000 0 260 0 r-x-- libc-2.12.1.so
00007f37b3da1000 0 0 0 ----- libc-2.12.1.so
00007f37b3fa0000 0 16 16 r---- libc-2.12.1.so
00007f37b3fa4000 0 4 4 rw--- libc-2.12.1.so
00007f37b3fa5000 0 12 12 rw--- [ anon ]
00007f37b3faa000 0 108 0 r-x-- ld-2.12.1.so
00007f37b41aa000 0 12 12 rw--- [ anon ]
00007f37b41c7000 0 12 12 rw--- [ anon ]
00007f37b41ca000 0 4 4 r---- ld-2.12.1.so
00007f37b41cb000 0 4 4 rw--- ld-2.12.1.so
00007f37b41cc000 0 4 4 rw--- [ anon ]
00007fff70cf8000 0 12 12 rw--- [ stack ]
00007fff70dff000 0 4 0 r-x-- [ anon ]
ffffffffff600000 0 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 8392520 464 88
rr_addr: 0x400000000, 17179869184
И детали моей системы:
Linux haig 2.6.35-24-generic #42-Ubuntu SMP Thu Dec 2 02:41:37 UTC 2010 x86_64 GNU/Linux
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
GNU C Library (Ubuntu EGLIBC 2.12.1-0ubuntu10.1) stable release version 2.12.1, by Roland McGrath et al.
Поскольку вы пытаетесь сопоставить конкретный адрес, он будет зависеть от текущей структуры памяти вашего процесса при вызове mmap
, Стратегия, по которой выполняется адрес, зависит от системы, на странице руководства linux говорится что-то вроде "подсказки".
Так что, возможно, в первом случае в виртуальном адресном пространстве вашего процесса просто недостаточно места для выполнения запроса, поскольку в этом диапазоне уже есть другое отображение.
Хорошая идея, чтобы проверить, если это связано с этим, было бы проверить, если вы добились успеха, когда вы не даете addr
намек.