Разработка ядра Linux | страница 44



реализовано через единственный (и достаточно сложный) системный вызов >wait4(). Стандартное поведение этой функции — приостановить выполнение вызывающей задачи до тех пор, пока один из ее порожденных процессов не завершится. При этом возвращается идентификатор >PID завершенного порожденного процесса. В дополнение к этому, в данную функцию передается указатель на область памяти, которая после возврата из функции будет содержать код завершения завершившегося порожденного процесса.

Когда приходит время окончательно освободить дескриптор процесса, вызывается функция >release_task(), которая выполняет указанные ниже операции.

• Вызывается функция >free_uid() для декремента счетчика ссылок на информацию о пользователе процесса. В системе Linux поддерживается кэш с информацией о каждом пользователе, в частности сколько процессов и открытых файлов имеет пользователь. Если счетчик ссылок достигает значения нуль, то пользователь больше не имеет запущенных процессов и открытых файлов, в результате кэш уничтожается.

• Вызывается функция >unhash_process() для удаления процесса из хеш-таблицы идентификаторов процессов >pidhash и удаления задачи из списка задач.

• Если задача была в состоянии трассировки (ptrace), то родительским для нее снова назначается первоначальный родительский процесс и задача удаляется из списка задач, которые находятся в состоянии трассировки (ptrace) данным процессом.

• В конце концов вызывается функция >put_task_struct() для освобождения страниц памяти, содержащих стек ядра процесса и структуру >thread_info, a также освобождается слябовый кэш, содержащий структуру >task_struct.

На данном этапе дескриптор процесса, а также все ресурсы, которые принадлежали только этому процессу, освобождены.

Дилемма "беспризорного" процесса

Если родительский процесс завершается до того, как завершаются вес его потомки, то должен существовать какой-нибудь механизм назначения нового родительского процесса для порожденных, иначе процессы, у которых нет родительского, навсегда останутся в состоянии зомби, что будет зря расходовать системную память. Решение этой проблемы было указано выше: новым родительским процессом становится или какой-либо один поток из группы потоков завершившегося родительского процесса, или процесс >init. При выполнении функции >do_exit() вызывается функция >notify_parent(), которая в свою очередь вызывает >forget_original_parent() для осуществления переназначения родительского процесса (reparent), как показано ниже.