netlib.narod.ru | < Назад | Оглавление | Далее > |
Подобно exec, wait представляет собой семейство функций, а не отдельную функцию. (Однако, в отличие от exec, семейство функций wait действительно содержит член с именем wait.) Все функции семейства wait обязательно реализуются в ядре посредством единственной системной функции sys_wait4 (строка 23327), имя которой отражает тот факт, что она реализует наиболее общую функцию в семействе wait, wait4. Реализации стандартной библиотеки С libc должны реорганизовывать аргументы вызовов других функций wait и вызывать функцию sys_wait4. (Что ж, это еще не все: по историческим причинам порт Alpha ядра также обеспечивает функцию sys_waitpid. Но даже sys_waitpid вызывает функцию sys_wait4.)
Кроме всего прочего, sys_wait4 — только она — наконец отсылает зомби в могилу. Однако, с точки зрения приложения wait и связанные с ней функции озабочены проверкой дочернего процесса: они смотрят, умер ли кто-нибудь, и если да, то кто и как.
23327: Как и надлежит очень общей функции, sys_wait4 имеет множество параметров, некоторые из которых необязательны, pid, как всегда, представляет PID целевого процесса; нулевое и отрицательные значения имеют специальный смысл, как будет вскоре показано. stat_addr если его значением является не NULL, представляет адрес, в который должно быть скопировано состояние выхода прерванного дочернего процесса, options — это набор флагов, которые могут изменить поведение функции sys_wait4. ru, если его значением является не NULL, — это адрес, в который должна быть скопирована информация об использовании ресурсов прерванного дочернего процесса.
23335: Если ей переданы любые недопустимые параметры, sys_wait4 возвращает код ошибки. Это решение кажется несколько грубым; вполне можно было бы просто игнорировать не имеющие важного значения флаги. Аргументы за выполнение этой задачи именно таким образом естественно заключаются в том, что если вызывающий процесс включает разряды, которые не собирался включать, он может столкнуться с неожиданным для себя поведением — в любом случае это означает, что вызывающий процесс ошибся и в этом случае лучше сообщить об ошибке, чем молча проигнорировать ошибку вызывающего процесса.
23342: Выполняет цикл по всем непосредственным дочерним процессам данного процесса (но не по внучатым процессам и т.д.). Как упоминалось ранее в этой главе, самый младший (наиболее недавно созданный) дочерний процесс доступен посредством члена p_cptr структуры struct task_struct, а список более старших братьев этого самого младшего дочернего процесса — посредством его члена p_osptr; таким образом, функция sys_wait4 проходит по всем дочерним процессам родительского процесса, начиная с самого младшего дочернего процесса, циклически переходя к все более старшим его братьям.
23343: Отфильтровывает неподходящие PID, исходя из значения аргумента pid. Обратите внимание, как будет обрабатываться аргумент pid равный –1 который позволяет выбрать любой процесс: значение этого pid не пройдет тесты в строках 23343, 23346 и 23349, поэтому он никогда не будет отклонен. Следовательно, это приводит к тому, что будет рассматриваться каждый дочерний процесс.
23376: Именно этот случай нас интересует в настоящее время — родительский процесс дожидается прерванного дочернего процесса. Именно здесь зомби наконец умирает окончательно. Все начинается с обновления представления родительского процесса о пользователе и системном времени, использованном его дочерними процессами (посредством системной функции sys_times, строка 29772), поскольку дочерний процесс больше не должен участвовать в вычислениях.
23382: Собирается информация об использовании других ресурсов (если она была запрошена), и состояние выхода дочернего процесса передается по указанному адресу (если оно было запрошено).
23387: Отправляет retval идентификатору PID завершившегося дочернего процесса, подлежащего удалению. Это конец; retval больше не будет меняться.
23388: Если текущий родительский процесс завершившегося процесса не является его исходным родительским процессом, процесс удаляет себя из текущей позиции в графе процессов (посредством REMOVE_LINKS, строка 16876), переустанавливает себя под исходным родительским процессом (посредством SET_LINKS, строка 16887), а затем отправляет своему родительскому процессу сигнал SIGCHLD, чтобы родительский процесс знал о выходе его дочернего процесса. Уведомление доставляется посредством notify_parent, описанной в главе 6 (строка 28548).
23396: Иначе — в обычном случае — может быть наконец вызвана функция release (строка 22951) для освобождения структуры struct task_struct завершившегося дочернего процесса. (Функция release будет рассмотрена через несколько минут, по завершении рассмотрения sys_wait4.)
23400: Теперь дочерний процесс успешно завершен, поэтому функции sys_wait4 остается только вернуть успех; она переходит к строке 23418, где возвращает значение retval (PID завершившегося дочернего процесса).
23401: Обратите внимание на необычное управление потоком; цикл for, который начался в строке 23342, продолжается в ветви default. Поскольку ветвь достигается только для процессов, которые не являются ни остановленными, ни зомби, это управление потоком является корректным, но его легко пропустить при первом чтении. Однако, в любом случае это излишне; и без этой ветви цикл вел бы себя так же.
23406: Если эта точка достигнута, цикл for пришел к завершению — вызывающий процесс просмотрел весь список дочерних процессов, так и не найдя процесс подлежащий, завершению — и вычисление находится в одном из трех состояний. Либо ни один дочерний процесс еще не завершился, либо ни один из дочерних процессов не совпал с переданным аргументом pid, либо (это особый случай предшествующей ситуации) задача вообще не имела дочерних процессов.
23408: Если значение flag ненулевое, во время цикла for была достигнута строка 23358, а значит по меньшей мере один дочерний процесс совпал с переданным аргументом pid — он просто не был зомби или остановленным, и поэтому не мог быть удален. В этом случае, если параметр WHOHANG был передан — т.е. вызывающий процесс не хочет ждать, раз ни один дочерний процесс не может быть удален — осуществляется переход к концу с возвратом нуля.
23411: Если был получен сигнал, выполняется выход с ошибкой. Этим сигналом не был SIGCHLD — если бы это было так, мертвый дочерний процесс не был бы найден, и, следовательно, эта точка не была бы достигнута.
23413: В противном случае все в порядке; вызывающему процессу нужно только дождаться выхода дочернего процесса. Таким образом, состояние процесса устанавливается равным TASK_INTERRUPTIBLE и для передачи процессора другому процессу вызывается функция schedule, которая не выполнит возврат до тех пор, пока ожидающий процесс не получит еще один доступ к процессору, во время которого он снова проверит наличие мертвого дочернего процесса (перейдя обратно к метке repeat в строке 23339). Вспомните, что в состоянии TASK_INTERRUPTIBLE процесс дожидается пробуждения сигналом — в данном случае он специально дожидается сигнала SIGCHLD, показывающего, что дочерний процесс выполнил выход, но ни один сигнал не смог прибыть.
23417: flag имел значение 0, поскольку либо процесс не имел дочерних процессов, либо переданный аргумент pid не совпал ни с одним из его дочерних процессов — в любом случае, функция sys_wait4 возвращает вызывающему процессу ошибку ECHILD.
22951: Единственным аргументом функции release является указатель на структуру struct task_struct, подлежащую освобождению.
22953: Убеждается, что задача не пытается освободить самое себя — невероятная ситуация, которая указывала бы на логическую ошибку в ядре.
22969: Код для однопроцессорной системы начинается вызовом функции free_uid (строка 23532), которая освобождает потенциально совместно используемую структуру struct user_struct, которая, кроме всего прочего, помогает процедуре fork убедиться, что единственный пользователь не владеет всеми процессами.
22970: Уменьшает значение системного счетчика общего количества выполняющихся задач и освобождает слот завершающегося процесса в массиве tarray_freelist.
22974: PID завершающегося процесса также освобожден и с помощью REMOVE_LINKS (строка 16876) он удален из графа процессов и из списка задач. Обратите внимание, что поскольку здесь структуры данных ядра должны быть исправлены, запись процесса в массиве task не нуждается в установке в NULL; достаточно добавления его слота в список свободных слотов.
22979: Значения счетчиков ошибок младшей страницы, ошибок старшей страницы и количества выполненных подкачек процесса добавлены к значениям соответствующих «дочерних счетчиков» текущего процесса — что совершено правильно; функция release вызывается только функцией sys_wait4, которая разрешает процессам освобождать только собственные дочерние процессы. Следовательно, текущий процесс должен быть родительским процессом завершающегося процесса.
22982: И наконец, пора освободить структуру struct task_struct завершившегося процесса, что выполняется посредством вызова функции free_task_struct (строка 2391). Эта функция просто освобождает страницы, на которых хранилась структура. И теперь, наконец, процесс полностью завершен.
netlib.narod.ru | < Назад | Оглавление | Далее > |