Программирование >>  Структура ядра и системные вызовы 

1 ... 32 33 34 [ 35 ] 36 37 38 ... 98


Альтернативой fork является API vfork, который имеет такую же сигнатуру, как и fort.

pid t vfork (void);

Системный вызов vfork выполняет ту же функцию, что и fork, и возврашает те же возможные значения, что и fork. Он применяется и в BSD UNIX, и в UNIX System V.4, но не является стандартным для POSIX. 1. Создание vfork связано с тем, что многие программы вызывают exec (в порожденном процессе) сразу после fork. В случае, если ядро не создаст отдельного виртуального адресного пространства для порожденного процесса до выполнения exec, системный вызов vfork повысит производительность системы. В API vfork происходит следующее: после того как функция вызвана, ядро приостанавливает выполнение родительского процесса на то время, пока в виртуальном адресном пространстве этого процесса выполняется порожденный процесс. После того как порожденный процесс вызовет exec или exit, выполнение родительского процесса возобновится, а порожденный процесс либо получит свое собственное виртуальное адресное пространство после вызова exec, либо завершится через вызов exit.

Использование vfork небезопасно, так как если порожденный процесс изменяет данные родительского (например, закрывает файлы или модифицирует переменные) до вызова exec или exit, названные изменения сохранятся при возобновлении исполнения родительского процесса. Это может вызвать нарушение нормального функционирования родительского процесса. Кроме того, порожденный процесс не должен вызывать exit или возвращаться в любую вызывающую функцию, так как это повлечет за собой соответственно закрытие потоковых файлов родительского процесса или изменение его динамического стека.

В последних системах UNIX (например, System V.4) эффективность использования fork повышена. В них порожденный и родительский процессы могут совместно использовать общее виртуальное адресное пространство, пока порожденный процесс не вызовет функцию exec или exit. Если родительский или порожденный процесс изменит какие-либо данные в совместно используемом виртуальном адресном пространстве, ядро создаст новые страницы памяти для измененного виртуального адресного пространства. Таким образом, процесс, внесший изменения, будет обращаться к новым страницам памяти с измененными данными, тогда как параллельный процесс будет продолжать обращаться к старым страницам. Такая процедура называется копированием при записи (copy-on-wiite) и обеспечивает выполнение fork, по эффективности сравнимое с vfork. Поэтому vfork следует использовать только для переноса старых приложений в новые UNIX-системы.

8.2.2. Функция exit

Системный вызов exit завершает процесс. В частности, этот API вызывает освобождение сегмента данных вызывающего процесса, сегментов стека и U-области и закрытие всех открытых дескрипторов файлов. Однако запись в таблице процессов для этого процесса остается нетронутой, с тем чтобы там регистрировался статус заверщения процесса, а также отображалась статистика его выполнения (например, информация об обшем времени выполнения, количестве пересланных блоков ввода-вывода данных и т.д.). Теперь процесс называется зомби-процессом, так как его выполнение уже не может быть запланировано. Родительский процесс может выбрать хранящиеся в записи таблицы процессов данные посредством системного вызова wait или waitpid. Эти API освобождают также запись в таблице процессов, относящуюся к порожденному процессу.

Если процесс создает порожденный процесс и заканчивается до завершения последнего, то ядро назначит процесс init в качестве управляющего для порожденного процесса (это второй процесс, создаваемый после начальной загрузки ОС UNIX. Его идентификатор всегда равен 1). После заверщения порожденного процесса соответствующая запись в таблице процессов будет уничтожена процессом init.

Прототип функции exit имеет следующий вид:

#include <unistd.h> void exit (int exit code);

Целочисленный аргумент функции jexit - это код заверщения процесса. Родительскому процессу передаются только младшие 8 битов этого кода. Код завершения О означает успешное выполнение процесса, а ненулевой код - неудачное выполнение. В некоторых UNIX-системах в заголовке <stdio.h> определяются символические константы EXIT SUCCESS и EXIT FAILU-RE, которые могут быть использованы как аргументы функций exit, соответствующие успешному и неудачному завершению.

Функция ех/7 никогда не выполняется неудачно, поэтому возвращаемого значения для нее не предусмотрено.

