Что такое «автоматическое расширение стека»?

getrlimit (2) имеет следующее определение в man-страницах:

RLIMIT_AS Максимальный размер виртуальной памяти процесса (адресного пространства) в байтах. Этот предел влияет на вызовы на brk (2), mmap (2) и mremap (2), которые не работают с ошибкой ENOMEM при превышении этого предела. Также произойдет автоматическое расширение стека (и генерируется SIGSEGV, который убивает процесс, если альтернативный стек не был доступен через sigaltstack (2)). Поскольку значение длинное, на машинах с 32-битным длинным либо этот предел не превышает 2 GiB, либо этот ресурс неограничен.

Что подразумевается под «автоматическим расширением стека» здесь? Увеличивает ли стек в среде Linux / UNIX по мере необходимости? Если да, какой именно механизм?

Да стеки растут динамично. Стек находится в верхней части памяти, растущей вниз в сторону кучи.

-------------- | Stack | -------------- | Free memory| -------------- | Heap | -------------- . . 

Куча растет вверх (когда вы делаете malloc), и стек растет вниз, когда и когда вызывается новые функции. Куча присутствует непосредственно над секцией BSS программы. Это означает, что размер вашей программы и способ ее привязки к памяти в куче также влияют на максимальный размер стека для этого процесса. Обычно размер стека неограничен (до тех пор, пока области кучи и стека не встречаются и / или не перезаписываются, что приведет к переполнению стека и SIGSEGV 🙂

Это только для пользовательских процессов, стек ядра всегда исправлен (обычно 8 КБ)

Точный механизм приведен здесь, в Linux: при обработке ошибки страницы на анонимных сопоставлениях вы проверяете, является ли это «выращенным распределением», которое вы должны расширять, как стек. Если запись в области VM говорит, что вы должны, то вы настраиваете начальный адрес для расширения стека.

Когда возникает ошибка страницы, в зависимости от адреса, она может быть обслуживается (и сбой отказа) через расширение стека. Это поведение «для роста» по вине для виртуальной памяти может быть запрошено с помощью произвольных пользовательских программ, при этом флаг MAP_GROWSDOWN передается в системный вызов mmap .

Вы можете обходиться с этим механизмом и в пользовательской программе:

 #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> int main() { long page_size = sysconf(_SC_PAGE_SIZE); void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if (MAP_FAILED == mem) { perror("failed to create growsdown mapping"); return EXIT_FAILURE; } volatile char *tos = (char *) mem + page_size; int i; for (i = 1; i < 10 * page_size; ++i) tos[-i] = 42; fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem); (void) getchar(); if (munmap(mem, page_size)) perror("failed munmap"); return EXIT_SUCCESS; } 

Когда он подскажет вам найти pid программы (через ps ) и посмотрите на /proc/$THAT_PID/maps чтобы увидеть, как выросла исходная область.