Должен ли планировщик linux переключиться на контекст?
У меня есть общий вопрос о планировщике linux и некоторых других подобных системных вызовах системы.
Является ли планировщик linux "процессом", и каждый вызов планировщику требует контекстного переключателя, как его еще один процесс?
Скажем, у нас есть тактик, который прерывает текущий текущий режим пользовательского режима, и теперь нам нужно вызвать планировщик. Вызывает ли вызов самого планировщика контекстный переключатель? Имеет ли планировщик свой собственный набор регистров и U-области, а также то, что он должен восстановить при каждом вызове?
И этот вопрос относится ко многим другим системным вызовам. Процессы ядра ведут себя как обычные процессы в отношении переключения контекста, единственное различие заключается в том, что они имеют больше разрешений и доступ к процессору?
Я спрашиваю об этом, потому что накладные расходы на коммутатор являются дорогостоящими. И звучит странно, что вызов самого планировщика провоцирует коммутатор контекста для восстановления состояния планировщика, после чего планировщик вызывает другой процесс для запуска и снова другой переключатель контекста.
Ответы
Ответ 1
Это очень хороший вопрос, и ответ на него будет "да", за исключением того, что аппаратное обеспечение знает о концепции OS и планировщика задач.
В аппаратном обеспечении вы найдете регистры, которые ограничены режимом "супервизор". Не вдаваясь в подробности о внутренней архитектуре процессора, есть копия основных регистров выполнения программы для "пользовательского режима" и "режима супервизора", последним из которых может быть доступ только сама ОС (через флаг в управляющий регистр, который устанавливает набор ядер, указывающий, работает ли в настоящее время ядро или приложение режима пользователя).
Таким образом, "контекстный переключатель", о котором вы говорите, - это процесс обмена/сброса регистров пользовательского режима (регистр команд, регистр указателя стека и т.д.) и т.д., но системные регистры не нужно заменять, поскольку они сохраняются отдельно от пользовательских.
Например, стек пользовательского режима в x86 является USP-A7, тогда как стек режима супервизора - SSP-A7. Таким образом, само ядро (в котором содержится планировщик задач) будет использовать стеки режима супервизора и другие регистры режима супервизора, чтобы запустить сам себя, установив флаг режима супервизора в 1, когда он работает, затем выполните переключатель контекста на аппаратном уровне пользователя, чтобы обменять приложений и установки флага режима супервизора в 0.
Но до того, как идея OS и планирования задач, если вы хотите сделать многозадачную систему, вам пришлось бы использовать базовую концепцию, изложенную в вашем вопросе: использовать аппаратное прерывание для вызова планировщика задач каждый x, затем замените приложение для планировщика задач, а затем замените его в новом приложении. Но в большинстве случаев прерывание таймера будет вашим фактическим планировщиком задач, и оно было бы сильно оптимизировано, чтобы сделать его менее контекстным и более простой процедурой обработчика прерываний.
Ответ 2
На самом деле вы можете проверить код для функции schedule()
в kernel/sched.c
. Это прекрасно написано и должно ответить на большинство ваших вопросов.
Но нижняя строка заключается в том, что планировщик Linux вызывается вызовом schedule()
, который выполняет задание, используя контекст своего вызывающего. Таким образом, нет специализированного процесса "планировщик". Это сделало бы вещи более трудными на самом деле - если планировщик был процессом, он также должен был бы запланировать себя!
Когда schedule()
вызывается явно, он просто переключает контексты потока A вызывающего абонента с одним из выбранного исполняемого потока B, например, он возвращается в B (путем восстановления значений регистра и указателей стека, обратного адреса schedule()
станет одним из B вместо A).
Ответ 3
Вот попытка простого описания того, что происходит во время вызова диспетчера:
- Программа, которая в настоящее время имеет контекст, запущена на процессоре. Регистры, счетчик программ, флаги, база стека и т.д. Подходят для этой программы; за исключением, например, "зарезервированного регистра" операционной системы или какого-то такого, ничего о программе ничего не знает о диспетчере.
- Запускается прерывание по времени для функции диспетчера. Единственное, что происходит на данный момент (в случае с ванильной архитектурой) - это то, что счетчик программ сразу же переходит к тому, что указано в адресе ПК в прерывании BIOS. Это начинает выполнение подпрограммы диспетчера диспетчеризации; все остальное остается нетронутым, поэтому диспетчер видит регистры, стек и т.д. программы, которая ранее выполнялась.
- Диспетчер (как и все программы) имеет набор инструкций, которые работают с текущим набором регистров. Эти инструкции написаны таким образом, что они знают, что предыдущее исполняемое приложение оставило все свое состояние позади. Первые несколько инструкций в диспетчере будут хранить это состояние в памяти где-то.
- Диспетчер определяет, какой должна быть следующая программа для CPU, берет все ранее сохраненное состояние и заполняет регистры.
- Диспетчер переходит к соответствующему счетчику ПК, как указано в задаче, которая теперь имеет полный контекст, установленный на процессоре.
Сверху упростить; диспетчер не нуждается в регистрах, все, что он делает, записывает текущее состояние процессора в заданное место памяти, загружает состояние процессора другого процесса из предопределенной ячейки памяти и переходит к тому месту, где этот процесс прекратился.