3. Устанавливаем обработчик для интересующего нас сигнала. При срабатывании он записывает в канал один байт. Следует выделить несколько замечаний относительно данного обработчика.
• В самом начале мы сделали записывающий конец канала неблокирующим во избежание ситуации, в которой сигнал будет доставляться настолько быстро, что повторные вызовы его обработчика заполнят весь канал, а операция write() внутри обработчика (как и весь процесс) заблокируется. (Это неважно в случае, если запись в заполненный канал завершится неудачно, поскольку предыдущие операции уже известили процесс о доставке сигнала.)
• Обработчик сигнала устанавливается после создания канала, чтобы избежать состояния гонки, связанного с преждевременной доставкой сигнала.
• Использование операции write() внутри обработчика является безопасным, так как это одна из функций, рассчитанных на работу с асинхронными сигналами (см. табл. 21.1).
4. Помещаем вызов select() в цикл, чтобы он перезапускался в случае прерывания со стороны обработчика сигнала (подобного рода перезапуск не является обязательным; он просто позволяет узнавать о появлении сигнала путем анализа аргумента readfds, не прибегая к проверке ошибки EINTR во время возвращения).
5. При успешном завершении вызова select() можно определить, дошел ли сигнал. Для этого нужно проверить, входит ли файловый дескриптор считывающего конца канала в набор readfds.
6. Приняв сигнал, считываем все данные, находящиеся в канале. Поскольку сигналов может быть несколько, создаем цикл, считывающий данные, пока (неблокирующая) операция read() не завершится ошибкой EAGAIN. Прочитав все содержимое канала, выполняем действия, которые нужно предпринять в ответ на доставку сигнала.
Эту методику обычно называют
Другие разновидности данной методики можно также использовать в сочетании с вызовами poll() и epoll_wait().
Листинг 59.9. Трюк с зацикленным каналом
#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);
}