У меня есть следующая программа, просто чтобы проверить, понимаю ли я разветвление и общую память. Таким образом, он создает общую память, вилки, вилки, вилки, и в каждую вилку записывает данные и завершает работу.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main() {
key_t key = ftok(".", 'x');
int shmid = shmget(key, sizeof(int)*4, IPC_CREAT | 0666);
if (shmid < 0) {
perror("shmget\n");
return 1;
}
int *arr = shmat(shmid, NULL, 0);
pid_t c1 = fork();
if (c1==0) {
pid_t c2 = fork();
if (c2==0) {
pid_t c3 = fork();
if (c3==0) {
arr[0] = 10;
} else {
arr[1] = 11;
}
exit(0);
} else {
arr[2] = 12;
}
exit(0);
} else {
arr[3] = 13;
wait(NULL);
wait(NULL);
wait(NULL);
for (int i=0; i<4; i++) printf("%d ", arr[i]);
printf("\n");
}
exit(0);
}
Это печатает 0 11 12 13
, показывая, что по какой-то причине значение 10
никогда не присваивается arr[0]
.
Я ожидал, что каждый процесс дойдет до какого-то вызова exit(0)
. Я думаю, особенно тот, что после третьего разветвления, должен достичь того же вызова exit(0)
, который достигается вторым разветвлением.
Однако когда я добавляю явный вызов выхода внутри третьего блока разветвления (т. е. после c3==0
), он выполняется так, как ожидалось.
Когда я пишу это, мне вдруг приходит в голову догадка о том, почему это происходит: не потому ли, что второй дочерний элемент достигает выхода раньше третьего, и поскольку он выходит, то связь от четвертого до родителя разрывается?
вы можете только wait()
для своих детей. Вызов wait(NULL)
3 раза в исходном родительском процессе не будет ждать внуков.
Проверьте, что возвращают эти вызовы wait
. Могу поспорить, что хотя бы один из них потерпит неудачу (поскольку вы не запускаете все дочерние процессы в первом, исходном, родительском процессе).
@Бармар, мне это как бы интересно. Но по всему Интернету я вижу такие вещи, как то, что пишет Крейг Эсти выше, которое, похоже, предполагает, что вы можете неоднократно вызывать ожидание в одном и том же месте. В любом случае я переместил ожидания в другие места и теперь получаю загадочную ошибку недопустимого аргумента для shmget
. Кажется, это как-то связано с использованием уже выделенной памяти, но я не знаю почему. В любом случае, скорее всего, это не связано с вышеупомянутой проблемой, поэтому мне, вероятно, придется разобраться в этом где-то еще.
Вы можете вызывать wait()
несколько раз, если у вас есть дочерние процессы, созданные исходным родительским процессом. Но ваш код этого не делает, он создает только один дочерний процесс. И этот дочерний процесс, в свою очередь, создает новый дочерний процесс, делая исходный родительский процесс дедушкой и бабушкой. Этого внука должен дождаться его собственный прямой родитель.
@Someprogrammerdude Да, это имеет смысл - но я видел, как предположил Крейг, делая это, даже когда есть внуки. Я почти уверен, что по крайней мере на одной веб-странице и в одном видео на YouTube я видел, как люди писали это. Я не уверен, это просто неправильно или происходит что-то еще, чего я не понимаю.
Либо они тоже не правы, либо вы неправильно прочитали. Может оказаться затруднительным определить, создает ли родительский элемент несколько собственных дочерних элементов или они создаются дочерними элементами.
Попробуйте добавить код в каждый процесс, чтобы вывести getpid()
и getppid()
, чтобы увидеть взаимосвязи.
В Linux вы можете использовать prctl()
из <sys/prctl.h>
вместе с PR_SET_CHILD_SUBREAPER
(см. prctl(2)), чтобы текущий процесс извлекал всех своих потомков (вместо того, чтобы позволить системному процессу собирать своих косвенных потомков).
К сожалению, на каждое хорошее видео или урок приходится десять не столь хороших. Найти хорошие непросто.
В настоящее время вы пытаетесь wait
выполнить три процесса в исходном родительском процессе, но у него только один дочерний процесс, поэтому только первый wait
успешен. Нет никакой гарантии, что другие процессы готовы к записи в общую память до того, как вы распечатаете результат.
Чтобы гарантировать, что все дети закончили работу, каждый родитель может дождаться своего ребенка.
Пример:
pid_t c1 = fork();
if (c1==0) { // child
pid_t c2 = fork();
if (c2==0) { // child
pid_t c3 = fork();
if (c3==0) { // child
arr[0] = 10;
exit(0);
} else {
arr[1] = 11;
waitpid(c3, NULL, 0); // waiting for child
}
exit(0);
} else {
arr[2] = 12;
waitpid(c2, NULL, 0); // waiting for child
}
exit(0);
} else {
arr[3] = 13;
waitpid(c1, NULL, 0); // waiting for child
for (int i=0; i<4; i++) printf("%d ", arr[i]);
printf("\n");
}
@zwol Тебе, должно быть, очень нравится симметрия. Мне нравится, как вы внесли изменения в мой ответ, и теперь я думаю: «Стоит ли мне удалить все exit(0)
», раз уж вы только что сделали их ненужными? :-) Хотелось бы, чтобы была кнопка +1 для редактирования.
Все, о чем я думал, это то, что «этот код был бы менее запутанным для новичков, если бы он явно вызывал waitpid только на родительской стороне каждого форка»…
Вам следует добавить четвертый
wait(NULL);
. Или еще лучше:while (wait(NULL) >= 0);