Создание процессов и исполнение программ



Скачать 336,76 Kb.
страница7/7
Дата12.09.2017
Размер336,76 Kb.
1   2   3   4   5   6   7

Ожидание одного процесса - Пример


Эта программа показывает, как ожидать завершения одного подпроцесса, и работает следующим образом:

13-17 Создается подпроцесс, который распечатывает свой идентификатор, идентификатор родительского процесса и код завершения, который будет передан exit(2) в следующем операторе. Родительский процесс запоминает идентификатор подпроцесса в переменной pid.

Замечание: Проверка успешности исполнения fork(2) для краткости опущена, но на практике всегда надо проверять значение, возвращенное системным вызовом.

19 Родительский процесс объявляет, что он ожидает завершения своего подпроцесса.

21 Родитель ожидает завершения порожденного подпроцесса. Может возникнуть одна из двух ситуаций. В одном случае, порожденный процесс может завершиться раньше, чем родитель вызовет wait(2). Тогда wait(2) возвращает управление немедленно, сохранив слово состояния в своем параметре. В другом случае, подпроцесс может начать исполнение длинной и медленной программы. Тогда родитель прекращает исполнение (засыпает), пока

подпроцесс не завершится. Так же, как и в первом случае, формируется слово состояния. Возвращаемое значение, идентификатор завершившегося подпроцесса, сохраняется в переменной ret.

23 Родитель распечатывает значение, возвращенное wait(2). Оно должно соответствовать значению, распечатанному в строке 19.

Замечание: Если код возврата отрицательный, может возникнуть ситуация потери знака: система сохраняет в слове состояния только младший байт кода возврата, а макрос WEXITSTATUS интерпретирует его как беззнаковое число, поэтому отрицательные коды возврата будут преобразованы в положительные числа в диапазоне от 127 до 255.

24-26 WIFEXITED возвращает ненулевое значение, если подпроцесс завершился нормально. Затем макросом WEXITSTATUS вычисляется код завершения подпроцесса.

28-31 WIFSIGNALED возвращает ненулевое значение, если подпроцесс был прерван сигналом. WTERMSIG используется для вычисления номера сигнала.

Пример демонстрируется следующим образом:

$ wait1 child: pid=10701 ppid=10700 exit_code=1 parent: waiting for child=10701 parent: return value=10701 child's exit status is: 1 Файл: wait1.c ОЖИДАНИЕ ОДНОГО ПРОЦЕССА - ПРИМЕР 1 #include 2 #include 3 #include 4 #include 5 #include 6 #define EXIT_CODE 1 7 8 main() 9 { 10 pid_t pid, ret; 11 int status; 12 13 if ((pid = fork()) == 0){ /* child */ 14 printf("child: pid=%ld ppid=%ld exit_code=%d\n", 15 getpid(), getppid(), EXIT_CODE); 16 exit(EXIT_CODE); 17 } 18 19 printf("parent: waiting for child=%ld\n", pid); 20 21 ret = wait(&status); 22 23 printf("parent: return value=%ld\n", ret); 24 if (WIFEXITED(status)) 25 printf("child's exit status is: %d\n", 26 WEXITSTATUS(status)); 27 else 28 if (WIFSIGNALED(status)) 29 printf("signal is: %d\n", 30 WTERMSIG(status)); 31 32 exit(0); 33 }


      1. Ожидание нескольких процессов - Пример


В этом примере родительский процесс порождает два процесса, каждый из которых запускает команду echo(1). Затем родитель ждет завершения своих потомков, прежде чем продолжить свое исполнение.

Строки 17 и 18 показывают, как использовать wait(2) в этой ситуации. wait(2) вызывается из цикла while. Он вызывается три раза. Первые два вызова ожидают завершения процессов-потомков. Последний вызов возвращает неуспех, так как некого больше ожидать. Заметьте, что код завершения потомков здесь игнорируется.

Ниже эта программа исполняется два раза. Порядок исполнения трех процессов непредсказуем.

$ wait2 this is message one parent: waiting for children this is message two parent: all children terminated $ wait2 this is message two this is message one parent: waiting for children parent: all children terminated Файл: wait2.c ОЖИДАНИЕ НЕСКОЛЬКИХ ПРОЦЕССОВ - ПРИМЕР 1 #include 2 #include 3 #include 4 #include 5 #include 6 7 main() 8 { 9 if (fork() == 0) /* child */ 10 execl("/bin/echo", "echo", "this is", 11 "message one", (char *) 0); 12 if (fork() == 0) /* child */ 13 execl("/bin/echo", "echo", "this is", 14 "message two", (char *) 0); 15 printf("parent: waiting for children\n"); 16 17 while (wait(0) != -1) 18 ; /* null */ 19 20 printf("parent: all children terminated\n"); 21 exit(0); 22 }

      1. Ожидание нескольких процессов - Пример (Улучшенный)


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

12-13 Первый подпроцесс исполняет команду date(1).

14-15 Второй подпроцесс исполняет date(1) с неправильной опцией.

17-24 Цикл while ожидает завершения обоих подпроцессов. Заметьте, как идентификатор завершившегося подпроцесса присваивается переменной pid. Внутри цикла выбирается оператор печати, соответствующий этому идентификатору. Заметьте также, что эта программа не зависит от порядка завершения подпроцессов.

Этот пример демонстрируется следующим образом:

$ wait3 Sun Oct 6 10:25:39 EDT 1990 parent: waiting for children date: bad conversion parent: first child: 0 parent: second child: 1 parent: all children terminated Файл: wait3.c ОЖИДАНИЕ НЕСКОЛЬКИХ ПРОЦЕССОВ - ПРИМЕР (УЛУЧШЕННЫЙ) 1 #include 2 #include 3 #include 4 #include 5 #include 6 7 main() 8 { 9 pid_t child1, child2, pid; 10 int status; 11 12 if ((child1 = fork()) == 0) 13 execl("/bin/date", "date", (char *) 0); 14 if ((child2 = fork()) == 0) 15 execl("/bin/date", "date", "-x", (char *) 0); 16 printf("parent: waiting for children\n"); 17 while ((pid = wait(&status)) != -1) { 18 if (child1 == pid) 19 printf("parent: first child: %d\n", 20 WEXITSTATUS(status)); 21 else if (child2 == pid) 22 printf("parent: second child: %d\n", 23 WEXITSTATUS(status)); 24 } 25 printf("parent: all children terminated\n"); 26 27 exit(0); 28 }

      1. Вызов команды shell из программы на языке C - Пример


Этот пример демонстрирует исполнение команды shell из программы на C. Он показывает функцию общего назначения, которая принимает в качестве аргумента произвольную команду shell. Функция создает подпроцесс и исполняет shell, передав ему свой параметр в качестве команды. Этот пример похож на библиотечную функцию system(3C).

15-18 Подпроцесс исполняет shell. Флаг -c, переданный shell'у означает, что следующий аргумент - это команда.

19-20 Цикл while ожидает завершения определенного подпроцесса, а именно запущенного shell'а. Причина использования цикла состоит в том, что может существовать несколько завершившихся подпроцессов. Функция command() может быть использована в большой программе, которая создает другие подпроцессы. Кроме того, цикл прекращается, если системный вызов wait(2) завершается неуспехом. Например, wait(2) возвращает -1 и устанавливает errno в EINTR, если он был прерван перехваченным сигналом. Кроме того, он может возвратить -1, если fork(2) в строке 15 завершился неуспехом.

24 Код завершения возвращается в вызвавшую функцию.

27-33 Эта функция main() является тестовым драйвером для command(). main() исполняет некоторые команды shell и распечатывает код возврата функции command(). Эта программа компилируется командой

cc -DDEBUG -o command command.c

Эта техника используется для включения драйверной функции main() для тестирования и отладки функции.

Этот пример демонстрируется так:

$ command Sun Oct 6 12:04:04 EDT 1990 0 date: bad conversion 1 Файл: command.c ВЫЗОВ КОМАНДЫ ИЗ СИ-ПРОГРАММЫ - ПРИМЕР 1 #include 2 #include 3 #include 4 #include 5 #include 6 int command(char *); 7 8 /* run a shell command from C program */ 9 10 int command(char *cmd) 11 { 12 pid_t chpid, w; 13 int status; 14 15 if ((chpid = fork()) == 0) { 16 execlp("sh", "sh", "-c", cmd, (char *) 0); 17 exit(127); 18 } 19 while ((w = wait(&status)) != chpid && w != -1) 20 ; /* null */ 21 if (w == -1) 22 return(-1); 23 else 24 return(WEXITSTATUS(status)); 25 } 26 27 #if DEBUG 28 main() /* test command() function */ 29 { 30 printf("%d\n", command("date > Date; cat Date")); 31 printf("%d\n", command("date -x")); 32 } 33 #endif


      1. Ожидание изменения состояния подпроцесса


waitid(2) приостанавливает вызывающий процесс, пока один из его подпроцессов не изменит состояние. Изменения состояния включают в себя не только завершение, но также остановку по сигналу и продолжение работы после такой остановки. Если изменения происходили до вызова waitid(2), он возвращает управление немедленно. Еще одно отличие waitid(2) от wait(2) состоит в том, что waitid(2) позволяет указывать, каких потомков следует ожидать, а wait(2) всегда ждет всех потомков. Параметры idtype и id определяют, какие из подпроцессов должны обрабатываться:



idtype

какие подпроцессы обрабатывать

P_PID

подпроцесс с идентификатором, равным id

P_PGID

подпроцесс с идентификатором группы процессов, равным id

P_ALL

любой подпроцесс; id игнорируется

Выходной параметр infop содержит информацию о причине изменения состояния подпроцесса. включает файл , который содержит описание структуры siginfo_t. Страница руководства SIGINFO(5) описывает поля siginfo_t, относящиеся к waitid. Это:

int si_signo /* always equals SIGCLD for waitid */ int si_code /* contains a code identifying the cause of signal */ int si_status /* equals exit value or signal */ pid_t si_pid /* child process id */

Параметр options используется для задания того, какие изменения состояния следует принимать во внимание. Он формируется побитовым ИЛИ следующих значений:

WEXITED ожидать нормального завершения подпроцесса (по exit(2)).

WTRAPPED ожидать прерывания трассировки или точки останова в отлаживаемом процессе (ptrace(2)).

WSTOPPED ожидать, пока подпроцесс будет остановлен получением сигнала.

WCONTINUED ожидать, пока остановленный подпроцесс не начнет исполнение.

WNOHANG немедленно возвращать управление, если нет немедленно доступного слова состояния (ни один из подпроцессов не менял свое состояние). Использование этого флага приводит к "ожиданию" без блокировки, в режиме опроса. Это может быть использовано для динамического наблюдения за изменением состояния подпроцессов.

WNOWAIT сохранить подпроцесс в таком состоянии, что его слово состояния может быть получено повторным вызовом wait. Этот флаг означает неразрушающее использование waitid(2). Например, этот флаг позволяет процессу опросить состояние своего потомка, но не уничтожает процесс- «зомби», если потомок уже был таковым, так что группа процессов этого потомка по прежнему будет существовать. Поэтому другие процессы по-прежнему могут присоединяться к этой группе.

      1. Ожидание изменения состояния подпроцесса - Пример 1


Следующая страница показывает пример использования waitid(2), который работает следующим образом:

10 Объявляется структура siginfo_t.

12-15 Запускается подпроцесс. Этот процесс спит случайное число секунд, а затем завершается с кодом 5.

sleeper.c: 1 #include 2 #include 3 #include 4 5 main() 6 { 7 srand(getpid()); 8 sleep(rand()%10 +1); 9 exit(5); 10 }

17 Родитель ожидает завершения всех своих потомков, используя опции P_ALL и WEXITED.

18 Для значения, сформированного waitid(2), поле si_signo всегда будет равно SIGCLD (значение 18).

19 Значения si_code, связанные с SIGCLD, определены в . Эти значения таковы:

#define CLD_EXITED 1 /* child has exited */ #define CLD_KILLED 2 /* child was killed */ #define CLD_DUMPED 3 /* child has coredumped */ #define CLD_TRAPPED 4 /* traced child has stopped */ #define CLD_STOPPED 5 /* child has stopped on signal */ #define CLD_CONTINUED 6 /* stopped child has continued */

