• Вызывается функция exit_notify()
init. Состояние завершающегося процесса устанавливается в значение TASK_ZOMBIE.• Вызывается функция schedule()
TASK_ZOMBIE никогда не планируется на выполнение, этот код является последним, который выполняется завершающимся процессом.Исходный код функции do_exit()
kernel/exit.c.К этому моменту освобождены все объекты, занятые задачей (если они используются только этой задачей). Задача больше не может выполняться (действительно, у нее больше нет адресного пространства, в котором она может выполняться), а кроме того, состояние задачи — TASK_ZOMBIE
thread_info и task_struct.Задание завершено настолько, насколько остается возможность передать необходимую информацию родительскому процессу.
Удаление дескриптора процесса
После возврата из функции do_exit()
TASK_ZOMBIE и не может выполняться. Как уже рассказывалось выше, это позволяет системе получить информацию о порожденном процессе после его завершения. Следовательно, завершение процесса и удаление его дескриптора происходят в разные моменты времени. После того как родительский процесс получил информацию о завершенном порожденном процессе, структура task_struct порожденного процесса освобождается.Семейство функций wait()
wait4(). Стандартное поведение этой функции — приостановить выполнение вызывающей задачи до тех пор, пока один из ее порожденных процессов не завершится. При этом возвращается идентификатор PID завершенного порожденного процесса. В дополнение к этому, в данную функцию передается указатель на область памяти, которая после возврата из функции будет содержать код завершения завершившегося порожденного процесса.Когда приходит время окончательно освободить дескриптор процесса, вызывается функция release_task()
• Вызывается функция free_uid()
• Вызывается функция unhash_process()
pidhash и удаления задачи из списка задач.• Если задача была в состоянии трассировки (
• В конце концов вызывается функция put_task_struct()
thread_info, a также освобождается слябовый кэш, содержащий структуру task_struct.На данном этапе дескриптор процесса, а также все ресурсы, которые принадлежали только этому процессу, освобождены.
Дилемма "беспризорного" процесса
Если родительский процесс завершается до того, как завершаются вес его потомки, то должен существовать какой-нибудь механизм назначения нового родительского процесса для порожденных, иначе процессы, у которых нет родительского, навсегда останутся в состоянии зомби, что будет зря расходовать системную память. Решение этой проблемы было указано выше: новым родительским процессом становится или какой-либо один поток из группы потоков завершившегося родительского процесса, или процесс init
do_exit() вызывается функция notify_parent(), которая в свою очередь вызывает forget_original_parent() для осуществления переназначения родительского процесса (reparent), как показано ниже.struct task_struct *p, *reaper = father;
struct list_head *list;
if (father->exit_signal != -1)
reaper = prev_thread(reaper);
else
reaper = child_reaper;
if (reaper == father)