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 выводится 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

Другой вывод такой же, как «Обновление 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 решает эту проблему. Подробности причины в ответе caf. Большое спасибо кафе, и все они оказывают любезную помощь!


person ericzma    schedule 26.01.2011    source источник
comment
Прочтите справочную страницу: при ошибке -1 и устанавливается errno. Он может сообщить вам что-то полезное, а может и не сказать, но вы должны распечатать его, прежде чем просить других людей изучить его.   -  person Tony Delroy    schedule 26.01.2011
comment
Почему вы сами указываете базовый адрес?   -  person sarnold    schedule 26.01.2011
comment
@ Тони: Спасибо! Я распечатал сообщение об ошибке, в нем говорится, что невозможно выделить память.   -  person ericzma    schedule 26.01.2011
comment
@sarnold: мы хотим написать код на ассемблере для чтения/записи памяти в определенном диапазоне. Я знаю, что это немного странно для обычного приложения.   -  person ericzma    schedule 26.01.2011
comment
@Zhiqiang Ма, ага! Спасибо :) Интересно, это рандомизация макета адресного пространства, дающая вам разные "свободные пространства" при каждом прогоне?   -  person sarnold    schedule 26.01.2011
comment
@sarnold: Хорошая догадка! :) В чем-то похоже. Мы просто предоставляем программе статический диапазон памяти для использования. Программа может свободно читать/записывать в этом конкретном диапазоне независимо от того, для чего она использует этот диапазон.   -  person ericzma    schedule 26.01.2011
comment
@sarnold: На самом деле, к тому времени я работал над проектом DVM ( zhiqiangma.com/homepage/publication/ma-vee12-dvm.pdf). Мы используем mmap для поддержки унифицированной и распределенной памяти DVM путем повторного использования оборудования и механизма обработки ошибок страниц в ОС.   -  person ericzma    schedule 07.11.2012
comment
В случае, если кто-то попал сюда, пытаясь mmap() использовать кольцевой буфер RX сокета... кажется, что существует ограничение в 4 ГБ при настройке кольца RX с помощью setsockopt(). Это отдельная проблема для чего я открыл здесь вопрос.   -  person rizard    schedule 16.04.2018


Ответы (5)


Если на самом деле у вас не настроено значительно больше 8G подкачки, то это большое сопоставление, скорее всего, не удастся.

Вы можете добавить флаг MAP_NORESERVE к mmap(), чтобы он не резервировал пространство подкачки для сопоставления заранее.

person caf    schedule 26.01.2011
comment
Отлично и спасибо! После добавления этого флага mmap будет работать успешно. На самом деле у меня только 2 ГБ памяти + 2 ГБ подкачки. - person ericzma; 26.01.2011
comment
Вы должны знать, что MAP_NORESERVE полностью игнорируется в системах, настроенных на строгую плату за фиксацию (без чрезмерной фиксации). - person R.. GitHub STOP HELPING ICE; 22.09.2011

Сколько физической памяти доступно? В Linux есть два различных режима выделения адресного пространства: выделение памяти при записи (т. е. режим избыточной фиксации) или выделение памяти при выделении адресного пространства. Вы можете проверить, прочитав два файла в procfs:

cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/overcommit_ratio

Если значение overcommit_memory отлично от 0, то каждое выделение адресного пространства должно поддерживаться физической памятью (ОЗУ + пространство подкачки). ядро ​​с радостью раздаст адресное пространство, но память будет выделена только в том случае, если данные будут записаны в выделенное адресное пространство. И тогда память выделяется не на полное зарезервированное адресное пространство, а только на те страницы, которые были затронуты. Это похоже на бронирование авиабилета: авиакомпании обычно продают больше билетов, чем мест на рейсе, ожидая, что не все забронированные пассажиры действительно появятся. Теперь вы можете задаться вопросом, что произойдет, если все программы будут использовать все пространство… Что ж, тогда произойдет кое-что неприятное: Linux Out Of Memory Killer нанесет ущерб вашей системе и, скорее всего, убьет те процессы, которые вам больше всего нужны, из-за это тайная эвристика.

overcommit_ratio сообщает ядру

  • в режиме overcommit, в каком отношении физическая память может быть перегружена, т. е. насколько больше адресного пространства может быть выдано, чем имеется физической памяти.

  • в режиме без избыточной фиксации, сколько свободной памяти оставить

