Открыть сервис

Стек ядра

Стек ядра — это структура данных, организованная по принципу LIFO (Last In, First Out — «последним пришёл — первым вышел»), которая используется операционной системой для хранения контекста выполнения потоков (нитей) в пространстве ядра. В отличие от пользовательского стека, предназначенного для работы прикладных программ, стек ядра служит для обработки системных вызовов, обработки прерываний и выполнения внутренних функций операционной системы. Каждый поток (или процесс) в системе имеет собственный стек ядра, который выделяется при его создании и используется только при переходе в привилегированный режим.

Назначение и роль

Стек ядра выполняет несколько критически важных функций в работе операционной системы:

Размер и ограничения

Размер стека ядра обычно фиксирован и значительно меньше, чем пользовательский стек. В большинстве современных операционных систем (Linux, Windows, FreeBSD) размер стека ядра составляет от 4 до 16 килобайт. Это ограничение связано с необходимостью экономии оперативной памяти: в системе может одновременно существовать тысячи потоков, и каждый из них требует собственного стека ядра.

Ограниченный размер накладывает строгие требования на код ядра:

В Linux размер стека ядра для каждого потока можно настроить при компиляции ядра (параметр THREAD_SIZE). На архитектуре x86-64 по умолчанию используется 16 килобайт, на ARM — 8 килобайт. В Windows размер стека ядра для потоков режима ядра (Kernel Mode Stack) составляет 12 килобайт на x86 и 24 килобайта на x64.

Структура и организация

Стек ядра организован как непрерывная область памяти, растущая в сторону уменьшения адресов (от старших к младшим). Каждый вызов функции создаёт в стеке фрейм (stack frame), который содержит:

Начало стека ядра обычно выровнено по границе страницы (4 килобайта). В нижней части стека (по старшим адресам) может располагаться служебная структура — например, thread_info в Linux или KTHREAD в Windows. Эта структура содержит информацию о потоке (идентификатор, флаги, указатели на другие структуры) и доступна через регистр процессора или вычисление адреса по маске.

В некоторых архитектурах (например, x86) стек ядра и пользовательский стек используют разные сегменты памяти и переключаются аппаратно при изменении уровня привилегий. В других (например, ARM) переключение стека выполняется программно в обработчике исключений.

Управление стеком ядра в операционных системах

Linux

В ядре Linux стек ядра выделяется для каждого потока (task) при его создании. Функция alloc_thread_stack_node() резервирует одну или две физические страницы (в зависимости от конфигурации THREAD_SIZE). В начале стека (по самому старшему адресу) располагается структура thread_info, которая содержит флаги состояния, идентификатор процесса (PID) и указатель на дескриптор задачи (task_struct). Доступ к thread_info осуществляется через макрос current_thread_info(), который вычисляет адрес, обнуляя младшие биты указателя стека.

При переполнении стека ядра в Linux активируется механизм защиты — STACKPROTECTOR (GCC-расширение). Компилятор вставляет в начало каждой функции ядра «канарейку» (случайное значение), которая проверяется перед возвратом. Если канарейка изменена, ядро вызывает панику (kernel panic). Дополнительно используется страница-страж (guard page) — виртуальная страница, расположенная непосредственно перед стеком, доступ к которой вызывает ошибку страницы.

Windows

В Windows стек ядра называется Kernel Mode Stack (KMS). Он выделяется для каждого потока при инициализации объекта потока (KTHREAD). Размер KMS фиксирован и зависит от архитектуры: 12 КБ для x86, 24 КБ для x64. В нижней части стека (по старшим адресам) располагается структура KTRAP_FRAME, которая сохраняет контекст процессора при входе в ядро.

Windows использует механизм защиты от переполнения стека ядра на основе страниц-стражей. Если поток в режиме ядра пытается выйти за пределы своего стека, возникает исключение KMODE_EXCEPTION_NOT_HANDLED, которое приводит к синему экрану смерти (BSOD). Для отладки таких ситуаций в Windows предусмотрен тег памяти 'TkSt' (Thread Kernel Stack), позволяющий отслеживать выделение и освобождение стеков.

FreeBSD

В FreeBSD стек ядра для каждого потока (thread) выделяется в контексте структуры thread. Размер стека по умолчанию составляет 8 килобайт (настраивается через KSTACK_PAGES). В отличие от Linux, FreeBSD не использует thread_info в самом стеке — вся информация о потоке хранится в отдельной структуре pcb (Process Control Block). Защита от переполнения реализована через страницы-стражи и проверки границ в диспетчере памяти.

Проблемы и уязвимости

Стек ядра является критическим ресурсом, и его неправильное использование может привести к серьёзным последствиям:

Интересные факты

Источники

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →