Я хочу узнать и заполнить пробелы в моих знаниях с помощью этого вопроса
Итак, пользователь запускает поток (уровень ядра), и теперь он вызывает yield (системный вызов, который я предполагаю) Планировщик должен теперь сохранить контекст текущего потока в TCB (который где-то хранится в ядре) и выбрать другой поток для запуска и загрузить его контекст и перейти к его CS: EIP. Чтобы сузить дело, я работаю над Linux, работающим поверх архитектуры x86. Теперь я хочу вдаваться в подробности:
Итак, сначала у нас есть системный вызов:
1) Функция-обертка для yield приведет к вводу аргументов системного вызова в стек. Нажмите адрес возврата и поднимите прерывание с номером системного вызова, нажатым на некоторый регистр (например, EAX).
2) Прерывание изменяет режим CPU от пользователя к ядру и переходит к таблице векторов прерываний, а оттуда к фактическому системному вызову в ядре.
3) Я предполагаю, что планировщик теперь вызван, и теперь он должен сохранить текущее состояние в TCB. Вот моя дилемма. Поскольку планировщик будет использовать стек ядра, а не стек пользователя для выполнения его операции (что означает, что SS и SP должны быть изменены), как он сохраняет состояние пользователя без изменения какого-либо регистра в процессе. Я читал на форумах, что есть специальные аппаратные инструкции для сохранения состояния, но тогда как планировщик получает доступ к ним и кто выполняет эти инструкции и когда?
4) Планировщик теперь сохраняет состояние в TCB и загружает другой TCB
5) Когда планировщик запускает исходный поток, элемент управления возвращается к функции-обертки, которая очищает стек, а поток возобновляет
Боковые вопросы: работает ли планировщик как поток только для ядра (т.е. поток, который может запускать только код ядра)? Есть ли отдельный стек ядра для каждого потока ядра или каждого процесса?