Так что, возможно, режим overcommit просто различается между системами.

person datenwolf    schedule 26.01.2011
comment
Учет виртуальных машин Linux фактически имеет три режима: эвристическая избыточная фиксация (0), всегда избыточная фиксация (1) и никогда избыточная фиксация (2). - person caf; 26.01.2011
comment
Большое спасибо! В обеих системах overcommit_ratio равен 50, а overcommit_memory равен 0. У меня только 2 ГБ памяти + 2 ГБ подкачки. После добавления флага MAP_NORESERVE, предложенного @caf, он может отображать 8 ГБ памяти. - person ericzma; 26.01.2011

Просто запустил ваш код в 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);
person Maxim Egorushkin    schedule 26.01.2011
comment
@Максим Спасибо! Я добавил system(pmap -x $$) до и после вызова mmap. Я разместил обновленный результат (обновление 2). Я не уверен, что использую его правильно. Пожалуйста, помогите мне взглянуть на него. - person ericzma; 26.01.2011
comment
Ой, глупый я; $$ получает pid shell. pmap сбрасывает не ту карту. Замените system("pmap -x $$") чем-то вроде char pid[10]; sprintf(pid, "%i", getpid()); execlp("pmap",pid, (char *)NULL); - person sarnold; 26.01.2011
comment
@sarnold: я изменил код и распечатал новый результат (обновление 3). Я генерирую правильную команду и выполняю ее, вызывая system. В коде, который вы даете, отсутствует параметр «-x». Я думаю, что диапазон (от 0x100000000 до 0x200000000) не используется в соответствии с выводом pmap. - person ericzma; 26.01.2011
comment
@Максим Егорушкин: Я использовал подобный (ваш лучше: безопаснее ;) ), но все равно спасибо. Я также разместил вывод в обновлении 3. - person ericzma; 26.01.2011

Поскольку вы пытаетесь сопоставить конкретный адрес, это будет зависеть от текущей схемы памяти для вашего процесса, когда вы вызываете mmap. Стратегия, по которой выполняется запрос, зависит от системы, справочная страница Linux говорит что-то вроде «подсказки».

Так что, возможно, в первом случае просто не хватает места в виртуальном адресном пространстве вашего процесса для выполнения запроса, так как в этом диапазоне уже есть другое отображение.

Хорошей идеей будет проверить, связано ли это с этим, чтобы проверить, удалось ли вам, когда вы не даете подсказку addr.

person Jens Gustedt    schedule 26.01.2011
comment
Спасибо за предложение! Я меняю адрес на NULL в вызове mmap, но он все равно терпит неудачу и дает сообщение об ошибке Cannot allocate memory. - person ericzma; 26.01.2011

Может быть, вы упираетесь в ограничения ресурсов? Попробуйте добавить system("ulimit -m -v");, чтобы распечатать объем памяти и адресного пространства, которые могут быть выделены.

EDIT: Ну, у меня нет идей. Извиняюсь. После очистки ошибок и предупреждений в коде у меня есть этот источник:

#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.
person sarnold    schedule 26.01.2011
comment
Похоже на это: ENOMEM Нет доступной памяти, или было бы превышено максимальное количество отображений процесса. - person Maxim Egorushkin; 26.01.2011
comment
Я добавил эту команду непосредственно перед вызовом mmap. Результат показан в «Обновлении 4». Он показывает «неограниченный» как для максимального размера памяти, так и для виртуальной памяти. - person ericzma; 26.01.2011
comment
Все равно спасибо за помощь! Я также пробовал разные системы с разными версиями ядра/gcc/libc. Только на моем ноутбуке с Fedora 14 (система 1) эта программа не работает. - person ericzma; 26.01.2011
comment
@Zhiqiang Ma, если у @datenwolf нет правильного ответа, я думаю, пришло время отправить отчет об ошибке в Fedora :) - person sarnold; 26.01.2011
comment
caf дает правильное решение. mmap пытается зарезервировать место для памяти объемом 8 ГБ, а на моем компьютере ее нет. Добавление флага MAP_NORESERVE позволит избежать резервирования места. Это не ошибка Fedora, это ошибка моего кода. Но приятно это узнать. - person ericzma; 26.01.2011