— может быть установлен в требуемое значение, которое включается в
siginfo_t
, доставляемое вместе с сигналом. Чтобы сгенерировать сигнал с
union sigval
, должна использоваться функция
sigqueue
.
#include <signal.h>
void *sigqueue(pid_t pid, int signum, const union sigval value);
В
отличие от
kill
,
pid
должен быть корректным идентификатором процесса (отрицательные значения не допускаются),
signum
указывает номер посылаемого сигнала. Подобно
kill
,
sigqueue
допускает нулевое значение
signum
нулю, чтобы проверить, позволяет ли вызывающий процесс посылать целевому сигналы, в действительности не выполняя такой посылки. Последний параметр,
value
, представляет собой элемент данных, передаваемый вместе с сигналом.
Чтобы принять
union sigval
, процесс, перехватывающий сигнал, должен использовать
SA_SIGINFO
при регистрации обработчика сигналов с помощью
sigaction
. Когда член
si_code
структуры
siginfo_t
равен
SI_QUEUE
, то
siginfo_t
представляет член
si_value
, который содержит значение
value
, переданное
sigqueue
.
Ниже приведен пример отправки элемента данных с сигналом. Он устанавливает в очередь три сигнала
SIGRTMIN
с разными элементами данных. Он демонстрирует, что сигналы доставляются в том же порядке, что были отправлены, как мы и ожидаем при работе с сигналами реального времени [73] . Более сложный пример, использующий сигналы для отслеживания изменений в каталогах, можно найти в главе 14.
73
Дополнительные примеры обработки сигналов вы можете найти в программах для аренды файлов (глава 13), управления терминалом (глава 16) и работы с интервальными таймерами (глава 18).
1: /* sigval.с */
2:
3: #include <sys/signal.h>
4: #include <stdlib.h>
5: #include <stdio.h>
6: #include <string.h>
7: #include <unistd.h>
8:
9: /* Захватить сигнал и зарегистрировать факт его обработки */
21: /* Отправить сигналы handler и сохранять все сигналы заблокированными,
22: чтобы handler был сконфигурирован для перехвата с исключением
23: состязаний при манипулировании глобальными переменными */
24: act.sa_sigaction = handler;
25: act.sa_mask = mask;
26: act.sa_flags = SA_SIGINFO;
27:
28: sigaction(SIGRTMIN, &act, NULL);
29:
30: /*
Блокировать SIGRTMIN, чтобы можно было увидеть очередь и упорядочение*/
31: sigemptyset(&mask);
32: sigaddset(&mask, SIGRTMIN);
33:
34: sigprocmask(SIG_BLOCK, &mask, &oldMask);
35:
36: /* Сгенерировать сигналы */
37: val.sival_int = 1;
38: sigqueue(me, SIGRTMIN, val);
39: val.sival_int++;
40: sigqueue(me, SIGRTMIN, val);
41: val.sival_int++;
42: sigqueue(me, SIGRTMIN, val);
43:
44: /* Разрешить доставку сигналов */
45: sigprocmask(SIG_SETMASK, &oldMask, NULL);
46:
47: return 0;
48: }
Глава 13
Расширенная обработка файлов
В Linux файлы применяются при решении большого количества задач, среди которых, например, хранение долговременных данных, организация сетей с помощью сокетов и доступ к устройствам посредством файлов устройств. Разнообразие приложений, работающих с файлами, привело к созданию множества специальных способов управления файлами. В главе 11 рассматривались наиболее распространенные действия с файлами; в настоящей же главе исследуются специализированные файловые операции. В частности, мы рассмотрим следующие вопросы: использование одновременно нескольких файлов, отображение файлов на системную память, блокировка файлов, чтение и запись вразброс.
13.1. Мультиплексирование входных и выходных данных
Многим клиент-серверным приложениям необходимо считывать входные данные или записывать выходные данные с помощью одновременно нескольких файловых дескрипторов. Например, современные Web-браузеры открывают одновременно несколько сетевых подключений, чтобы уменьшить время загрузки Web-страницы. Это позволяет им загружать множество изображений, имеющихся на большинстве Web-страниц, быстрее, чем с помощью последовательных подключений. Кроме канала межпроцессных взаимодействий (IPC), используемого графическими браузерами для связи с X-сервером, на котором они отображаются, браузеры работают с множеством файловых дескрипторов.
Браузеру легче всего обработать эти файлы, считывая и обрабатывая данные из них (системный вызов
read
в сетевом подключении, так же, как и в канале, возвращает доступные в настоящий момент данные и блокирует их только в случае неготовности). Этот подход эффективен, пока все подключения доставляют данные достаточно регулярно.
Если одно из сетевых подключений является медленным, начинают возникать проблемы. Когда браузер снова считывает из этого файла, он перестает работать, в то время как
read
блокируется в ожидании поступления данных. Не стоит и упоминать, что подобное поведение не является удобоваримым для пользователя браузера.
Для иллюстрации этих проблем рассмотрим короткую программу, считывающую из двух файлов,
p1
и
p2
. Для ее испытания откройте три сеанса работы с X-терминалом (или воспользуйтесь тремя виртуальными консолями). Создайте каналы под именами
p1
и
p2
(с помощью команды
mknod
), затем запустите
cat > p1
и
cat > p2
в двух терминалах, одновременно запустив
mpx-blocks
в третьем. После этого набирайте любой текст в каждом окно
cat
и смотрите, как он появляется. Помните, что две команды
cat
не будут записывать данные в каналы до конца строки.