Що таке "автоматичне розширення стека"?


13

getrlimit (2) має таке визначення на сторінках man:

RLIMIT_AS Максимальний розмір віртуальної пам'яті процесу (адресний простір) у байтах. Цей ліміт впливає на виклики до brk (2), mmap (2) та mremap (2), які не вдається з помилкою ENOMEM при перевищенні цього ліміту. Також не вдасться автоматичного розширення стека (і генерує SIGSEGV, який вбиває процес, якщо альтернативний стек не був доступний через sigaltstack (2)). Оскільки це значення довге, для машин із 32-бітовою довжиною або ця межа становить не більше 2 Гб, або цей ресурс необмежений.

Що тут розуміється під "автоматичним розширенням стека"? Чи зростає стек у середовищі Linux / UNIX в міру необхідності? Якщо так, то який саме механізм?

Відповіді:


1

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

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

Купа зростає вгору (коли ви робите малок), а стек росте вниз, як і коли викликаються нові функції. Купа присутня трохи вище розділу BSS програми. Що означає розмір вашої програми і те, як вона розширює пам'ять в купі, також впливає на максимальний розмір стека для цього процесу. Зазвичай розмір стека необмежений (до тих пір, поки ділянки купи та стеку не зустрінуться та / або перезапишуються, що дасть переповнення стека та SIGSEGV :-)

Це лише для користувацьких процесів, стек ядра виправляється завжди (зазвичай 8 КБ)


"Зазвичай розмір стека необмежений", ні, зазвичай він обмежений 8Mb ( ulimit -s).
Eddy_Em

так, ви праві в більшості систем. Ви перевіряєте команду ulimit оболонки, якщо так, існує жорсткий ліміт розміру стека, який є необмеженим (ulimit -Hs). У будь-якому разі, це було наголосити на тому, що стек і купа ростуть у протилежних напрямках.
Сантош

Тоді чим "автоматичне розширення" відрізняється від "натискання елементів на стек"? З вашого пояснення я отримую відчуття, що вони однакові. Крім того, я відчував, що початкові точки стеку і купи набагато більше 8 МБ, тому стек може зростати стільки, скільки потрібно (або він потрапляє в купу). Це правда? Якщо так, то як операційна система вирішила, де розмістити купу та стек?
loudandclear

Вони однакові, але стеки не мають фіксованого розміру, якщо ви жорстко обмежуєте розмір, використовуючи rlimit. Стек розміщується в кінці області віртуальної пам'яті, і купа негайно знаходиться в сегменті даних, що виконується.
Сантош

Я розумію, дякую. Однак я не думаю, що я отримую частину "стеки не мають фіксованого розміру". Якщо це так, для чого 8Mb м'який межа?
loudandclear

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;
}

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


Чи добре зателефонувати в munmap для оригінальної пам’яті та розміру сторінки, навіть якщо область пам’яті зросла через MAP_GROWSDOWN? Я припускаю, що так, бо в іншому випадку це було б дуже складний API, але в документації нічого чітко не сказано з цього приводу
i.petruk

2
MAP_GROWSDOWN не слід використовувати, а його було видалено з glibc (для цього див. Lwn.net/Articles/294001 ).
Колін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.