Внутренние компоненты переключателя контекста Linux: Что происходит, когда процесс завершается до прерывания таймера?
Как происходит переключение контекста в ядре Linux, когда процесс завершается до прерывания по таймеру?
Я знаю, что если процесс работает и происходит прерывание по таймеру, то schedule
Функция вызывается автоматически, если флаг установлен, функция расписания затем выбирает следующий процесс для запуска. В основном, в этом случае функция расписания выполняется в контексте текущего процесса, но что происходит, когда процесс завершается еще до прерывания таймера? кто звонит schedule
функция в этом случае? И в каком контексте это работает?
1 ответ
Важно понимать, что прерывание по таймеру является лишь одной из нескольких сотен разных причин, почему schedule
может быть вызван. Только программы, во время выполнения которых преобладают вычисления, что является более редким, чем вы думаете, когда-либо исчерпывают свой временной интервал. Гораздо чаще программы запускаются всего за несколько микросекунд - да, микросекунд - одновременно, между "блокировками" системных вызовов, ожиданием ввода данных пользователем или чем-то еще.
Когда процесс завершается каким-либо образом, в конечном итоге вызов do_exit
всегда происходит в (ядро) контексте этого процесса. do_exit
звонки schedule
как его последнее действие, и schedule
никогда не возвращается в этот контекст. Обратите внимание, как в самом конце do_exit
есть звонок в schedule
с последующим немедленным BUG();
и бесконечный цикл.
Как раз перед этим, do_exit
звонки exit_notify
, который отвечает за отправку SIGCHLD
родительскому процессу и / или освобождая его от вызова wait
, Итак, большую часть времени родительский процесс будет готов к запуску, когда schedule
будет вызван и будет выбран.
do_exit
также освобождает все состояние пользовательского пространства и большую часть состояния ядра, связанного с процессом, освобождает память, закрывает файловые дескрипторы и т. д. task_struct
сам должен выжить, пока кто-то не позвонит wait
и я не могу понять, как именно ядро решает, что теперь оно может быть освобождено; этот код слишком запутанный.
Если процесс называется _exit
цепочка вызовов ядра просто sys_exit_group
в do_group_exit
в do_exit
, Если он принял фатальный синхронный сигнал (например, SIGSEGV
), цепочка вызовов намного длиннее и имеет хитрое отклонение. Аппаратная ловушка направляется специфичным для архитектуры кодом (например, x86). do_trap
) через force_sig_info
а также send_signal
в complete_signal
, который корректирует состояние задачи, а затем указывает планировщику разбудить нарушающий процесс. Нарушающий процесс просыпается, и лабиринт специфичной для архитектуры логики обработки сигналов в конечном итоге доставляет его get_signal
, который вызывает do_group_exit
, который вызывает do_exit
, Фатальные асинхронные сигналы (например, от ввода kill 12345
по приглашению оболочки) начать с sys_kill
и пройти kill_something_info
, group_send_sig_info
, do_send_sig_info
в send_signal
после чего все происходит как указано выше. В обоих случаях все шаги до complete_signal
может произойти в любом контексте процесса, но все, что происходит после "нарушающего процесса", происходит в контексте этого процесса.
Единственными частями этого описания, которые специфичны для Linux, являются имена функций в коде ядра. Любая реализация Unix будет иметь функции ядра, которые делают более или менее то же, что и Linux do_exit
а также schedule
делать, и последовательность операций, связанных с полем _exit
, фатальные синхронные сигналы и фатальные асинхронные сигналы будут узнаваемо похожи.