20 Если si_signo равно SIGCLD и si_code равно CLD_EXITED, то si_status будет равно коду завершения подпроцесса. Иначе, если si_signo равно SIGCLD, а si_code не равно CLD_EXITED, то si_status будет равно номеру сигнала, который вызвал изменение состояния

процесса. В этом примере подпроцесс нормально завершается с кодом 5, так что si_status имеет значение 5.

$ waitid parent: waiting for child : 8370 child signal no: 18 child signal code: 1 child status: 5 parent: child completed $ Файл: waitid1.c ОЖИДАНИЕ ИЗМЕНЕНИЯ СОСТОЯНИЯ ПОДПРОЦЕССА - ПРИМЕР 1 1 #include 2 #include 3 #include 4 #include 5 #include 6 7 main() 8 { 9 pid_t child1; 10 siginfo_t status; 11 12 if ((child1 = fork()) == 0) { 13 execl("sleeper", "sleeper", (char *) 0); 14 exit(1); 15 } 16 printf("parent: waiting for child : %ld\n",child1); 17 waitid(P_ALL, 0, &status, WEXITED); 18 printf("child signal no: %d\n", status.si_signo); 19 printf("child signal code: %d\n", status.si_code); 20 printf("child status: %d\n", status.si_status); 21 22 printf("parent: child completed\n"); 23 }

      1. Ожидание изменения состояния подпроцесса - Пример 2


На следующей странице приведен другой пример использования waitid(2), который работает так:

11 Объявляется структура siginfo_t. Она объявляется как static, поэтому она будет проинициализирована нулями.

15-18 Запускается подпроцесс. Этот процесс исполняет программу sleeper из предыдущего примера, которая спит случайное число секунд и завершается с кодом 5. Его идентификатор сохраняется в переменной child1.

20-21 Родительский процесс ожидает завершения определенного подпроцесса, используя опцию WEXITED. Опция WNOHANG заставляет waitid не приостанавливать исполнение вызвавшего процесса, если статус child1 не доступен немедленно. Эта опция используется для опроса завершения подпроцесса. waitid возвращает ноль, если он дождался подпроцесса или из-за опции WNOHANG.

22 Если si_pid остается нулевым, waitid возвратил управление из-за WHOHANG. Если si_pid равен идентификатору подпроцесса, то waitid возвратил статус этого подпроцесса.

23-27 Делается MAXTRIES попыток получить состояние завершения подпроцесса.

28-32 После MAXTRIES попыток подпроцессу посылается сигнал SIGKILL.

34 Распечатывается количество попыток получить статус.

35 Поле si_signo для waitid всегда будет равно SIGCLD (значение 18).

36 Поле si_code будет равно CLD_EXITED (значение 1), если подпроцесс нормально завершился. Оно будет равно CLD_KILLED (значение 2), если подпроцесс получил сигнал SIGKILL.

37-40 Если подпроцесс нормально завершился, si_status равен его коду завершения. Если он был убит сигналом, si_status будет равен номеру сигнала, вызвавшего завершение процесса. В этом случае, номер сигнала будет SIGKILL (значение 9).

$ waitid2 parent: waiting for child 8291 sending signal to child tries = 8 child signal no: 18 child signal code: 2 child signal is: 9 parent: child completed $ Файл: waitid2.c ОЖИДАНИЕ ИЗМЕНЕНИЯ СОСТОЯНИЯ ПОДПРОЦЕССА - ПРИМЕР 2 ... 9 main() 10 { 11 static siginfo_t stat; ... 15 if ((child1 = fork()) == 0) { 16 execl("sleeper", "sleeper", (char *) 0); 17 exit(1); 18 } 19 printf("parent: waiting for child %ld\n", child1); 20 while (waitid(P_PID, child1, &stat, 21 WNOHANG|WEXITED) != -1) { 22 if (stat.si_pid == 0) { 23 if (try < MAXTRIES) { 24 try++; 25 sleep(1); 26 continue; 27 } 28 else { 29 printf("sending signal to child\n"); 30 kill(child1, SIGKILL); 31 continue; 32 } 33 } 34 printf("tries = %d\n", try); 35 printf("child signal no: %d\n", stat.si_signo); 36 printf("child signal code: %d\n", stat.si_code); 37 if (stat.si_code == CLD_EXITED) 38 printf("exit status is: %d\n", stat.si_status); 39 else 40 printf("child signal is: %d\n", stat.si_status); 41 } 42 printf("parent: child completed\n"); 43 }

      1. Ожидание изменения состояния подпроцесса


