next up previous contents index
Next: Семафоры Up: Операции над очередями сообщений Previous: Прием сообщений   Contents   Index

Программа-пример

Назначение переменных, описанных в программе:

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).

Прежде всего программа запрашивает, какую операцию нужно выполнить - послать или принять сообщение. Должно быть введено число, соответствующее требуемой операции; это число заносится в переменную choice (строки 21-27).

Если выбрана операция посылки сообщения, указатель msgp инициализируется адресом структуры данных sndbuf (строка 30). После этого запрашивается идентификатор очереди сообщений, в которую должно быть послано сообщение; идентификатор заносится в переменную msqid (строки 31-34). Затем должен быть введен тип сообщения; он заносится в поле mtype структуры данных, указываемой значением msgp (строка 34).

После этого программа приглашает ввести с клавиатуры текст посылаемого сообщения и выполняет цикл, в котором символы читаются и заносятся в массив mtext структуры данных (строки 43-46). Ввод продолжается до тех пор, пока не будет обнаружен признак конца файла; для функции getchar() таким признаком является символ CTRL+D, непосредственно следующий за символом возврата каретки. После того как признак конца обнаружен, определяется размер сообщения (строки 47, 48) - он на единицу больше значения счетчика i, поскольку элементы массива, в который заносится сообщение, нумеруются с нуля. Следует помнить, что сообщение будет содержать заключительные символы и, следовательно, будет казаться, что сообщение на три символа короче, чем указывает аргумент msgsz.

Чтобы обеспечить пользователю обратную связь, текст сообщения, содержащийся в массиве mtext структуры sndbuf, немедленно выводится (строки 49-51).

Следующее, и последнее, действие заключается в определении, должен ли быть установлен флаг IPC_NOWAIT. Чтобы выяснить это, программа предлагает ввести 1, если флаг установить нужно, или любое другое число, если не нужно (строки 52-59). Введенное значение заносится в переменную flag. Если введена единица, аргумент msgflg полагается равным IPC_NOWAIT, в противном случае msgflg устанавливается равным нулю.

После этого выполняется системный вызов msgsnd() (строка 63). Если вызов завершается неудачей, выводится сообщение об ошибке, а также ее код (строки 64-66). Если вызов завершается успешно, печатается возвращенное им значение, которое должно быть равно нулю (строки 69-71).

При каждой успешной посылке сообщения обновляются три поля ассоциированной структуры данных. Изменения можно описать следующим образом:

msg_qnum

Определяет общее число сообщений в очереди; в результате выполнения операции увеличивается на единицу.

msg_lspid

Содержит идентификатор процесса, который последним послал сообщение; полю присваивается соответствующий идентификатор.

msg_stime

Содержит время последней посылки сообщения, время измеряется в секундах, начиная с 00:00:00 1 января 1970 года (по Гринвичу).

После каждой успешной операции посылки сообщения значения этих полей выводятся (строки 76-82)

Если указано, что требуется принять сообщение, начальное значение указателя msgp устанавливается равным адресу структуры данных rcvbuf (строка 87).

Запрашивается код требуемой комбинации флагов, который заносится в переменную flags (строки 94-103). Переменная msgflg устанавливается в сответствии с выбранной комбинацией (строки 104- 119).

В заключение запрашивается, сколько байт нужно принять; указанное значение заносится в переменную msgsz (строки 120-123).

После этого выполняется системный вызов msgrcv() (строка 130) Если вызов завершается неудачей, выводится сообщение об ошибке, а также ее код (строки 131-133). Если вызов завершается успешно, программа сообщает об этом, а также выводит размер и текст сообщения (строки 135-144).

При каждом успешном приеме сообщения обновляются три поля ассоциированной структуры данных. Изменения можно описать следующим образом:

msg_qnum

Определяет общее число сообщений в очереди; в результате выполнения операции уменьшается на единицу.

msg_lrpid

Содержит идентификатор процесса, который последним получил сообщение; полю присваивается соответствующий идентификатор.

msg_rtime

Содержит время последнего получения сообщения, время измеряется в секундах, начиная с 00:00:00 1 января 1970 года (по Гринвичу).