Библиотечная С-функция exit является оболочкой для exit. В частности, exit сначала очищает буферы и закрывает все открытые потоки вызывающего процесса. Затем она вызывает все функции, которые были зарегистрированы посредством функции atexit (в порядке, обратном порядку их регистрации), и, наконец, вызывает exit для завершения процесса.

В приведенной ниже программе test exit. С демонстрируется использование функции exit. Когда программа запускается, она объявляет о своем существовании и затем завершается посредством вызова exit. Для обозначения успешного выполнения программы она передает нулевое значение кода завершения.



finclude <iostream.h> finclude < unistd.h> int main() {

cout Test program for exit endl;

exit(0);

return 0;

После выполнения этой программы пользователи могут проверить статус ее завершения с помошью переменных status (в C-shell) или $? (в Bourne-shell). Программа может дать следующие результаты:

% СС -о test exit test exit.C; test exit Test program for exit % echo $status 0

8.2.3. Функции wait, waitpid

Родительский процесс использует системные вызовы wait и waitpid для перехода в режим ожидания завершения порожденного процесса и для выборки его статуса завершения (присвоенного порожденным процессом с помощью функции exit). Кроме того, эти вызовы освобождают ячейку таблицы процессов порожденного процесса с тем, чтобы она могла использоваться новым процессом. Прототипы этих функций выглядят так:

#include <sys/wait.h> pid t wait ( int* status p );

pid t waitpid ( pid t child pid, int* status p, int options )

Функция wait приостанавливает выполнение родительского процесса до тех пор, пока ему не будет послан сигнал, либо пока один из его порожденных процессов не завершится или не будет остановлен (а его статус еще не будет сообщен). Если порожденный процесс уже завершился или был остановлен до вызова wait, функция wa/f немедленно возвратится со статусом завершения порожденного процесса (его значение содержится в аргументе statusjp), а возвращаемым значением функции будет PID порожденного процесса. Если, однако, родительский процесс не имеет порожденных процессов, завершения которых он ожидает, или был прерван сигналом при выполнении wait, функция возвращает значение -1, а переменная еггпо будет содержать код ошибки. Следует отметить, что если родительский процесс создал более одного порожденного, функция wait будет ожидать завершения любого из них.

Функция waitpid является более универсальной по сравнению с wait. Как и wait, функция waitpid сообщает код завершения и идентификатор порожденного процесса по его завершении. Однако в случае с waitpid в вызывающем

Процессе можно указать, завершения какого из порожденных процессов следует ожидать. Для этого необходимо аргументу child pid присвоить одно из следующих значений:

Фактическое значение Завершения чего ожидает функция

Порожденного процесса с данным идентификатором

Любого порожаенного процесса

Любого порожденного процесса, принадлежащего к той же фуппе процессов, что и родительский

Любого порожденного процесса, идентификатор группы (GID) которого является абсолютным значением child pid

Равное ID порожденного процесса

Отрицательное значение, но не -1

Кроме того, вызывающий процесс может дать функции waitpid указание быть блокирующей или неблокирующей, а также ожидать завершения порожденного процесса, приостановленного или не приостановленного механизмом управления заданиями. Эти опции указываются в аргументе optins. В частности, если аргумент options установлен в значение WNOHANG (этот флаг определен в <sys/wait.h>), вызов будет неблокирующим (то есть функция немедленно возвратит управление, если нет порожденного процесса, отвечающего критериям ожидания). В противном случае вызов будет блокирующим, а родительский процесс будет остановлен, как при вызове wait. Далее, если аргумент options установлен в значение WUNTRACED, функция также будет ожидать завершения порожденного процесса, остановленного в результате действия механизма управления заданиями (но о статусе которого не было сообщено ранее).

Если фактическое значение аргумента status р вызова wait или waitpid равно NULL, нет необходимости запрашивать статус завершения порожденного процесса. Однако если его фактическое значение является адресом целочисленной переменной, функция присвоит этой переменной код завершения (указанный в API exit). После этого родительский процесс может проверить этот код завершения с помощью следующих макрокоманд, определенных в заголовке <sys/wait.h>:

Макрокоманда

Возвращаемое значение

WlFEXITED(*status p) WEXITSTATUS(*status p)

WIFSIGNALED(*status p)

Ненулевое, если порожденный процесс был завершен посредством вызова exit; в противном случае возвращается нулевое значение

Код завершения порожденного процесса, присвоенного вызовом exit. Эту макрокоманду следует вызывать лишь в том случае, если W1FEXITED возвращает ненулевое значение

Ненулевое, если порожденный процесс бьш завершен по причине прерывания сигналом



WTERMSIG(*status p)

WIFST0PPED(*statu8 p) WST0PSIG(*5tatus p)

Номер сигнала, завершившего порожденный процесс. Эту макрокоманду следует вызывать только в том случае, если WIFSIGNALED возвращает ненулевое значение

Ненулевое, если порожденный процесс был остановлен механизмом управления заданиями

Номер сигнала, завершившего порожденный процесс. Эту макрокоманду следует вызывать лишь тогда, когда WIFSTOPPED возвращает ненулевое значение

В некоторых версиях UNIX, где данные макросы не определены, приведенную выше информацию можно получить непосредственно из *s1a1usjp. В частности, семь младших битов (биты с 0-го по 6-й включительно) *status р содержат нуль, если порожденный процесс был завершен с помощью функции exit, или номер сигнала, завершившего процесс. Восьмой бит *status р равен 1, если из-за прерывания порожденного процесса сигналом был создан дамп образа процесса (файл core). В противном случае он равен нулю. Далее, если порожденный процесс был завершен с помощью API exii, то биты с 8-го по 15-й *status р являются кодом завершения порожденного процесса, переданным посредством exit. Следующая схема демонстрирует применение битов данных *status р:

8 7 6

*status р:

Статус завершения порожденного процесса

Номер сигнала

Флаг создания файла core

В BSD UNIX аргумент statusjp имеет тип union wait* (union wait определяется в <sys/wait.h>). Это объединение целочисленной переменной и набора битовых полей. Битовые поля используются для извлечения такой же информации о статусе, какую получают приведенные выше макросы.

Если возвращаемое значение wait или waitpid является положительным целочисленным значением, то это - идентификатор порожденного процесса, в противном случае - это (pidj)-l. Последнее означает, что либо ни один из порожденных процессов не отвечает критериям ожидания, либо функция была прервана перехваченным сигналом. Здесь егто может быть присвоено одно из следующих значений:

E1NTR ECHILD

EFAULT EINVAL

wa/r или wa /)/ возвращает управление вызывающему процессу, так как системный вызов был прерван сигналом

