Каналы: pipe и FIFO
Канал pipe
Канал — однонаправленный поток байтов в ядре с двумя концами: в один пишут, из другого читают.
int fds[2];
pipe(fds); /* fds[0] — для чтения, fds[1] — для записи */
Канал не имеет имени, поэтому им пользуются родственные процессы: создать до fork — и дескрипторы унаследуются.
int fds[2];
char buf[64];
pipe(fds);
pid_t pid = fork();
if (pid == 0) { /* потомок — писатель */
close(fds[0]);
const char msg[] = "hello from child\n";
write(fds[1], msg, sizeof(msg) - 1);
close(fds[1]);
_exit(0);
}
close(fds[1]); /* родитель — читатель: обязательно закрыть лишний конец */
ssize_t n;
while ((n = read(fds[0], buf, sizeof buf)) > 0)
write(1, buf, n);
close(fds[0]);
wait(NULL);
Правила работы канала
| Ситуация | Результат |
|---|---|
read, канал пуст, писатели есть |
блокировка до появления данных |
read, канал пуст, все концы для записи закрыты |
0 — конец файла |
write, есть место |
запись (до PIPE_BUF = 4096 байт — атомарно) |
write, канал полон |
блокировка, пока читатель не освободит место |
write, все концы для чтения закрыты |
сигнал SIGPIPE (по умолчанию — завершение), write вернёт EPIPE |
Закрывайте лишние концы
Конец файла наступает, только когда закрыты все дескрипторы записи во всех процессах. Если читатель не закрыл свою копию fds[1], он никогда не получит 0 и зависнет навсегда.
Канал — ещё и средство синхронизации: read из пустого канала блокирует процесс, пока другой не запишет. Это можно использовать как «семафор для бедных».
Конвейер shell
ls | wc -l:
int fds[2];
pipe(fds);
if (fork() == 0) { /* ls */
dup2(fds[1], STDOUT_FILENO);
close(fds[0]); close(fds[1]);
execlp("ls", "ls", (char *)NULL);
_exit(127);
}
if (fork() == 0) { /* wc -l */
dup2(fds[0], STDIN_FILENO);
close(fds[0]); close(fds[1]);
execlp("wc", "wc", "-l", (char *)NULL);
_exit(127);
}
close(fds[0]); close(fds[1]); /* shell закрывает оба конца */
wait(NULL); wait(NULL);
Именованный канал FIFO
FIFO — канал с именем в файловой системе. Им могут пользоваться неродственные процессы.
mkfifo myfifo
cat myfifo & # читатель ждёт
echo hi > myfifo # писатель
mkfifo("/tmp/myfifo", 0600);
int fd = open("/tmp/myfifo", O_RDONLY); /* блокируется, пока кто-то не откроет на запись */
Данные через диск не идут: в ФС только имя (inode типа p), буфер — в ядре.
Обзор средств IPC
| Средство | Для чего | Неродственные процессы |
|---|---|---|
| pipe | поток байтов, родитель ↔ потомок | нет |
| FIFO | поток байтов | да |
| сигналы | уведомления (RT — с маленьким значением) | да |
| разделяемая память | быстрый обмен большими данными, нужна синхронизация | да (shm_open, shmget) |
| семафоры | синхронизация | да |
| очереди сообщений | сообщения с границами и приоритетами | да |
| сокеты | поток или сообщения, в том числе по сети | да |
Итог
pipe(fds):fds[0]— чтение,fds[1]— запись; создавать доfork, лишние концы закрывать.- Пустой канал блокирует читателя; без писателей — конец файла; без читателей —
SIGPIPE. - Конвейер shell =
pipe+fork+dup2+exec. - FIFO — именованный канал для неродственных процессов.
Практика решено 0 из 5
Создайте канал int fds[2]; в потомке закройте конец для чтения, запишите строку "hi" (2 байта) в конец для записи и выйдите _exit(0);
в родителе закройте конец для записи и прочитайте до 2 байт в buf.
int fds[2];
char buf[3] = {0};
pipe(fds);
if (fork() == 0) {
close(fds[0]);
write(fds[1], "hi", 2);
_exit(0);
}
close(fds[1]);
read(fds[0], buf, 2);
Что произойдёт в каждой ситуации?
| Процесс блокируется (ждёт) | read возвращает 0 (конец файла) | SIGPIPE и ошибка EPIPE | Операция выполняется сразу | |
|---|---|---|---|---|
| read из пустого канала, писатели ещё есть | ||||
| read из пустого канала, все концы для записи закрыты | ||||
| write в канал, все концы для чтения закрыты | ||||
| write в полный канал (буфер 64 КиБ заполнен), читатели есть | ||||
| write 100 байт в канал со свободным местом |
Родитель создал канал, потомок пишет данные и завершается, но родитель забыл закрыть свой конец для записи и читает в цикле до конца файла. Что будет?
Как в shell создать именованный канал с именем myfifo? (команда)
Какие средства IPC позволяют передавать данные между процессами?
Войдите, чтобы отмечать пройденные уроки.