Далее следует текст программы. 1 /* Программа иллюстрирует 2 возможности системных вызовов msgsnd() и msgrcv() 3 (операции над очередями сообщений) */ 4 #include <stdio.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/msg.h> 8 #define MAXTEXTSIZE 8192 9 struct msgbufl { 10 long mtype; 11 char mtext [MAXTEXTSIZE]; 12 } sndbuf, rcvbuf, *msgp; 13 main () 14 { 15 extern int errno; 16 int flag, flags, choice, rtrn, i, c; 17 int rtrn, msqid, msgsz, msgflg; 18 long msgtyp; 19 struct msqid_ds msqid_ds, *buf; 20 buf = &msqid_ds; 21 /* Выбрать требуемую операцию */ 22 printf ("\nВведите код, соответствующий "); 23 printf ("посылке или приему сообщения:\n"); 24 printf (" Послать = 1\n"); 25 printf (" Принять = 2\n"); 26 printf (" Выбор = "); 27 scanf ("%d", &choice); 28 if (choice == 1) { 29 /* Послать сообщение */ 30 msgp = &sndbuf; /* Указатель на структуру */ 31 printf ("\nВведите идентификатор "); 32 printf ("очереди сообщений,\n"); 33 printf ("в которую посылается сообщение: "); 34 scanf ("%d", &msqid); 35 /* Установить тип сообщения */ 36 printf ("\nВведите положительное число - "); 37 printf ("тип сообщения: "); 38 scanf ("%d", &msgp->mtype); 39 /* Ввести посылаемое сообщение */ 40 printf ("\nВведите сообщение: \n"); 41 /* Управляющая последовательность CTRL+D 42 завершает ввод сообщения */ 43 /* Прочитать символы сообщения 44 и поместить их в массив mtext */ 45 for (i = 0; ((c = getchar ()) != EOF); i++) 46 sndbuf.mtext [i] = c; 47 /* Определить размер сообщения */ 48 msgsz = i + 1; 49 /* Выдать текст посылаемого сообщения */ 50 for (i = 0; i < msgsz; i++) 51 putchar (sndbuf.mtext [i]); 52 /* Установить флаг IPC_NOWAIT, если это нужно */ 53 printf ("\nВведите 1, если хотите установить "); 54 printf ("флаг IPC_NOWAIT: "); 55 scanf ("%d", &flag); 56 if (flag == 1) 57 msgflg = IPC_NOWAIT; 58 else 59 msgflg = 0; 60 /* Проверить флаг */ 61 printf ("\nФлаг = 0%o\n", msgflg); 62 /* Послать сообщение */ 63 rtrn = msgsnd (msqid, msgp, msgsz, msgflg); 64 if (rtrn == -1) { 65 printf ("\nmsgsnd завершился неудачей!\n"); 66 printf ("Код ошибки = %d\n", errno); 67 } 68 else { 69 /* Вывести результат; при успешном 70 завершении он должен равняться нулю */ 71 printf ("\nРезультат = %d\n", rtrn); 72 /* Вывести размер сообщения */ 73 printf ("\nРазмер сообщения = %d\n", msgsz); 74 /* Опрос измененной структуры данных */ 75 msgctl (msqid, IPC_STAT, buf); 76 /* Вывести изменившиеся поля */ 77 printf ("Число сообщений в очереди = %d\n", 78 buf->msg_qnum); 79 printf ("Ид-р последнего отправителя = %d\n", 80 buf->msg_lspid); 81 printf ("Время последнего отправления = %d\n", 82 buf->msg_stime); 83 } 84 } 85 if (choice == 2) { 86 /* Принять сообщение */ 87 msgp = &rcvbuf; 88 /* Определить нужную очередь сообщений */ 89 printf ("\nВведите ид-р очереди сообщений: "); 90 scanf ("%d", &msqid); 91 /* Определить тип сообщения */ 92 printf ("\nВведите тип сообщения: "); 93 scanf ("%d", &msgtyp); 94 /* Сформировать управляющие флаги 95 для требуемых действий */ 96 printf ("\nВведите код, соответствущий "); 97 printf ("нужной комбинации флагов:\n"); 98 printf (" Нет флагов = 0\n"); 99 printf (" MSG_NOERROR = 1\n"); 100 printf (" IPC_NOWAIT = 2\n"); 101 printf (" MSG_NOERROR и IPC_NOWAIT = 3\n"); 102 printf (" Выбор = "); 103 scanf ("%d", &flags); 104 switch (flags) { 105 /* Установить msgflg как побитное ИЛИ 106 соответствующих констант */ 107 case 0: 108 msgflg = 0; 109 break; 110 case 1: 111 msgflg = MSG_NOERROR; 112 break; 113 case 2: 114 msgflg = IPC_NOWAIT; 115 break; 116 case 3: 117 msgflg = MSG_NOERROR | IPC_NOWAIT; 118 break; 119 } 120 /* Определить, какое число байт принять */ 121 printf ("\nВведите число байт, которое "); 122 printf ("нужно принять (msgsz): "); 123 scanf ("%d", &msgsz); 124 /* Проверить значение аргументов */ 125 printf ("\nИдентификатор msqid = %d\n", msqid); 126 printf ("Тип сообщения = %d\n", msgtyp); 127 printf ("Число байт = %d\n", msgsz); 128 printf ("Флаги = %o\n", msgflg); 129 /* Вызвать msgrcv для приема сообщения */ 130 rtrn = msgrcv (msqid, msgp, msgsz, msgtyp, msgflg); 131 if (rtrn == -1) { 132 printf ("\nmsgrcv завершился неудачей!\n"); 133 printf ("Код oшибки = %d\n", errno); 134 } 135 else { 136 printf ("\nmsgrcv завершился успешно,\n"); 137 printf ("идентификатор очереди = %d\n", msqid); 138 /* Напечатать число принятых байт, 139 оно равно возвращаемому значению */ 140 printf ("Принято байт: %d\n", rtrn); 141 /* Распечатать принятое сообщение */ 142 for (i = 0; i < rtrn; i++) 143 putchar (rcvbuf.mtext [i]); 144 } 145 /* Опрос ассоциированной структуры данных */ 146 msgctl (msqid, IPC_STAT, buf); 147 printf ("\nЧисло сообщений в очереди = %d\n", 148 buf->msg_qnum); 149 printf ("Ид-р последнего получателя = %d\n", 150 buf->msg_lrpid); 151 printf ("Время последнего получения = %d\n", 152 buf->msg_rtime); 153 } 154 exit (0); 155 }



Alex Otwagin 2002-12-16