netlib.narod.ru | < Назад | Оглавление | Далее > |
Процесс можно прервать против его желания, послав ему сигнал 9, как было описано в главе 6, но добровольное завершение является более распространенным. Процессы завершаются добровольно, вызывая системную функцию exit, которая реализована в ядре функцией sys_exit (строка 23322). (Кстати, когда программа на С осуществляет возврат из main, она косвенно вызывает exit.)
Когда процесс осуществляет выход, ядро должно освободить все ресурсы, выделенные процессу — память, файлы и т.п. — и, конечно, прекратить предоставлять в его распоряжение центральный процессор.
Однако, ядро не сразу освобождает структуру struct task_struct, представляющую процесс, поскольку родительский процесс данного процесса должен быть в состоянии запросить о состоянии выхода дочернего процесса с помощью системного вызова wait. Функция wait возвращает PID дочернего процесса, о завершении которого она докладывает, поскольку приложения могли бы быть введены в заблуждение, если бы PID более не существующего дочернего процесса был повторно выделен прежде, чем родительский процесс получил ответ wait. (Кроме всего прочего, один и тот же родительский процесс мог бы получить два дочерних процесса с одним и тем же PID — один действующий и один не существующий — не знал бы, какой из них завершил работу). Следовательно, ядро должно резервировать PID не существующего дочернего процесса до тех пор, пока не будет выполнена функция wait — это делается автоматически путем сохранения соответствующей структуры struct task_struct; код, выполняющий выделение PID просто не запрашивает, существуют ли найденные им в списке задач процессы.
Процессы в этом промежуточном состоянии — уже не существующие, но еще и не завершившиеся полностью — называются зомби. Таким образом, задача функции sys_exit заключается в превращении действующих процессов в зомби.
Сама по себе функция sys_exit тривиальна; она просто преобразует код выхода в формат, ожидаемый функцией do_exit, а затем вызывает функцию, которая выполняет реальную работу (do_exit вызывается также в качестве части отправки сигналов, как было описано в главе 6.)
23267: do_exit принимает код выхода в качестве аргумента и содержит необычный символ NORET_TYPE перед возвращаемым ею типом. Хотя в настоящий момент NORET_TYPE (строка 14955) определяется в качестве пустого комментария — т.е. он не оказывает никакого действия — обычно он определяется как __volatile__, что указывает gcc что функция не выполнила возврат. В свете этого gcc выполняет некоторую дополнительную оптимизацию и подавляет предупреждение о том, что функция не выполнила возврат. Получив новое определение, NORET_TYPE больше не оказывает никакого влияния на работу компилятора, но при том сохраняется, поскольку по прежнему доносит полезную информацию до читателя.
23285: Освобождает свои семафоры и другие структуры System V IPC, которые освещены в главе 9.
23286: Освобождает свою выделенную память, что освещается в главе 8.
23290: Освобождает свои выделенные файлы, как вскоре будет описано.
23291: Освобождает свои данные файловой системы, что выходит за рамки этой книги.
23292: Освобождает свою таблицу обработчика сигналов, что освещено в главе 6.
23294: Выполняющая выход задача теперь входит в состояние TASK_ZOMBIE и ее код выхода запоминается родительским процессом для использования в будущем.
23296: Вызывает функцию exit_notify (строка 23198), которая предупреждает родительский процесс завершающейся задачи и членов ее группы процессов о том, что этот процесс завершается.
23304: Вызывает функцию schedule (строка 26686) для освобождения процессора. Это обращение к schedule никогда не выполняет возврат, поскольку оно переключает контекст на другой процесс и никогда не выполняет обратного переключения; поэтому это последний раз, когда завершающийся процесс получает доступ к процессору.
Способ взаимодействия процессов с файлами не является главной темой этой книги. Но следует кратко рассмотреть функцию __exit_files (строка 23109), поскольку это поможет получить более полное представление о функции __clone. Ранее мы уже рассматривали функцию __clone, которая позволяет родительском и дочернему процессам совместно использовать определенную информацию. Один из компонентов, которые родительский и дочерний процессы могут использовать совместно — их список открытых файлов. Как уже упоминалось, Linux использует схему подсчета ссылок, чтобы процессы могли корректно выполнить после себя уборку. Что ж, вот удачный пример такой уборки.
23115: Предполагая, что процесс имел какие-либо открытые файлы (это справедливо почти всегда), __exit_files уменьшит значение счетчика ссылок, которое первоначально хранилось в tsk->files->count. Элементарные операции, такие как atomic_dec_and_test подробно освещены в главе 10; а пока достаточно сказать, что atomic_dec_and_test (строка 10249) уменьшает свой аргумент и возвращает значение true, когда новое значение аргумента равно 0. Следовательно, возвращаемое значение истинно, когда ссылка tsk на интересующую структуру struct files_struct была последней. (Если бы эта копия была закрытой, а не используемой совместно с другим процессом, первоначально значение счетчика ссылок было бы равно 1 и обязательно уменьшилось бы до 0.)
23116: Перед освобождением памяти, которая отслеживает открытые файлы процесса, все файлы должны быть закрыты, что делается посредством вызова функции close_files (строка 23081).
23118: Освобождает память, содержащую массив дескрипторов файлов процесса, fd, что является субполем поля files. Максимальное число открытых файлов (NR_OPEN, определенное значением 1024 в строке 15067) выбирается так, чтобы проверка if в этой строке дала результат true — массив fd должен полностью помещаться на одной странице памяти. В основном это связано с тем, что при этом выделение (и освобождение) его памяти будет выполняться быстрее; в противном случае функция __exit_files будет вынуждена прибегнуть к более общим, но и более медленным функциям памяти ядра. Подробнее это решение будет обосновано в следующей главе.
23122: В последнюю очередь функция __exit_files освобождает сами файлы.
В основе остальных функций __exit_xxx лежит аналогичная концепция: они уменьшают значение собственного счетчика ссылок на потенциально общую с другими процессами информацию задачи, и если это была последняя ссылка, делают все необходимое для полного ее освобождения.
netlib.narod.ru | < Назад | Оглавление | Далее > |