Главная » Ядро Linux » Вытеснение и переключение контекста

0

Переключение контекста — это  переключение от одной, готовой  к выполнению задачи  к другой.  Это  переключение производится с помощью функции context_switch() , определенной в файле  kernel/sched.с . Данная функция вызывается функцией schedule  (), когда новый  процесс выбирается для выполнения. При  этом  выполняются следующие шаги.

• Вызывается функция switch_mm (), которая определена в файле include/asm/ mmu_context.h и предназначена для переключения от виртуальной памяти старого  процесса к виртуальной памяти нового  процесса.

• Вызывается функция switch_t o () , определенная в файле  includ e /asm/ system.h, для переключения от состояния процессора предыдущего процесса к состоянию процессора нового  процесса. Эта процедура включает восстановление  информации стека ядра и регистров процессора.

Ядро  должно  иметь  информацию о том, когда  вызывать функцию schedul e () . Если  эта функция будет вызываться только  тогда, когда программный код вызывает ее явно, то пользовательские программы могут выполняться неопределенное время. Поэтому ядро  поддерживает флаг  need_resched для  того, чтобы  сигнализировать, необходимо ли  вызывать функцию schedul e ()   (табл.  4.2).  Этот  флаг устанавливается  функцией schediiler_tic k (), когда  процесс истрачивает свой  квант  времени, и функцией try_to_wake_up () , когда  процесс с приоритетом более  высоким, чем у текущего  процесса, возвращается к выполнению. Ядро проверяет значение этого флага, и если он установлен, то вызывается функция schedule  ()  для переключения на новый  процесс. Этот флаг является сообщением ядру о том, что планировщик должен  быть  активизирован по  возможности раньше, потому  что другой  процесс должен  начать  выполнение.

Таблица 4.2 . Функции для управления флагом need_resche d

Функция                                              Назначение

set_tsk_need_resched (task)   Установить флаг need_resched для данного процесса

clear_tsk_need_resched (task) Очистить флаг need_resched для данного процесса

need_resched()             Проверить значение флага need_resched для данного процесса. Возвращается значение true , если этот флаг установлен, и false , если не установлен

Во время  переключения в пространство пользователи или  при  возврате  из прерывания, значение флага  need_resched проверяется. Если  он установлен, то ядро активизирует планировщик перед  тем, как  продолжить работу.

Этот флаг не является глобальной переменной, так как  обращение к дескриптору процесса получается более  быстрым, чем обращение к глобальным данным (из-за скорости обращения к переменной curren t и потому, что соответствующие данные могут находиться в кэше). Исторически, этот флаг был глобальным в ядрах до серии

2.2.  В ядрах  серий  2.2 и 2.4 этот  флаг  принадлежал структуре  task_struc t  и имел тип int . В серии  ядер 2.6 этот флаг перемещен в один  определенный бит специальной  переменной флагов  структуры  threa d   info . Легко  видеть, что  разработчики ядра никогда не могут быть всем  довольны.

Вытеснение пространства пользователя

Вытеснение пространства пользователя (user  preemption) происходит в тот момент, когда ядро  собирается возвратить управление режиму  пользователя, при  этом устанавливается флаг  need_resched и, соответственно, активизируется планировщик.  Когда ядро  возвращает управление в пространство пользователя, то оно находится  в безопасном и "спокойном" состоянии. Другими  словами, если продолжение выполнения текущего  задания является безопасным, то безопасным будет также  и выбор  нового  задания для  выполнения. Поэтому когда  ядро  готовится возвратить управление в режим  пользователя или  при  возврате  из прерывания или  после  системного вызова, происходит проверка флага  need_resched. Если  этот  флаг установлен, то активизируется планировщик И выбирает новый, более  подходящий процесс для  исполнения. Как  процедура возврата  из прерывания, так и процедура возврата  из системного вызова  являются зависимыми от аппаратной платформы и обычно  реализуются на языке  ассемблера в файле  entry. S  (этот  файл, кроме  кода входа в режим  ядра, также  содержит и код  выхода  из режима  ядра).  Если  коротко, то вытеснение пространства пользователя может  произойти в следующих  случаях.

