Прежде всего, вы не хотите MAP_GROWSDOWN
, и дело не в том, как работает стек основных потоков. Анализ отображения памяти процесса с помощью pmap. [стек] Ничто не использует это, и почти ничего не должно использовать это. Материал на странице руководства, в котором говорится, что он «используется для стеков», неверен и должен быть исправлен.
Я подозреваю, что это может быть ошибка (потому что ничто не использует его, поэтому обычно никто не заботится и даже не замечает, если оно сломается.)
Ваш код работает для меня, если я изменю вызов mmap
на карту более чем на 1 страницу. В частности, я попробовал 4096 * 100
. Я использую Linux 5.0.1 (Arch Linux) на голом железе (Skylake).
/proc/PID/smaps
действительно показывает флаг gd
.
И затем (при однократном шаге asm) запись maps
фактически меняется на более низкий начальный адрес, но с тем же конечным адресом, поэтому она буквально растет вниз, когда я начинаю с сопоставления 400k. Это дает начальное выделение 400 КБ выше обратного адреса, которое увеличивается до 404 КБ при запуске программы. (Размер для _GROWSDOWN
сопоставления составляет , а не предел роста или что-то в этом роде.)
https://bugs.centos.org/view.php?id=4767 может быть связано; что-то изменилось между версиями ядра в CentOS 5.3 и 5.5. И / или это было связано с работой на виртуальной машине (5.3), а не с ростом и отказом от голого металла (5.5).
Я упростил C для использования ptr[-4095]
и т. Д .:
int main(void){
volatile char *ptr = mmap(NULL, 4096*100,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK | MAP_GROWSDOWN,
-1, 0);
if(ptr == MAP_FAILED){
int error_code = errno;
fprintf(stderr, "Cannot do MAP_FIXED mapping."
"Error code = %d, details = %s\n", error_code, strerror(error_code));
exit(EXIT_FAILURE);
}
ptr[0] = 'a'; //address returned by mmap
ptr[-4095] = 'b'; // grow by 1 page
}
Компиляция с gcc -Og
дает asm, который хорошо подходит для одного шага.
Кстати, разные слухи об удалении флага из glibc явно ошибочны. Этот источник компилируется, и ясно, что он также поддерживается ядром, а не игнорируется. (Хотя поведение, которое я вижу с размером 4096 вместо 400 кБ, в точности соответствует тому, что флаг молча игнорируется. Однако gd
VmFlag все еще присутствует в smaps
, поэтому он не игнорируется на этом этапе.)
Я проверил, и было место для его роста, не приближаясь к другому отображению. Итак, IDK, почему он не вырос, когда отображение GD было всего 1 страницей. Я попробовал пару раз, и он каждый раз работал с ошибками. С большим начальным отображением он никогда не ошибался.
Оба раза были с сохранением возвращаемого значения mmap (первой страницы собственно сопоставления), затем хранилище на 4095 байт ниже этого.