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

3. Устанавливаем обработчик для интересующего нас сигнала. При срабатывании он записывает в канал один байт. Следует выделить несколько замечаний относительно данного обработчика.

• В самом начале мы сделали записывающий конец канала неблокирующим во избежание ситуации, в которой сигнал будет доставляться настолько быстро, что повторные вызовы его обработчика заполнят весь канал, а операция write() внутри обработчика (как и весь процесс) заблокируется. (Это неважно в случае, если запись в заполненный канал завершится неудачно, поскольку предыдущие операции уже известили процесс о доставке сигнала.)

• Обработчик сигнала устанавливается после создания канала, чтобы избежать состояния гонки, связанного с преждевременной доставкой сигнала.

• Использование операции write() внутри обработчика является безопасным, так как это одна из функций, рассчитанных на работу с асинхронными сигналами (см. табл. 21.1).

4. Помещаем вызов select() в цикл, чтобы он перезапускался в случае прерывания со стороны обработчика сигнала (подобного рода перезапуск не является обязательным; он просто позволяет узнавать о появлении сигнала путем анализа аргумента readfds, не прибегая к проверке ошибки EINTR во время возвращения).

5. При успешном завершении вызова select() можно определить, дошел ли сигнал. Для этого нужно проверить, входит ли файловый дескриптор считывающего конца канала в набор readfds.

6. Приняв сигнал, считываем все данные, находящиеся в канале. Поскольку сигналов может быть несколько, создаем цикл, считывающий данные, пока (неблокирующая) операция read() не завершится ошибкой EAGAIN. Прочитав все содержимое канала, выполняем действия, которые нужно предпринять в ответ на доставку сигнала.

Эту методику обычно называют трюком с зацикленным каналом. Код с ее реализацией представлен в листинге 59.9.

Другие разновидности данной методики можно также использовать в сочетании с вызовами poll() и epoll_wait().

Листинг 59.9. Трюк с зацикленным каналом

Из файла altio/self_pipe.c

#include

#include

#include

#include

#include "tlpi_hdr.h"

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

static void

handler(int sig)

{

int savedErrno; /* В случае, если мы изменим 'errno' */

savedErrno = errno;

if (write(pfd[1], "x", 1) == -1 && errno!= EAGAIN)

errExit("write");

errno = savedErrno;

}

int

main(int argc, char *argv[])

{

fd_set readfds;

int ready, nfds, flags;

struct timeval timeout;

struct timeval *pto;

struct sigaction sa;

char ch;

/* Инициализируем 'timeout', 'readfds' и 'nfds' для вызова select() */

int fd, j;

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

usageErr("%s {timeout|-} fd…\n"

"\t\t('-' means infinite timeout)\n", argv[0]);

if (strcmp(argv[1], "-") == 0) {

pto = NULL; /* Бесконечный таймаут */

} else {

pto = &timeout

timeout.tv_sec = getLong(argv[1], 0, "timeout");

timeout.tv_usec = 0; /* Нет микросекунд */

}

nfds = 0;

FD_ZERO(&readfds);

for (j = 2; j < argc; j++) {

fd = getInt(argv[j], 0, "fd");

if (fd >= FD_SETSIZE)

cmdLineErr("file descriptor exceeds limit (%d)\n", FD_SETSIZE);

if (fd >= nfds)

nfds = fd + 1;

FD_SET(fd, &readfds);

}

if (pipe(pfd) == -1)

errExit("pipe");

FD_SET(pfd[0], &readfds); /* Добавляем в 'readfds' считывающий конец канала */

nfds = max(nfds, pfd[0] + 1); /* При необходимости корректируем 'nfds' */

flags = fcntl(pfd[0], F_GETFL);

if (flags == -1)

errExit("fcntl-F_GETFL");

flags |= O_NONBLOCK; /* Делаем считывающий конец неблокирующим */

if (fcntl(pfd[0], F_SETFL, flags) == -1)

errExit("fcntl-F_SETFL");

flags = fcntl(pfd[1], F_GETFL);

if (flags == -1)

errExit("fcntl-F_GETFL");

flags |= O_NONBLOCK; /* Делаем записывающий конец неблокирующим */

if (fcntl(pfd[1], F_SETFL, flags) == -1)

errExit("fcntl-F_SETFL");

sigemptyset(&sa.sa_mask);

sa.sa_flags = SA_RESTART; /* Перезапускаем прерванное чтение */

sa.sa_handler = handler;

if (sigaction(SIGINT, &sa, NULL) == -1)

errExit("sigaction");

while ((ready = select(nfds, &readfds, NULL, NULL, pto)) == -1 &&

errno == EINTR)

continue; /* Перезапускаем, если прервано по сигналу */

if (ready == -1) /* Непредвиденная ошибка */

errExit("select");

if (FD_ISSET(pfd[0], &readfds)) { /* Был вызван обработчик */

printf("A signal was caught\n");

for (;;) { /* Читаем данные из канала */

if (read(pfd[0], &ch, 1) == -1) {

if (errno == EAGAIN)

break; /* Данных больше не осталось */

else

errExit("read"); /* Какая-то другая ошибка */

}

/* Реагируем на сигнал должным образом */

}

}

/* Ищем в массиве, полученном из select(), готовые файловые дескрипторы */

printf("ready = %d\n", ready);

for (j = 2; j < argc; j++) {

fd = getInt(argv[j], 0, "fd");

printf("%d: %s\n", fd, FD_ISSET(fd, &readfds)? "r": "");

}

printf("%d: %s (read end of pipe)\n", pfd[0],

FD_ISSET(pfd[0], &readfds)? "r": "");

if (pto!= NULL)

printf("timeout after select(): %ld.%03ld\n",

(long) timeout.tv_sec, (long) timeout.tv_usec / 1000);

exit(EXIT_SUCCESS);

}

Из файла altio/self_pipe.c

59.6. Резюме
Перейти на страницу:

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

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

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

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

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

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

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

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