• При  возврате  в пространство пользователя из системного вызова.

• При  возврате  в пространство пользователя из обработчика прерывания.

Вытеснение пространства ядра

Ядро  операционной системы Linux, в отличие  от ядер  большинства вариантов ОС Unix, является полностью преемптивным (вытесняемым, preemptible). В непреемптивных ядрах код ядра выполняется до завершения. Иными словами, планировщик  не может  осуществить планирование для  выполнения другого  задания, пока какое-либо задание  выполняется в пространстве ядра — код ядра планируется на выполнение кооперативно, а не посредством вытеснения. Код ядра выполняется до тех пор, пока  он  не завершится (возвратит управление в пространство пользователя) или пока явно  не заблокируется. С появлением серии ядер 2.6, ядро Linux стало преемптивным: теперь  есть возможность вытеснить задание  в любой  момент, конечно, пока  ядро  находится в состоянии, когда безопасно производить перепланирование выполнения.

В таком  случае когда же безопасно производить перепланирование? Ядро способно вытеснить задание, работающее в пространстве ядра, когда  это задание  не удер-

живает  блокировку. Иными словами, блокировки используются в качестве маркеров тех областей, в которые задание не  может  быть  вытеснено. Ядро  рассчитано на многопроцессорность (SMP-safe),  поэтому если  блокировка не удерживается, то код  ядра является реентерабельным и его  вытеснять безопасно.

Первое изменение, внесенное для  поддержки вытеснения пространства ядра, — это  введение  счетчика преемптивности  preempt_coun t  в  структуру   thread_inf о каждого процесса. Значение этого  счетчика вначале равно  нулю  и увеличивается на единицу при  каждом захвате  блокировки, а также  уменьшается на единицу при  каждом  освобождении блокировки.  Когда  значение  счетчика равно  нулю—  ядро  является  вытесняемым. При  возврате из  обработчика прерывания,  если  возврат  выполняется в пространство ядра, ядро  проверяет значения переменных need_resche d и  preempt_count .  Если   флаг   need_resche d  установлен  и  значение счетчика preempt__coun t  равно  нулю, значит,  более  важное   задание готово  к  выполнению и  выполнять вытеснение безопасно. Далее  активизируется планировщик.  Если  значение  счетчика preempt_coun t  не  равно  нулю,  значит,  удерживается захваченная блокировка и  выполнять вытеснение не  безопасно. В таком  случае  возврат   из  обработчика прерывания  происходит в текущее   выполняющееся  задание.  Когда  освобождаются  все  блокировки,  удерживаемые текущим заданием,  значение  счетчика preempt_coun t  становится равным нулю.  При  этом  код, осуществляющий освобождение  блокировки,  проверяет, не  установлен ли  флаг  need_resched .  Если  установлен, то  активизируется планировщик.  Иногда коду  ядра  необходимо иметь  возможность  запрещать или  разрешать вытеснение в режиме ядра, что  будет  рассмотрено в  главе 9.

Вытеснение пространства ядра  также  может  произойти явно,  когда  задача  блокируется в  режиме ядра  или  явно  вызывается функция  schedul e () .  Такая   форма преемптивности ядра  всегда  поддерживалась, так  как  в этом  случае  нет  необходимости  в дополнительной логике, которая бы давала  возможность убедиться, что  вытеснение проводить безопасно. Предполагается, что если  код явно  вызывает функцию schedul e () , то точно  известно, что перепланирование  производить безопасно.

Вытеснение пространства ядра  может  произойти в  следующих случаях.

• При  возврате из обработчика прерывания в пространство ядра.

• Когда  код  ядра  снова  становится преемптивным.

• Если  задача, работающая в режиме ядра, явно  вызывает функцию schedul e () .

• Если  задача, работающая в режиме ядра, переходит в приостановленное  состояние, т.е.  блокируется (что  приводит к вызову  функции schedul e ()) .

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии