Предположим, у меня есть следующий код. Можно ли написать от ребенка обратно родитель? Могу ли я сделать это через ту же трубу, мифпип? Нужна ли мне другая функция ожидания?
int mypipe[2];
int i, pid;
pipe (mypipe);
value = fork ();
if (value == -1)
{
exit (0);
}
else if (value == 0)
{
read (mypipe[0], (char *) &i, sizeof (int));
cout << "i = " << i << endl;
exit (0);
}
else
{
i = 7;
write (mypipe[1], (char *) &i, sizeof (int));
wait (&pid);
}
До тех пор, пока вы каким-либо образом контролируете синхронизацию между двумя процессами, вы можете использовать один канал для записи от родителя к дочернему и от дочернего к родительскому. Например:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
int mypipe[2];
int i, pid;
pipe(mypipe);
pid = fork();
if (pid == -1)
{
perror("Cannot fork");
return 1;
}
else if (pid == 0)
{
read(mypipe[0], &i, sizeof(int));
printf("child: i = %d\n", i);
i += 13;
write(mypipe[1], &i, sizeof(int));
}
else
{
i = 7;
write(mypipe[1], &i, sizeof(int));
int status;
int corpse = wait(&status);
read(mypipe[0], &i, sizeof(int));
printf("parent: i = %d (child = %d = %d; status = 0x%.4X)\n",
i, pid, corpse, status);
}
return 0;
}
Пробный пробег:
child: i = 7
parent: i = 20 (child = 62299 = 62299; status = 0x0000)
Синхронизация здесь заключается в том, что родительский процесс ждет, пока ребенок умрет, прежде чем читать записку о самоубийстве от ребенка.
Это редко используется - вы обычно создаете две отдельные трубы, одну для родителя, чтобы писать ребенку, а другая - для ребенка, чтобы написать родителям - потому что это упрощает синхронизацию. Единственная проблема, с которой вы должны быть осторожны, - это не заполнение труб, чтобы родительский объект был заблокирован, пытаясь записать его дочернему элементу, когда ребенок заблокирован, пытаясь записать родительский (тупик). Если у вас простой полудуплексный протокол (так что каждый процесс знает, когда другой завершил свое текущее сообщение), или если у вас есть отдельные потоки чтения и записи в каждом процессе, вы можете избежать взаимоблокировки.
Обратите внимание, что некоторые системы (BSD) обеспечивают двунаправленные каналы. Сокеты также являются двунаправленными каналами связи.
Да, вы можете использовать трубу так, чтобы родитель записывал и ребенок читал, или чтобы ребенок записывал и родитель читал. Трубы являются однонаправленными, поэтому вы не можете делать то и другое (если вы хотите сделать то и другое, вам понадобится socketpair
).
С manpage:
pipe()
создает канал, однонаправленный канал данных, который может использоваться для межпроцессного взаимодействия. Массивpipefd
используется для возврата двух дескрипторов файлов, ссылающихся на концы трубы.pipefd[0]
относится к считываемому концу трубы.pipefd[1]
относится к концу записи трубы. Данные, записанные на конец записи, буферизуются ядром до тех пор, пока они не будут прочитаны с конца считывания. Для получения дополнительной информации см.pipe(7)
.
Я очистил вашу программу и скомпилировал ее в C (no cout
), и она работает:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int mypipe[2];
int i, pid;
int
main (int argc, char **argv)
{
int value;
pipe (mypipe);
value = fork ();
if (value == -1)
{
perror ("Cannot fork");
exit (1);
}
else if (value == 0)
{
read (mypipe[0], &i, sizeof (int));
printf ("i = %d\n", i);
wait (&pid);
exit (0);
}
else
{
i = 7;
write (mypipe[1], &i, sizeof (int));
exit (0);
}
}
Как вы можете видеть, никаких существенных изменений не произошло (за исключением того, что по какой-то причине вы пытались написать int, используя указатель char
который на малой машине будет писать 0).
Ваша программа читает дочерние элементы и записывает их в родительский.
Здесь версия, которая читается в родительском и записывает в дочерний элемент, также отлично работает. Как вы можете видеть, это просто вопрос об обмене чтениями и записью бит вокруг.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int mypipe[2];
int i, pid;
int
main (int argc, char **argv)
{
int value;
pipe (mypipe);
value = fork ();
if (value == -1)
{
perror ("Cannot fork");
exit (0);
}
else if (value == 0)
{
i = 7;
write (mypipe[1], &i, sizeof (int));
exit (0);
}
else
{
read (mypipe[0], &i, sizeof (int));
printf ("i = %d\n", i);
wait (&pid);
exit (0);
}
}
Трубы однонаправленные. Существует один конец записи и один конец. Если у вас есть несколько процессов (дочерний и родительский), которые читают этот канал, не определено, какие сообщения будут прочитаны каждым.
Да, ребенок может писать в (обычно канал "родитель-ребенок"), но если он затем оборачивается и читает снова из канала, до того, как родитель может, он будет читать свое собственное сообщение!
Лучше всего использовать пару трубок (по одному в каждом направлении) или соединение сокетов PF_LOCAL, которое является двунаправленным.
wait()
в дочернем процессе, чтобы родитель умер. В системах Unix родители могут ждать смерти своих детей, но не наоборот.wait
родителю. Тестовый сценарий сработал, потому чтоread
не завершится до того, какwrite
будет завершена.