waitpid(2) приостанавливает исполнение вызывающего процесса, пока один из его потомков не изменит состояние. Если такое изменение произошло до вызова waitpid(2), он возвращает управление немедленно. pid задает набор подпроцессов, для которых запрашивается состояние. Вызов waitpid(2) требует меньше дополнительного кода для использования, чем waitid(2) (в частности, не требуется создавать структуру siginfo_t), но не позволяет делать некоторые вещи, которые можно сделать при помощи waitid(2)



pid

состояние запрашивается

-1

для всех подпроцессов

>0

для подпроцесса с идентификатором pid

0

для любого подпроцесса с тем же идентификатором группы, что у вызывающего процесса

<-1

для любого подпроцесса, чей идентификатор группы процессов равен -pid

Параметр options формируется побитовым ИЛИ следующих значений, которые описаны в

WNOHANG то же значение, что и для waitid(2).

WUNTRACED то же, что WSTOPPED для waitid(2).

WCONTINUED то же, что и для waitid(2).

WNOWAIT то же, что и для waitid(2).

Функция waitpid(2) эквивалентна вызову waitid(2) с добавлением опций WEXITED | WTRAPPED к значению соответствующего параметра waitpid(2). Если передан ненулевой указатель stat_loc, то слово состояния подпроцесса будет сохранено по этому указателю. Затем полученное значение может быть проанализировано макросами из wstat(5).


      1. Подпрограмма, исполняемая при завершении


atexit(3C) используется для определения функции, которая должна быть вызвана при нормальном завершении программы. Нормальным завершением в данном случае считается вызов функции exit(2) или возврат из функции main(), ненормальным — завершение по сигналу или по _exit(2).

atexit(3C) гарантируют пользователю возможность зарегистрировать по крайней мере 32 таких функции, которые будут вызываться в порядке, обратом их регистрации.

Стандартная библиотека языка C сама использует atexit(3C) для выполнения действий при завершении программы. Наиболее важным из таких действий является сброс буферов потоков буферизованного ввода-вывода. Обработчики, используемые библиотекой для собственных нужд, не занимают 32 обработчка, гарантированные пользователю.

Деструкторы глобальных и статических переменных C++ вызываются через тот же механизм, что и atexit(3C).

      1. Подпрограмма, вызываемая при завершении - Пример


Этот пример иллюстрирует использование библиотечной функции atexit(3C).

13 Библиотечная функция atexit(3C) вызывается, чтобы зарегистрировать функцию finish для исполнения при выходе из программы.

20-27 Родительский процесс ожидает завершения своих потомков.

28 Родитель завершается системным вызовом exit(2). При этом вызывается функция finish.



31-35 Функция finish. Она автоматически вызывается при завершении программы.

Файл: atexit.c ПОДПРОГРАММА, ВЫЗЫВАЕМАЯ ПРИ ЗАВЕРШЕНИИ - ПРИМЕР 1 #include 2 #include 3 #include 4 #include 5 #include 6 static void finish(void); 7 8 main() 9 { 10 pid_t child1, child2, pid; 11 int status; 12 13 atexit(finish); 14 if ((child1 = fork()) == 0) 15 execl("/bin/date", "date", (char *) 0); 16 if ((child2 = fork()) == 0) 17 execl("/bin/date", "date", "-x", (char *) 0); 18 19 printf("parent: waiting for children\n"); 20 while ((pid = wait(&status)) != -1) { 21 if (child1 == pid) 22 printf("parent: first child: %d\n", 23 WEXITSTATUS(status)); 24 else if (child2 == pid) 25 printf("parent: second child: %d\n", 26 WEXITSTATUS(status)); 27 } 28 exit(0); 29 } 30 31 static void finish(void) 32 { 33 /* additional processing */ 34 printf("parent: all children terminated\n"); 35 }

Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7


База данных защищена авторским правом ©grazit.ru 2017
обратиться к администрации

    Главная страница