switch (pid) {
case -1:
perror("fork failed");
exit(1);
case 0:
message = "This is the child";
n = 3;
break;
default:
message = "This is the parent";
n = 5;
break;
}
Как это работает
Если вы выполните только что приведенную программу с помощью команды ./fork2 & и затем вызовите программу ps после завершения дочернего процесса, но до окончания родительского, то увидите строку, подобную следующей. (Некоторые системы могут сказать <zombie> вместо <defunct>.)
$ ps -аl
F S UID PID PPID С PRI NI ADDR SZ WCHAN TTY TIME CMD
004 S 0 1273 1259 0 75 0 - 589 wait4 pts/2 00:00:00 su
000 S 0 1274 1273 0 75 0 - 731 schedu pts/2 00:00:00 bash
000 S 500 1463 1262 0 75 0 - 788 schedu pts/1 00:00:00 oclock
000 S 500 1465 1262 0 75 0 - 2569 schedu pts/1 00:00:01 emacs
000 S 500 1603 1262 0 75 0 - 313 schedu pts/1 00:00:00 fork2
003 Z 500 1604 1603 0 75 0 - 0 do_exi pts/1 00:00:00 fork2 <defunct>
000 R 500 1605 1262 0 81 0 - 781 - pts/1 00:00:00 ps
Если родительский процесс завершится необычно, дочерний процесс автоматически получит в качестве родителя процесс с PID, равным 1 (init). Теперь дочерний процесс — зомби, который уже не выполняется, но унаследован процессом init из-за необычного окончания родительского процесса. Зомби останется в таблице процессов, пока не пойман процессом init. Чем больше таблица, тем медленнее эта процедура. Следует избегать процессов-зомби, поскольку они потребляют ресурсы до тех пор, пока процесс init не вычистит их.
Есть еще один системный вызов, который можно применять для ожидания дочернего процесса. Он называется waitpid и применяется для ожидания завершения определенного процесса.
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *stat_loc, int options);
Аргумент pid — конкретный дочерний процесс, окончания которого нужно ждать. Если он равен –1, waitpid вернет информацию о любом дочернем процессе. Как и вызов wait, он записывает информацию о состоянии процесса в место, указанное аргументом stat_loc, если последний не равен пустому указателю. Аргумент options позволяет изменить поведение waitpid. Наиболее полезная опция WNOHANG мешает вызову waitpid приостанавливать выполнение вызвавшего его процесса. Ее можно применять для выяснения, завершился ли какой-либо из дочерних процессов, и если нет, то продолжать выполнение. Остальные опции такие же, как в вызове wait.
Итак, если вы хотите, чтобы родительский процесс периодически проверял, завершился ли конкретный дочерний процесс, можно использовать следующий вызов:
waitpid(child_pid, (int *)0, WNOHANG);
Он вернет ноль, если дочерний процесс не завершился и не остановлен, или child_pid, если это произошло. Вызов waitpid вернет -1 в случае ошибки и установит переменную errno. Это может произойти, если нет дочерних процессов (errno равна ECHILD), если вызов прерван сигналом (EINTR) или аргумент options неверный (EINVAL).
Перенаправление ввода и вывода
Вы можете применить ваши знания о процессах для изменения поведения программ, используя тот факт, что открытые файловые дескрипторы сохраняются вызовами fork и exec. Следующий пример из упражнения 11.6 содержит программу-фильтр, которая читает из стандартного ввода и пишет в свой стандартный вывод, выполняя при этом некоторое полезное преобразование.
Далее приведена программа очень простой фильтрации upper.c, которая читает ввод и преобразует строчные буквы в прописные:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main() {
int ch;
while ((ch = getchar()) != EOF) {
putchar(toupper(ch));
}
exit(0);
}
Когда вы выполните программу, она сделает то, что и ожидалось:
$ ./upper
hello THERE
HELLO THERE
^D
$
Вы, конечно, можете применить ее для преобразования символов файла, используя перенаправление, применяемое командной оболочкой:
$ cat file.txt
this is the file, file.txt, it is all lower case.
$ ./upper < file.txt
THIS IS THE FILE, FILE.TXT, IT IS ALL LOWER CASE.
Что если вы хотите применить этот фильтр из другой программы? Программа useupper.c принимает имя файла как аргумент и откликается сообщением об ошибке при некорректном вызове:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
char *filename;
if (argc != 2) {
fprintf (stderr, "usage: useupper file\n");
exit(1);
}
filename = argv[1];
Вы повторно открываете стандартный ввод, снова при этом проверяете наличие любых ошибок, а затем применяете функцию execl для вызова программы upper:
if (!freopen(filename, "r", stdin)) {