Вы написали вилочную бомбу .Это когда процесс порождает потомков, которые потом порождают потомков, которые потом порождают потомков ... пока вся память не будет использована или операционная система не убьет ее.
fork
может быть трудно обрести голову.При его вызове родительские процессы и продолжают выполнение кода.Из-за этого важно отделить дочерний код и убедиться, что ребенок выходит.Если родитель и дочерний элемент выполняют один и тот же код, как вы гарантируете, что дочерний элемент останавливается, а родительский продолжает?
fork
в родительском элементе возвращает идентификатор процесса дочернего элемента.fork
в потомке возвращает 0. И если есть ошибка, он вернет -1.Таким образом, типичная идиома выглядит следующим образом:
pid_t pid = fork();
if( pid == 0 ) {
// child
exit(0);
}
else if( pid < 0 ) {
// error
perror("fork failed");
}
// parent
После того, как вы закончите, вы должны дождаться завершения всех детей.wait
вернется, когда любой дочерний процесс завершится, вернув дочерний pid.Вам нужно продолжать вызывать wait
до тех пор, пока он не вернется с <0, указывающим на то, что дочерних процессов больше не было. </p>
void wait_all() {
while(wait(NULL) > 0);
}
Соединение их всех вместе и удаление компиляции при выполнении 15 раз ...
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
// print
printf("%d", j);
// job's done
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
}
wait_all();
printf("\n");
Несмотря на это, я все еще приводил их в порядок.Возможно, потому что каждый ребенок выполняет одну и ту же очень простую и предсказуемую вещь.Все они занимают примерно одинаковое количество времени для выполнения, поэтому они, вероятно, будут выходить в одном и том же порядке.
Side Note : stdout
- строка с буферизациейЭто означает, что его содержимое будет отображаться или сбрасываться только при появлении новой строки.printf("%d", j);
не будет отображаться, пока не будет напечатана новая строка или stdout
не будет сброшено.exit
очистит и закроет все потоки, так что все в порядке.
Однако дочерний объект наследует буфер родителя.Это может привести к довольно странному поведению, если родитель оставляет что-либо в буфере.Например ...
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
printf("child: %d, ", j);
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
printf("parent: %d, ", j);
}
wait_all();
printf("\n");
Мы получаем:
child: 0, parent: 0, child: 1, parent: 0, parent: 1, child: 2, parent: 0, parent: 1, parent: 2, child: 3, parent: 0, parent: 1, parent: 2, parent: 3, child: 4, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, child: 5, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, child: 6, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, child: 7, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, child: 8, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, child: 9, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, parent: 9,
Что происходит?Это ошеломило меня на некоторое время.Немного понятнее, если мы поместим новую строку в печать ребенка.
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
printf("child: %d\n", j);
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
printf("parent: %d, ", j);
}
wait_all();
printf("\n");
child: 0
parent: 0, child: 1
parent: 0, parent: 1, child: 2
parent: 0, parent: 1, parent: 2, child: 3
parent: 0, parent: 1, parent: 2, parent: 3, child: 4
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, child: 5
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, child: 6
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, child: 7
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, child: 8
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, child: 9
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, parent: 9,
Каждый раз, когда ребенок разветвляется, он получает копию буфера stdout
родителя.Каждый раз в цикле родитель добавляет parent: %d,
в этот буфер без очистки.Когда дочерний элемент делает printf("child: %d\n", j);
, он добавляет к существующему буферу и затем сбрасывает его.
- Первый дочерний элемент копирует
""
из родительского элемента, добавляет child: 0\n
исбрасывает. - Родитель добавляет
parent: 0,
к stdout
. - Второй дочерний элемент копирует
parent: 0,
, добавляет child: 1\n
и сбрасывает. - Родитель добавляет
parent: 1,
до stdout
, теперь его parent: 0, parent: 1,
- Третий дочерний элемент копирует
parent: 0, parent: 1,
, добавляет child: 2\n
и сбрасывает.
И т. Д.
Промывка stdout
после того, как родитель делает частичную печать, избегает этого и гарантирует, что все отображается так, как это происходит.
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
printf("child: %d\n", j);
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
printf("parent: %d, ", j);
fflush(stdout);
}
wait_all();
printf("\n");
parent: 0, child: 0
parent: 1, child: 1
parent: 2, child: 2
parent: 3, child: 3
parent: 4, child: 4
parent: 5, child: 5
parent: 6, child: 6
parent: 7, child: 7
parent: 8, child: 8
parent: 9, child: 9