Программа-пример
В данном пункте приводится программа-пример, управляемая посредством меню. Она позволяет поупражняться со всевозможными комбинациями в использовании системного вызова msgget(2), проследить, как передаются аргументы и получаются результаты.
Имена переменных выбраны максимально близкими к именам, используемым в спецификации синтаксиса системного вызова, что облегчает чтение программы.
Выполнение программы начинается с приглашения ввести шестнадцатеричный ключ key, восьмеричный код прав на операции и, наконец, выбираемую при помощи меню комбинацию флагов (строки 14-30). В меню предлагаются все возможные комбинации, даже бессмысленные, что позволяет при желании проследить за реакцией на ошибку.
Затем выбранные флаги комбинируются с правами на операции, после чего выполняется системный вызов, результат которого помещается в переменную msqid (строка 49). Контроль успешного завершения системного вызова производится в строке 50. Если значение msqid равно -1, выдается сообщение об ошибке и выводится значение внешней переменной errno (строки 52, 53). Если ошибки не произошло, выводится значение полученного идентификатора очереди сообщений (строка 57).
Далее приводится текст программы-примера. Здесь и далее номера строк служат только для ссылок и не являются частью программы.
1 /* Программа иллюстрирует 2 возможности системного вызова msgget() 3 (получение идентификатора очереди сообщений) */
4 #include <stdio.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/sem.h> 8 #include <errno.h>
9 main () 10 { 11 key_t key; /* Тип описан как целое */ 12 int opperm, flags; /* Права на операции и флаги */ 13 int msgflg, msqid;
14 /* Ввести требуемый ключ */ 15 printf ("\nВведите шестнадцатеричный ключ: "); 16 scanf ("%x", &key);
17 /* Ввести права на операции */ 18 printf ("\nВведите права на операции "); 19 printf ("в восьмеричной записи: "); 20 scanf ("%o", &opperm);
21 /* Установить требуемые флаги */ 22 printf ("\nВведите код, соответствущий "); 23 printf ("нужной комбинации флагов:\n"); 24 printf (" Нет флагов = 0\n"); 25 printf (" IPC_CREAT = 1\n"); 26 printf (" IPC_EXCL = 2\n"); 27 printf (" IPC_CREAT и IPC_EXCL = 3\n"); 28 printf (" Выбор = ");
Назначение переменных, описанных в программе:
command | |
Код управляющего действия. | |
choice | |
Используется для выбора поля ассоциированной структуры данных, которое нужно изменить. | |
msqid_ds | |
Структура для хранения информации об очереди. |
Программа ведет себя следующим образом. Прежде всего предлагается ввести допустимый идентификатор очереди сообщений, который заносится в переменную msqid (строки 15, 16). Это значение требуется в каждом системном вызове msgct(2).
Затем нужно ввести код выбранного управляющего действия (строки 17-22); он заносится в переменную command.
Если выбрано действие IPC_STAT (код 1), системный вызов выполняется (строка 31) и распечатывается информация о состоянии очереди (строки 32-39); в программе распечатываются только те поля структуры, которые могут быть переустановлены. Отметим, что если системный вызов завершается неудачей, распечатывается информация о состоянии очереди на момент последнего успешного выполнения системного вызова. Кроме того, выводится сообщение об ошибке и распечатывается значение переменной errno (строки 90, 91). Если системный вызов завершается успешно, выводится сообщение, уведомляющее об этом, и значение использованного идентификатора очереди сообщений (строки 95, 96).
Если выбрано действие IPC_SET (код 2), программа прежде всего получает информацию о текущем состоянии очереди сообщений с заданным идентификатором (строка 45). Это необходимо, поскольку пример обеспечивает изменение только одного поля за один раз, в то время ведет себя следующим образом. Прежде всего предлагается ввести допустимый идентификатор очереди сообщений, который заносится в переменную msqid (строки 15, 16). Это значение требуется в каждом системном вызове msgct(2).
Если выбрано действие IPC_RMID (код 3), выполняется системный вызов (строка 86), удаляющий из системы идентификатор msqid, очередь сообщений и ассоциированную с ней структуру данных. Отметим, что для выполнения этого управляющего действия аргумент buf не требуется, поэтому его значение может быть заменено нулем (NULL).
Назначение переменных, описанных в программе:
sndbuf | |
Используется в качестве буфера, содержащего посылаемое сообщение (строка 12); шаблон при описании этой переменной - структура данных msgbufl (строки 9-12). Структура msgbufl является почти точной копией структуры msgbuf, описание которой находится во включаемом файле <sys/msg.h>. Единственное различие состоит в том, что длина символьного массива в msgbufl равна максимально допустимому размеру сообщения для данной конфигурации (MSGMAX), в то время как в msgbuf она устанавливается равной единице, чтобы отвечать требованиям компилятора. По этой причине нельзя использовать в пользовательской программе описание msgbuf непосредственно, то есть пользователь должен сам определять поля данной структуры. | |
rcvbuf | |
Используется в качестве буфера, содержащего принимаемое сообщение (строка 12); шаблон при описании этой переменной - структура данных msgbufl (строки 9-12). | |
i | |
Используется как счетчик символов при вводе с клавиатуры и занесении в массив, а также отслеживает длину сообщения при выполнении системного вызова msgsnd(); кроме того, используется как счетчик при выводе принятого сообщения после выполнения системного вызова msgrcv(). | |
c | |
Содержит символ, возвращаемый функцией getchar() (строка 45). | |
flag | |
При выполнении системного вызова msgsnd() содержит значение, определяющее, нужен ли флаг IPC_NOWAIT
(строка 55). | |
flags | |
При выполнении системного вызова msgrcv() содержит значение, определяющее комбинацию флагов IPC_NOWAIT и MSG_NOERROR (строка 103). | |
choice | |
Содержит признак, определяющий выбранную операцию - посылка или прием сообщения (строка 27). |
Отметим, что в программе структура данных msqid_ds снабжается указателем на нее (строка 19), указатель соответствующим образом инициализируется (строка 20); это позволяет следить за полями ассоциированной структуры данных, которые могут измениться в результате операций над сообщениями. При помощи системного вызова msgctl() (действие IPC_STAT) программа получает значения полей ассоциированной структуры данных и выводит их (строки 74-82 и 145-152).
/* Программа иллюстрирует возможности системного вызова semctl() (управление семафорами) */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
#define MAXSETSIZE 25
main () { extern int errno; struct semid_ds semid_ds; int length, rtrn, i, c; int semid, semnum, cmd, choice; union semun { int val; struct semid_ds *buf; ushort array [MAXSETSIZE]; } arg;
/* Инициализация указателя на структуру данных */ arg.buf = &semid_ds;
/* Ввести идентификатор множества семафоров */ printf ("Введите ид-р множества семафоров: "); scanf ("%d", &semid);
/* Выбрать требуемое управляющее действие */ printf ("\nВведите номер требуемого действия:\n"); printf (" GETVAL = 1\n"); printf (" SETVAL = 2\n"); printf (" GETPID = 3\n"); printf (" GETNCNT = 4\n"); printf (" GETZCNT = 5\n"); printf (" GETALL = 6\n"); printf (" SETALL = 7\n"); printf (" IPC_STAT = 8\n"); printf (" IPC_SET = 9\n"); printf (" IPC_RMID = 10\n"); printf (" Выбор = "); scanf ("%d", &cmd);
/* Проверить значения */ printf ("идентификатор = %d, команда = %d\n", semid, cmd);
/* Сформировать аргументы и выполнить вызов */ switch (cmd) { case 1: /* Получить значение */ printf ("\nВведите номер семафора: "); scanf ("%d", &semnum); /* Выполнить системный вызов */ rtrn = semctl (semid, semnum, GETVAL, 0); printf ("\nЗначение семафора = %d\n", rtrn); break;
case 2: /* Установить значение */ printf ("\nВведите номер семафора: "); scanf ("%d", &semnum); printf ("\nВведите значение: "); scanf ("%d", &arg.val); /* Выполнить системный вызов */ rtrn = semctl (semid, semnum, SETVAL, arg.val); break;
case 3: /* Получить ид-р процесса */ rtrn = semctl (semid, 0, GETPID, 0); printf ("\nПоследнюю операцию выполнил: %d\n",rtrn); break;
/* Программа иллюстрирует возможности системного вызова semop() (операции над множеством семафоров) */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
#define MAXOPSIZE 10
main () { extern int errno; struct sembuf sops [MAXOPSIZE]; int semid, flags, i, rtrn; unsigned nsops;
/* Ввести идентификатор множества семафоров */ printf ("\nВведите идентификатор множества семафоров,"); printf ("\nнад которым будут выполняться операции: "); scanf ("%d", &semid); printf ("\nИд-р множества семафоров = %d", semid);
/* Ввести число операций */ printf ("\nВведите число операций "); printf ("над семафорами из этого множества: \n"); scanf ("%d", &nsops); printf ("\nЧисло операций = %d", nsops);
/* Инициализировать массив операций */ for (i = 0; i < nsops; i++) { /* Выбрать семафор из множества */ printf ("\nВведите номер семафора: "); scanf ("%d", &sops [i].sem_num); printf ("\nНомер = %d", sops [i].sem_num);
/* Ввести число, задающее операцию */ printf ("\nЗадайте операцию над семафором: "); scanf ("%d", &sops [i].sem_op); printf ("\nОперация = %d", sops [i].sem_op);
/* Указать требуемые флаги */ printf ("\nВведите код, "); printf ("соответствующий требуемым флагам:\n"); printf (" Нет флагов = 0\n"); printf (" IPC_NOWAIT = 1\n"); printf (" SEM_UNDO = 2\n"); printf (" IPC_NOWAIT и SEM_UNDO = 3\n"); printf (" Выбор = "); scanf ("%d", &flags);
switch (flags) { case 0: sops [i].sem_flg = 0; break; case 1: sops [i].sem_flg = IPC_NOWAIT; break; case 2: sops [i].sem_flg = SEM_UNDO; break; case 3: sops [i].sem_flg = IPC_NOWAIT | SEM_UNDO; break; } printf ("\nФлаги = 0%o", sops [i].sem_flg); }
/* Распечатать все структуры массива */ printf ("\nМассив операций:\n"); for (i = 0; i < nsops; i++) { printf (" Номер семафора = %d\n", sops [i].sem_num); printf (" Операция = %d\n", sops [i].sem_op); printf (" Флаги = 0%o\n", sops [i].sem_flg); }
/* Выполнить системный вызов */ rtrn = semop (semid, sops, nsops); if (rtrn == -1) { printf ("\nsemop завершился неудачей!\n"); printf ("Код ошибки = %d\n", errno); } else { printf ("\nsemop завершился успешно.\n"); printf ("Идентификатор semid = %d\n", semid); printf ("Возвращенное значение = %d\n", rtrn); }
exit (0); }
/* Программа иллюстрирует возможности системного вызова shmctl() (операции управления разделяемыми сегментами) */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>
main () { extern int errno; int rtrn, shmid, command, choice; struct shmid_ds shmid_ds, *buf; buf = &shmid_ds;
/* Ввести идентификатор сегмента и действие */ printf ("Введите идентификатор shmid: "); scanf ("%d", &shmid);
printf ("Введите номер требуемого действия:\n"); printf (" IPC_STAT = 1\n"); printf (" IPC_SET = 2\n"); printf (" IPC_RMID = 3\n"); printf (" SHM_LOCK = 4\n"); printf (" SHM_UNLOCK = 5\n"); printf (" Выбор = "); scanf ("%d", &command);
/* Проверить значения */ printf ("\nидентификатор = %d, действие = %d\n", shmid, command);
switch (command) { case 1: /* Скопировать информацию о состоянии разделяемого сегмента в пользовательскую структуру и вывести ее */
rtrn = shmctl (shmid, IPC_STAT, buf); printf ("\nИд-р пользователя = %d\n", buf->shm_perm.uid); printf ("Ид-р группы пользователя = %d\n", buf->shm_perm.gid); printf ("Ид-р создателя = %d\n", buf->shm_perm.cuid); printf ("Ид-р группы создателя = %d\n", buf->shm_perm.cgid); printf ("Права на операции = 0%o\n", buf->shm_perm.mode); printf ("Последовательность номеров "); printf ("используемых слотов = 0%x\n", buf->shm_perm.seq); printf ("Ключ = 0%x\n", buf->shm_perm.key); printf ("Размер сегмента = %d\n", buf->shm_segsz); printf ("Выполнил последнюю операцию = %d\n", buf->shm_lpid); printf ("Создал сегмент = %d\n", buf->shm_cpid); printf ("Число присоединивших сегмент = %d\n", buf->shm_nattch); printf ("Число удерживаюших в памяти = %d\n", buf->shm_cnattch); printf ("Последнее присоединение = %d\n", buf->shm_atime); printf ("Последнее отсоединение = %d\n", buf->shm_dtime); printf ("Последнее изменение = %d\n", buf->shm_ctime); break;
/* Программа иллюстрирует возможности системных вызовов shmat() и shmdt() (операции над разделяемыми сегментами памяти) */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>
main () { extern int errno; int shmid, shmaddr, shmflg; int flags, attach, detach, rtrn, i;
/* Цикл присоединений для данного процесса */ printf ("\nВведите число присоединений "); printf ("для процесса (1-4): "); scanf ("%d", &attach); printf ("\nЧисло присоединений = %d\n", attach);
for (i = 0; i < attach; i++) { /* Ввести идентификатор разделяемого сегмента */ printf ("\nВведите ид-р разделяемого сегмента,\n"); printf ("над которым нужно выполнить операции: "); scanf ("%d", &shmid); printf ("\nИд-р сегмента = %d\n", shmid);
/* Ввести адрес присоединения */ printf ("\nВведите адрес присоединения "); printf ("в шестнадцатеричной записи: "); scanf ("%x", &shmaddr); printf ("\nАдрес присоединения = 0x%x\n", shmaddr);
/* Выбрать требуемые флаги */ printf ("\nВведите номер нужной комбинации флагов:\n"); printf (" SHM_RND = 1\n"); printf (" SHM_RDONLY = 2\n"); printf (" SHM_RND и SHM_RDONLY = 3\n"); printf (" Выбор = "); scanf ("%d", &flags);
switch (flags) { case 1: shmflg = SHM_RND; break; case 2: shmflg = SHM_RDONLY; break; case 3: shmflg = SHM_RND | SHM_RDONLY; break; } printf ("\nФлаги = 0%o", shmflg);
/* Выполнить системный вызов shmat */ rtrn = shmat (shmid, shmaddr, shmflg); if (rtrn == -1) { printf ("\nshmat завершился неудачей!\n"); printf ("\Код ошибки = %d\n", errno); } else { printf ("\nshmat завершился успешно.\n"); printf ("Идентификатор shmid = %d\n", shmid); printf ("Адрес = 0x%x\n", rtrn); } }
/* Цикл отсоединений для данного процесса */ printf ("\nВведите число отсоединений "); printf ("для процесса (1-4): "); scanf ("%d", &detach); printf ("\nЧисло отсоединений = %d\n", detach);
for (i = 0; i < detach; i++) { /* Ввести адрес отсоединения */ printf ("\nВведите адрес отсоединяемого сегмента "); printf ("в шестнадцатеричной записи: "); scanf ("%x", &shmaddr); printf ("\nАдрес отсоединения = 0x%x\n", shmaddr);
/* Выполнить системный вызов shmdt */ rtrn = shmdt (shmaddr); if (rtrn == -1) { printf ("\nshmdt завершился неудачей!\n"); printf ("\Код ошибки = %d\n", errno); } else { printf ("\nshmdt завершился успешно,\n"); printf ("идентификатор shmid = %d\n", shmid); } }
exit (0); }