Ответ 1
- В чем разница между стеком ядра и стекю пользователя?
Короче говоря, ничего - кроме использования другого места в памяти (и, следовательно, другого значения для регистра стека), и обычно различных защит доступа к памяти. То есть при выполнении в пользовательском режиме память ядра (часть из которых представляет собой стек ядра) не будет доступна даже при сопоставлении. И наоборот, без явного запроса кода ядра (в Linux с помощью таких функций, как copy_from_user()
), пользовательская память (включая стек пользователя) обычно не доступна напрямую.
- Почему используется [отдельный] стек ядра?
Разделение привилегий и безопасности. Во-первых, программы пользовательского пространства могут сделать свой стек (указатель) тем, что они хотят, и, как правило, нет требований к архитектуре даже для допустимого. Поэтому ядро не может доверять стекному указателю пользовательского пространства, чтобы оно было действительным и непригодным для использования, и поэтому для его управления потребуется один набор. Различные архитектуры ЦП реализуют это по-разному; x86 CPU автоматически переключают стековые указатели при переключении режимов привилегий, а значения, которые будут использоваться для разных уровней привилегий, настраиваются - с помощью привилегированного кода (т.е. только ядра).
- Если локальная переменная объявлена в ISR, где она будет сохранена?
В стеке ядра. Ядро (ядро Linux, то есть) не перехватывает ISR напрямую в ворота прерывания архитектуры x86, а вместо этого делегирует отправку прерываний на механизм ввода/вывода прерывания ядра, который сохраняет состояние регистрации перед прерыванием перед вызовом зарегистрированного обработчика (ов), Сам процессор при отправке прерывания может выполнять переключатель привилегий и/или стека, и это используется/настраивается ядром, так что общий код ввода прерывания может уже полагаться на стек ядра. Тем не менее, прерывания, возникающие во время выполнения кода ядра, будут просто (продолжать) использовать стек ядра на месте в этой точке. Это может быть, если обработчики прерываний имеют глубоко вложенные пути вызова, приводят к переполнениям стека (если прерывается глубокий путь вызова ядра, а обработчик вызывает еще один глубокий путь; в Linux код RAID/файловой системы/программного RAID, прерываемый сетевым кодом с активным iptables, является как известно, запускать такие в неработающих старых ядрах... решение заключается в увеличении размеров стека ядра для таких рабочих нагрузок).
- У каждого процесса есть свой собственный стек ядра?
Не только каждый процесс - каждый поток имеет свой собственный стек ядра (и, фактически, собственный стек пользователя). Помните, что единственная разница между процессами и потоками (для Linux) заключается в том, что несколько потоков могут совместно использовать адресное пространство (формирование процесса).
- Как процесс координирует между этими стеками?
Совсем нет - это не нужно. Планирование (как/когда выполняются разные потоки, как их состояние сохраняется и восстанавливается) является задачей операционной системы, и процессам не нужно беспокоиться об этом. Поскольку потоки создаются (и каждый процесс должен иметь хотя бы один поток), ядро создает для них стеки ядра, в то время как стеки пользовательского пространства либо явно создаются/предоставляются каким-либо механизмом, используемым для создания потока (такие функции, как makecontext()
или pthread_create()
позволяет вызывающему абоненту указать область памяти, которая будет использоваться для "дочернего" потока потоков) или наследуется (при клонировании памяти при доступе, обычно называемом "копирование при записи" /COW, при создании нового процесса). < ш > Тем не менее, этот процесс может влиять на планирование его потоков и/или влиять на контекст (состояние, среди которого есть указатель потока потока). Для этого существует несколько способов: сигналы UNIX, setcontext()
, pthread_yield()
/pthread_cancel()
,... - но это немного нарушает исходный вопрос.