Внутренние компоненты переключателя контекста 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, фатальные синхронные сигналы и фатальные асинхронные сигналы будут узнаваемо похожи.

Другие вопросы по тегам