Для wait это означает, что вызывающий процесс не имеет порожденного процесса, завершения которого он ожидает. В случае waitpid это означает, что либо значение ciiJdjid недопустимо, либо процесс не может находиться в состоянии, определенном значением options

Аргумент status р указывает на недопустимый адрес Значение options недопустимо

Как wait, так и waitpid являются стандартными для POSIX.l. Системный вызов waitpid отсутствует в BSD UNIX 4.3, System V.3 и их более старых версиях. ,

На примере приве>П[енной ниже программы tesi waitpid. Сдемонстрируется использование API waitpid.

tinclude <iostream.h> ♦include <stdio.h> ♦include <sys/types.h> ♦ include <sys/walt.h> ♦include <unistd.h> int mainO { .

child pid, pid; status;

(child pid=fork() )

/* fork fails */

pid t int switch (

.case {pid t)-l: perrof( fork ); break;

--fcase (pid t)0: cout Child process created\n ; *> exit(15); /* terminate child */

.i**tdefault: cout Parent process after fork\n ; >.Tfi; pid = waitpid (child pid, &status,WUNTRACED) ; >

if (WIFEXITED(status))

cerr child pid exits: WEXITSTATUS(status) endl; else if (WIFSTOPPED(status))

cerr childpid stopped by: <WSTOPSIG(status else if (WIFSIGNALED(status))

cerr child pid killed by: WTERMSIG(status else perror( waitpid ); exit(0);. return 0;

endl; endl.;

Эта простая программа создает порожденный процесс, который подтвер-ает свое создание, а затем завершается с кодом завершения 15. Тем



1 ... 32 33 34 [ 35 ] 36 37 38 ... 98

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика