Читаем Linux API. Исчерпывающее руководство полностью

Записывающий процесс закрывает считывающий дескриптор канала по другой причине. Если попытаться записать в канал, на другом конце которого нет открытого дескриптора, ядро пошлет сигнал SIGPIPE. По умолчанию это приводит к завершению процесса, хотя он может его перехватить или проигнорировать — в таком случае запись в канал завершится ошибкой EPIPE (канал поврежден). Получение сигнала SIGPIPE или ошибки EPIPE позволяет узнать состояние канала, поэтому неиспользуемый считывающий дескриптор следует закрывать.

Стоит отметить, что прерывание операции write() сигналом SIGPIPE обрабатывается особым образом. Обычно, когда обработчик сигнала прерывает запись (или другой «медленный» вызов), операция либо автоматически перезапускается, либо завершается ошибкой EINTR — это зависит от того, был ли обработчик установлен с помощью флага SA_RESTART для вызова sigaction() (см. раздел 21.5). Сигнал SIGPIPE ведет себя иначе, поскольку автоматический перезапуск записи или оповещение о ее прерывании обработчиком (предполагается, что тогда операция write() может быть перезапущена вручную) не имеет никакого смысла. Ни в том ни в другом случае последующий вызов write() не сможет завершиться успешно, так как канал останется поврежденным.

Если записывающий процесс не закроет считывающий конец канала, то сможет продолжать запись даже после того, как другой процесс закроет свой считывающий дескриптор. В какой-то момент записывающий процесс заполнит канал, и следующая попытка записи будет навсегда заблокирована.

Еще одна причина для закрытия неиспользуемых дескрипторов состоит в том, что канал можно уничтожить и освободить его ресурсы только после того, как будут закрыты все дескрипторы во всех процессах, ссылающихся на данный канал. В этот момент любые данные, которые в нем еще оставались, теряются.

Пример программы

Программа, представленная в листинге 44.2, демонстрирует применение канала для взаимодействия родительского и дочернего процессов. Данный пример иллюстрирует ранее озвученный нами факт: каналы являются потоками байтов. Для этого родитель выполняет запись за одну операцию, а потомок считывает данные небольшими блоками.

Главная программа открывает канал с помощью вызова pipe() и вызывает fork(), чтобы создать дочерний процесс . После этого родитель закрывает свой файловый дескриптор для чтения из канала и записывает в другой конец канала строку , переданную программе в виде аргумента командной строки. Затем родитель закрывает считывающий конец канала и делает вызов wait(), чтобы дождаться завершения дочернего процесса . После закрытия своего дескриптора для записывающего конца канала потомок входит в цикл, в котором считывает из канала блоки данных (размером до BUF_SIZE байт) и записывает их в стандартный вывод. Обнаружив символ конца файла , потомок выходит из цикла , выводит в конце символ новой строки, закрывает свой дескриптор для чтения из канала и завершается.

Вот какой результат можно получить, если запустить программу из листинга 44.2:

$ ./simple_pipe 'It was a bright cold day in April, '\

'and the clocks were striking thirteen.'

It was a bright cold day in April, and the clocks were striking thirteen.

Листинг 44.2. Использование канала для взаимодействия родительского и дочернего процессов

pipes/simple_pipe.c

#include

#include "tlpi_hdr.h"

#define BUF_SIZE 10

int

main(int argc, char *argv[])

{

int pfd[2]; /* Файловые дескрипторы канала */

char buf[BUF_SIZE];

ssize_t numRead;

if (argc!= 2 || strcmp(argv[1], " — help") == 0)

usageErr("%s string\n", argv[0]);

if (pipe(pfd) == -1) /* Создаем канал */

errExit("pipe");

switch (fork()) {

case -1:

errExit("fork");

case 0: /* Потомок читает из канала */

 if (close(pfd[1]) == -1) /* Записывающий конец не используется */

errExit("close — child");

for (;;) { /* Считываем данные из канала и направляем их в стандартный вывод */

 numRead = read(pfd[0], buf, BUF_SIZE);

if (numRead == -1)

errExit("read");

 if (numRead == 0)

break; /* Конец файла */

 if (write(STDOUT_FILENO, buf, numRead)!= numRead)

fatal("child — partial/failed write");

}

 write(STDOUT_FILENO, "\n", 1);

if (close(pfd[0]) == -1)

errExit("close");

_exit(EXIT_SUCCESS);

default: /* Родитель записывает в канал */

 if (close(pfd[0]) == -1) /* Считывающий конец не используется */

errExit("close — parent");

 if (write(pfd[1], argv[1], strlen(argv[1]))!= strlen(argv[1]))

fatal("parent — partial/failed write");

 if (close(pfd[1]) == -1) /* Потомок увидит символ завершения файла */

errExit("close");

 wait(NULL); /* Ждем завершения потомка */

exit(EXIT_SUCCESS);

}

}

pipes/simple_pipe.c

44.3. Каналы как средство синхронизации процессов
Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных