Pipes
Продолжаем учиться связывать процессы между собой
Пока что мы умеем передавать вывод одного процесса другому через неименнованный канал:
echo "hello" | wc -l
Ядро считает, сколько процессов записывают в канал и сколько читают. Когда пишущие процессы заканчиваются, то чтение из канала возвращает EOF. Если же не остается больше читающих процессов, то пишущие убиваются.
Теперь опробуем все в коде:
int main() {
pid_t child;
int fds[2];
pipe(fds);
if (child = fork()) {
close(fds[0]); // надо закрывать, чтоб процесс мог убиться
for (int i = 0; i < 10000; ++i) {
write(fds[1], "hello", 5);
}
close(fds[1]);
wait(NULL);
} else {
close(fds[1]); // аналогично
for (int i = 0; i < 3; ++i) {
char buf[10] = {0};
if (read(fds[0], buf, sizeof(buf)) <= 0) {
return 0;
}
puts(buf);
}
}
}
Пример 2:
int main() {
int fds[2];
pipe(fds);
if (!fork()) {
// child proc 1
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execlp("yes", "yes", NULL);
perror("yes");
exit(EXIT_FAILURE);
} else if (!fork()) {
// child proc 2
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
execlp("head", "head", NULL);
perror("head");
exit(EXIT_FAILURE);
}
close(fds[0]); close(fds[1]);
while (waitpid(-1, NULL, 0) != -1);
}
Пример 3: раздаем команды через pipe
void handler(int sig) {
write(STDOUT_FILENO, "killing zombies\n", strlen("killing zombies\n"))
wait(NULL);
}
int main() {
int fds[2];
pipe(fds);
signal(SIGCHLD, handler); // обработчик, чтоб избавляться от процессов зомби
// или так signal(SIGCHLD, SIG_IGN);
for (int i = 0; i < 4; ++i) {
if (!fork()) {
close(fds[1]);
int task;
while(read(fds[0], &task, sizeof(task)) > 0) {
printf("pid %d: sleeping for %d seconds\n", getpid(), task);
sleep(task);
}
return EXIT_SUCCESS;
}
}
for (int i = 1; i < 10; ++i) {
int task = i % 3 + 1;
write(fds[1], &task, sizeof(task));
}
close(fds[1]);
while (waitpid(-1, NULL, 0) != -1);
}
Posix дает гарантию: когда мы пишем в pipe данные размера меньше PIPE_BUF, то данные записываются атомарно.
SIGCHLD - специальный сигнал, который приходит, когда завершается какой-то процесс.
self pipe trick - в обработчике, просто записываем 1 байт в отдельный канал, чтоб не было асинхронности.