#include <signal.h>
int raise (int signum);
Процессу посылается сигнал, определённый параметром signum, и в случае успеха функция raise возвращает нулевое значение.
Чтобы приостановить выполнение программы до тех пор, пока не придёт хотя бы один сигнал, используется вызов pause:
#include <signal.h>
int pause (void);
Вызов pause приостанавливает выполнение вызывающего процесса до получения любого сигнала. Если сигнал вызывает нормальное завершение процесса или игнорируется процессом, то в результате вызова pause будет просто выполнено соответствующее действие (завершение работы или игнорирование сигнала). Если же сигнал перехватывается, то после завершения соответствующего обработчика прерывания вызов pause вернёт значение –1 и поместит в переменную errno значение EINTR.
Пример программы, обрабатывающей сигналы, приведён в листинге 16.
Листинг 16. Обработка сигналов
1.#include <stdio.h>
2.#include <signal.h>
3.
4./* Функция-обработчик сигнала */
5.void sighandler (int signo){
6.printf (“Получен сигнал !!! Ура !!!\n”);
7.}
8./* Основная функция программы */
9.int main (void){
10.int x = 0;
11.
12./* Регистрируем обработчик сигнала */
13.signal (SGUSR1, sghandler);
14.do {
15.printf (“Введите X = ”); scanf (“%d”, &x);
16.if (x & 0x0A) raise (SIGUSR1);
17.} while (x != 99);
6.3.4. Сообщения
Данная возможность позволяет процессам обмениваться структурированными данными, имеющими следующие атрибуты:
96
тип сообщения (позволяет мультиплексировать сообщения в одной очереди);
длина данных сообщения в байтах (может быть нулевой);
собственно данные (если длина ненулевая, могут быть структурированными).
Очередь сообщений хранится в виде внутреннего однонаправленного связанного списка в адресном пространстве ядра. Для каждой очереди ядро создаёт заголовок очереди (msgid_ds), где содержится информация о правах доступа к очереди (msg_perm), её текущем состоянии (msg_cbytes – число байтов и msg_qnum – число сообщений в очереди), а также указатели на первое (msg_first) и последнее (msg_last) сообщения, хранящиеся в виде связного списка. Каждый элемент этого списка является отдельным сообщением. Для создания новой очереди сообщений или для доступа к существующей используется системный вызов msgget:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
int msgget (key_t key, int msgflag);
Функция возвращает дескриптор объекта-очереди или –1 в случае ошибки. Подобно файловому дескриптору, этот идентификатор используется процессом для работы с очередью сообщений. В частности, процесс может:
помещать в очередь сообщения с помощью функции msgsnd;
получать сообщения определённого типа из очереди с помощью функции msgrcv;
управлять сообщениями с помощью функции msgctl.
Перечисленные системные вызовы манипулирования сообщениями имеют следующий вид:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
int msgsnd (int msgid, const void *msgp, size_t msgsz, int msgfig);
int msgrcv (int msgid, void *msgp, size t msgsz, long msgtyp, int msgfig);
int msgctl (int msgid, int cmd, struct msgid_ds * buf);
Здесь msgid является дескриптором объекта, полученного в результате вызова msgget. Параметр msgp указывает на буфер, содержащий тип сообщения и его данные, размер которого равен msgsz байт. Буфер имеет следующие поля:
97
long msgtype; |
/*тип сообщения*/ |
char msgtext []; /*данные сообщения*/
Параметр msgtyp указывает на тип сообщения и используется для их выборочного получения. Если msgtyp равен 0, функция msgrcv получит первое сообщение из очереди. Если величина msgtyp больше 0, будет получено первое сообщение указанного типа. Если msgtyp меньше 0, функция msgrcv получит сообщение с минимальным значением типа, меньшим или равным абсолютному значению msgtyp.
Очереди сообщений обладают весьма полезным свойством – в одной очереди можно мультиплексировать сообщения от различных процессов. Для демультиплексирования используется атрибут msgtypе, на основании которого любой процесс может фильтровать сообщения с помощью функции msgrcv, как это было описано выше.
Функция msgctl выполняет контрольную операцию, заданную в cmd, над очередью сообщений msgid. Возможные значения cmd:
IPC_STAT Скопировать информацию из структуры данных очереди сообщений в структуру с адресом buf (причём, у пользователя должны быть права на чтение очереди сообщений).
IPC_SET Записать значения некоторых элементов структуры msgid_ds, адрес которой указан в buf, в структуру данных из очереди сообщений, обновляя при этом её поле msg_ctime. Предполагаемые элементы заданной пользователем структуры msgid_ds, адрес которой указан в buf, являются следующими: msg_perm.uid, msg_perm.gid, msg_perm.mode, msg_qbytes. Эффективный иденти-
фикатор пользователя процесса, вызывающего эту функцию, должен принадлежать либо root, либо создателю или владельцу очереди сообщений. Только суперпользователь может устанавливать значение msg_qbytes большим, чем MSGMNB.
IPC_RMID Немедленно удалить очередь сообщений и структуры её данных, «разбудив» все процессы, ожидающие записи или чтения этой очереди (при этом функция возвращает ошибку, а переменная errno приобретает значение EIDRM). Эффективный идентификатор пользователя процесса, вызывающего эту функцию, должен принадлежать либо root, либо создателю или владельцу очереди сообщений.
Рассмотрим типичную ситуацию взаимодействия процессов, когда серверный процесс обменивается данными с несколькими клиентами. Свойство мультиплексирования позволяет использовать для такого обмена одну очередь сообщений. Для этого сообщениям, направляемым от любого из клиентов серверу, будем присваивать значение типа, скажем, равным 1. Если в теле сообщения клиент каким-либо образом идентифицирует себя (например,
98
передаёт свой PID), то сервер сможет передать сообщение конкретному клиенту, устанавливая тип сообщения, равным этому идентификатору.
Поскольку функция msgrcv позволяет принимать сообщения определённого типа (типов), сервер будет принимать сообщения с типом 1, а клиенты – сообщения с типами, равными идентификаторам их процессов. Схема такого взаимодействия представлена на рисунке 8.
Атрибут msgtype также можно использовать для изменения порядка извлечения сообщений из очереди. Стандартный порядок получения сообщений аналогичен принципу FIFO – сообщения получаются в порядке их записи. Однако используя тип, например, для назначения приоритета сообщений, этот порядок легко изменить.
Рисунок 8. Пример работы очереди сообщений
Пример программы «Здравствуй, Мир!», использующей сообщения, приведён в листингах 17–19.
Листинг 17. Файл заголовков mesg.h
1. |
#define |
MAXBUFF |
80 |
2. |
#define |
PERM |
0666 |
3./*Oпределим структуру нашего сообщения. */
4.typedef struct our_msgbuf {
5.long mtype;
6.char buff[MAXBUFF] ;
7.} Message;
99
Листинг 18. Программа, реализующая сервер
1.#include <sys/types.h>
2.#include <sys/ipc.h>
3.#include <sys/msg.h>
4.#include <stdlib.h>
5.#include <stdio.h>
6.#include “mesg.h”
7.int main (void){
8.Message message; /* Буфер для передачи сообщения */
9.key_t key;
10. |
int |
msgid, length, n; |
11. |
|
|
12.if ((key = ftok(“server”, ‘A’)) < 0){
13.printf(“Невозможно получить ключ\n”); exit(1);
14.}
15.message.mtype=1L; /*Тип принимаемых сообщений*/
16./*Создадим очередь сообщений*/
17.if ((msgid = msgget(key, PERM | IPC_CREAT)) < 0){
18.printf(“Невозможно создать очередь\n”); exit(1);
19.}
20.n = msgrcv(msgid, &message, sizeof(message),
21. message.mtype, 0);
22.if (n > 0){
23.if (write(1, message.buff, n) != n){
24.printf(“Ошибка вывода\n”); exit(1);
25.}
26.}
27.else{
28.printf(“Ошибка чтения сообщения\n”); exit(1);
29.}
30./*Удалить очередь поручим клиенту*/
31.exit(0);
32.}
Листинг 19. Программа, реализующая клиент
1.#include <sys/types.h>
2.#include <sys/ipc.h>
3.#include <sys/msg.h>
4.#include <stdlib.h>
5.#include <stdio.h>
6.#include “mesg.h”
7. |
int main(void){ |
|
8. |
||
9. |
Message |
message; |
10. |
key_t |
key; |
100