Функция fork()
необычна;он возвращает два раза, по одному в каждом из двух разных (но тесно связанных) процессов, если это не сработает.Он возвращает -1
, если это не удается (в исходном процессе, по необходимости).Если это успешно, он возвращает 0
в дочернем процессе, в то время как он возвращает PID дочернего процесса в исходном (родительском) процессе, который никогда не будет 0
(или отрицательным).
Вы должнынаучиться код инструмента.В контексте кода, использующего fork()
, обычно эффективно напечатать PID (идентификатор процесса) и PPID (идентификатор родительского процесса), чтобы помочь выяснить, какой процесс печатает что.Короткий ответ на ваш вопрос о том, какой процесс печатает, состоит в том, что два процесса печатают данные из первого вызова printf()
, а четыре процесса печатают данные из второго вызова printf()
- если вы не передадите выходные данные программы другому (например cat
), в этом случае кажется, что некоторые процессы печатают данные несколько раз.(См. Также printf()
аномалия после fork()
.)
Давайте обработаем ваш код:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
printf("PID = %d\n", (int)getpid());
int x = 10;
int y = 10;
int pid = fork();
if (pid != 0)
{
x++;
y--;
}
printf("1: x = %i y = %i (PID %d, PPID %d)\n",
x, y, (int)getpid(), (int)getppid());
pid = fork();
if (pid != 0)
{
x++;
y--;
}
printf("2: x = %i y = %i (PID %d, PPID %d)\n",
x, y, (int)getpid(), (int)getppid());
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
{
printf("%d: child %d exited with status 0x%.4X\n",
(int)getpid(), corpse, status);
}
return (getpid() % 16);
}
Оператор return в конце main()
возвращаетненулевой статус выхода 15 раз из 16, просто чтобы сделать вещи немного интереснее.
Примеры запусков (программы ./fork43
, построенной из fork43.c
) - один без трубопровода, другой с трубопроводами:
$ ./fork43
PID = 26226
1: x = 11 y = 9 (PID 26226, PPID 23612)
2: x = 12 y = 8 (PID 26226, PPID 23612)
1: x = 10 y = 10 (PID 26227, PPID 26226)
2: x = 11 y = 9 (PID 26228, PPID 26226)
2: x = 11 y = 9 (PID 26227, PPID 26226)
26226: child 26228 exited with status 0x0400
2: x = 10 y = 10 (PID 26229, PPID 26227)
26227: child 26229 exited with status 0x0500
26226: child 26227 exited with status 0x0300
$ ./fork43 | cat
PID = 26230
1: x = 11 y = 9 (PID 26230, PPID 23612)
2: x = 11 y = 9 (PID 26233, PPID 26230)
PID = 26230
1: x = 10 y = 10 (PID 26232, PPID 26230)
2: x = 10 y = 10 (PID 26234, PPID 26232)
PID = 26230
1: x = 10 y = 10 (PID 26232, PPID 26230)
2: x = 11 y = 9 (PID 26232, PPID 26230)
26232: child 26234 exited with status 0x0A00
PID = 26230
1: x = 11 y = 9 (PID 26230, PPID 23612)
2: x = 12 y = 8 (PID 26230, PPID 23612)
26230: child 26233 exited with status 0x0900
26230: child 26232 exited with status 0x0800
$
При первом запуске исходный (родительский) процесс имеет PID 26226. Он разветвляется, а его дочерний - 26227. Родительскому процессу сообщается его дочерний PID, поэтому он увеличивает x
и уменьшает * 1029.*;затем он выполняет оператор printf()
, где строка формата начинается с 1:
, печатая значения x
как 11 и y
как 9. В этом запуске родительский процесс снова разветвляется, создавая своего второго дочернего элемента с PID 26228.родительский элемент увеличивает x
и уменьшает y
снова и выполняет оператор printf()
, где строка формата начинается 2:
, прежде чем что-либо еще произойдет.Затем он достигает цикла wait()
и ожидает смерти одного из его дочерних элементов.
Затем первый дочерний процесс (26227) выполняет инструкцию printf()
, где строка формата начинается с 1:
, но значенияx
и y
оба неизменны на 10. Затем он достигает второго разветвления и создает свой собственный дочерний процесс (внука исходного процесса) с PID 26229.
Второй дочерний элемент 26228 имеет (x,y) значения (11, 9), потому что это были значения, когда он был разветвлен, поэтому он печатает эти значения при выполнении оператора printf()
, где строка формата начинается с 2:
.
после первогодочерние вилки, ему сообщают PID своего дочернего элемента, поэтому он увеличивает x
и уменьшает y
, печатая значения 11 и 9.
Второй дочерний элемент завершается, и его статус сообщается исходным процессом.Процесс внука теперь выполняет оператор printf()
, где строка формата начинается с 2:1. Since the value in
pid was 0 twice, the values in
x and
y`, все еще неизменными на 10. Затем он завершается.
Первый дочерний элемент может сообщитьчто его ребенок вышел, а потом сам вышел.Родительский процесс сообщает, что первый дочерний элемент завершил работу и также завершил работу.
В целом имеется одна копия вывода PID
, две копии 1:
и 4 копии 2:
(плюс три 'дочерние выходы 'отчеты).
Второй запуск с выводом по конвейеру на cat
показывает, что выходные данные полностью буферизуются, а не буферизируются строкой, поэтому процессы сбрасывают записанные данные при выходе, а не при переводе строкнапечатаны.Вот почему существует 4 копии вводного вывода PID = 26230
, а также 4 копии вывода 1:
.До сих пор есть только 3 отчета о «выходе ребенка».
Печать информации о PID, как это, является ценным помощником в понимании кода.Важно признать, что последовательность вывода не является фиксированной.Разные прогоны могут создавать разные последовательности (совершенно отдельно от разных номеров PID) просто из-за алгоритма планирования и того, что еще происходит на компьютере одновременно.