Я воссоздал пример на страницах руководства для системного вызова pipe()
и немного озадачен тем, почему сообщение 'Hello world'
печатается, когда родительский процесс завершается раньше дочернего процесса:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
const int BSIZE = 100;
int fildes[2];
char buff[BSIZE];
pipe(fildes);
pid_t rc = fork();
if (rc == 0) {
printf("in child\n");
close(fildes[0]);
write(fildes[1], "Hello world\n", 13);
close(fildes[1]);
} else {
printf("in parent\n");
close(fildes[1]);
read(fildes[0], buff, BSIZE);
close(fildes[0]);
printf("%s", buff);
}
return 0;
}
Результат этого выглядит так:
in parent
in child
Hello world
Судя по выходным данным, родительский процесс завершается до запуска дочернего процесса. Если да, то как печатается Hello world
? Если read
вызывается до того, как дочерний процесс завершит запись, я ожидаю, что buff
будет пустым (или, по крайней мере, неполным), когда read
вызывается.
Я удалил вызовы close()
, чтобы посмотреть, ждет ли read()
закрытия на другом конце или ждет ли метод pipe()
, но я получил те же результаты.
Это просто вопрос времени? Просто так получилось, что мне нужно добавить вызов wait() в родительский процесс?
Я не нашел ответа на pipe()
на странице руководства и не смог правильно сформулировать его для Google, поэтому решил попробовать спросить здесь.
Спасибо
Оба процесса выполняются одновременно. Таким образом, выходные данные двух процессов могут перекрываться.
Ваш код неполный без заголовков, и вы не объявляете переменную rc
, поэтому вы никогда не выполняли то, что опубликовали.
Дочерний процесс не выполняет печать для stdout
.
@AndrewHenle Вывод в stdout
— это самое первое, что делает ребенок.
@tkausl мне показалось, что порядок операторов печати именно такой. При добавлении операторов печати в конце каждого процесса (т. е. printf("out child")
) я вижу, что родительский элемент не завершается первым.
@Dúthomhas Да. Надо было написать, что ребенок не пишет Hello World
на stdout
.
@AndrewHenle Хех, не волнуйся, слова все время вылетают из моих пальцев не так, как надо. Я думаю, это потому, что я старею.
Порядок двух выходных строк in parent
и in child
не определен.
В родительском процессе read(fildes[0], buff, BSIZE)
происходит блокирующее чтение минимум 1 байта из канала. Вы можете убедиться в этом, например, добавив sleep(3)
перед ребенком write(fildes[1], "Hello world\n", 13);
. Запись будет атомарной, так как ее размер меньше PIPE_BUF
байт. Это эффективно синхронизирует дочерние и родительские процессы, поэтому родительский printf("%s", buff);
происходит последним. Канал будет буферизовать данные, если клиент выйдет раньше родительского.
Вот слегка переработанная версия вашей программы, которая показывает, что родительский элемент существует после клиента:
#include <stdio.h>
#include <unistd.h>
#define BSIZE 100
int main() {
int fildes[2];
pipe(fildes);
if (fork()) {
char buff[BSIZE];
printf("in parent\n");
close(fildes[1]);
read(fildes[0], buff, BSIZE); // blocking
close(fildes[0]);
printf("%s", buff);
printf("out parent\n");
} else {
printf("in child\n");
close(fildes[0]);
// sleep(3);
write(fildes[1], "Hello world\n", 13);
close(fildes[1]);
printf("out child\n");
}
}
какой пример вывода:
in parent
in child
out child
Hello world
out parent
Это предполагает, что родитель существует после клиента.
спасибо, дополнительные операторы журнала действительно помогли. Откуда вызов read()
узнает, когда вернуться? Блокируется ли он до тех пор, пока не будут прочитаны BSIZE
байт? Метод write()
отправляет только 13 байт, поэтому это может показаться неправильным. Или это когда другой конец трубы закрывается? Когда я удалил close()
, казалось, все закончилось хорошо. Извините, за вопросы, я не вижу информации о блокировке на странице руководства read
.
@learningc: он блокируется до тех пор, пока не станет доступен хотя бы один байт, после чего возвращает все доступные байты до BSIZE
. Ключевым моментом здесь является то, что запись в канал размером менее PIPE_BUF
байт гарантированно будет атомарной, поэтому запись в дочернем элементе будет атомарно записывать все 13 байт, поэтому чтение всегда будет получать ровно 13 байт.
Ох, хорошо. вызов read
прекратится после того, как станет доступно любое количество байтов. Здесь мы получаем все, потому что когда мы вызываем write
на вход трубы, мы записываем всю Hello world\n
строку. Спасибо, теперь это имеет смысл
Я только что подтвердил это, поставив еще один звонок write
прямо под первым. Печатается только первый. Спасибо, что объяснили еще раз!
From the output it looks like the parent process is finishing before the child process runs.
Что вам об этом говорит?