The prototypes of msgsnd() and msgrcv() are as follows (see also the reference page, which can be accessed as msgsnd(2), msgrcv(2), or as msgops(2)):
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
Upon successful completion, a zero value is returned; when unsuccessful, msgsnd() returns a -1.
The msqid argument must be a nonnegative integer established as a message queue id by msgget().
The msgp argument is a pointer to a structure in the user memory area that contains the type of the message and the message to be sent.
The msgsz argument specifies the length of the character array in the data structure pointed to by the msgp argument. This is the length of the message. The maximum size of this array is determined by the MSGMAX system tuning parameter.
The msg_qbytes data structure member can be lowered from MSGMNB by using the msgctl() IPC_SET control command, but only the superuser can raise it afterwards.
The msgflg argument allows the "blocking message operation" to be performed if the IPC_NOWAIT flag is not set (msgflg & IPC_NOWAIT = 0). This occurs if the total number of bytes allowed on the specified message queue are in use (msg_qbytes or MSGMNB), or the total system-wide number of messages on all queues is equal to the system imposed limit (MSGTQL). If the IPC_NOWAIT flag is set, the system call fails and returns -1.
Further details of this system call are discussed in the example program for it. If you don't understand the logic manipulations in this program, read the "Getting Message Queues With msgget()" section of this chapter; it goes into more detail than would be practical to do for every system call.
The msqid argument must be a nonnegative integer established as a message queue id by msgget().
The msgp argument is a pointer to a structure in the user memory area that receives the message type and the message text.
The msgsz argument specifies the length of the message to be received. If its value is less than size of the message in the array, an error can be returned if desired; see the msgflg argument.
The msgtyp argument is used to pick the first message on the message queue of the particular type specified. If msgtyp is equal to zero, the first message on the queue is received; if it is greater than zero, the first message of the same type is received; if it is less than zero, the lowest type that is less than or equal to its absolute value is received.
The msgflg argument allows the "blocking message operation" to be performed if the IPC_NOWAIT flag is not set (msgflg & IPC_NOWAIT = 0); this occurs if there is not a message on the message queue of the desired type (msgtyp) to be received. If the IPC_NOWAIT flag is set, the system call fails immediately when a message of the desired type is not on the queue. msgflg can also specify that the system call fails if the message is longer than the size to be received; this is done by not setting the MSG_NOERROR flag in the msgflg argument (msgflg & MSG_NOERROR = 0). If the MSG_NOERROR flag is set, the message is truncated to the length specified by the msgsz argument of msgrcv().
Further details of this system call are discussed in the example program for it. If you don't understand the logic manipulations in this program, read the "Getting Message Queues With msgget()" section of this chapter; it goes into more detail than would be practical to do for every system call.
From studying this program, you can observe the method of passing arguments and receiving return values. The user-written program requirements are pointed out.
This program begins (lines 5-9) by including the required header files as specified by the msgop(2) reference page. Note that in this program, errno is declared as an external variable. Therefore, the errno.h header file does not have to be included.
Variable and structure names have been chosen to be as close as possible to those in the synopsis. Their declarations are self-explanatory. These names make the program more readable, and this is perfectly legal since they are local to the program.
The variables declared for this program and their purposes are as follows:
sndbuf | Used as a buffer to contain a message to be sent (line 13); it uses the msgbuf1 data structure as a template (lines 10-13). The msgbuf1 structure (lines 10-13) is almost an exact duplicate of the msgbuf structure contained in the msg.h header file. The only difference is that the character array for msgbuf1 contains the maximum message size (MSGMAX) for the workstation, whereas in msgbuf it is set to 1 to satisfy the compiler. For this reason, msgbuf cannot be used directly as a template for the user-written program. It is there so you can determine its members. |
rcvbuf | Used as a buffer to receive a message (line 13); it uses the msgbuf1 data structure as a template (lines 10-13). |
*msgp | Used as a pointer (line 13) to both the sndbuf and rcvbuf buffers. |
i | Used as a counter for inputting characters from the keyboard, storing them in the array, and keeping track of the message length for the msgsnd() system call; it is also used as a counter to output the received message for the msgrcv() system call. |
c | Used to receive the input character from the getchar() function (line 50). |
flag | Used to store the code of IPC_NOWAIT for the msgsnd() system call (line 61). |
flags | Used to store the code of the IPC_NOWAIT or MSG_NOERROR flags for the msgrcv() system call (line 117). |
choice | Used to store the code for sending or receiving (line 30). |
rtrn | Used to store the return values from all system calls. |
msqid | Used to store and pass the desired message queue identifier for both system calls. |
msgsz | Used to store and pass the size of the message to be sent or received. |
msgflg | Used to pass the value of flag for sending or the value of flags for receiving. |
msgtyp | Used for specifying the message type for sending, or used to pick a message type for receiving. |
Note that a msqid_ds data structure is set up in the program (line 21) with a pointer that is initialized to point to it (line 22); this allows the data structure members that are affected by message operations to be observed. They are observed by using the msgctl() system call (with IPC_STAT) to get them so the program can print them out (lines 80-92 and lines 161-168).
The first thing the program prompts for is whether to send or receive a message. A corresponding code must be entered for the desired operation, and it is stored in the choice variable (lines 23-30). Depending upon the code, the program proceeds as in the following msgsnd() or msgrcv() sections.
The program now prompts for a message to be entered from the keyboard and enters a loop of getting and storing into the mtext array of the data structure (lines 48-51). This continues until an end of file is reached (for the getchar() function, this is a <Ctrl-D> immediately following a carriage return). When this happens, the size of the message is determined by adding 1 to the i counter (lines 52, 53) as the is stored beginning in the zero array element of mtext. Keep in mind that the message also contains the terminating characters; therefore, the message appears to be three characters short of msgsz.
The message is immediately echoed from the mtext array of the sndbuf data structure to provide feedback (lines 54-56).
The next and final thing that must be decided is whether to set the IPC_NOWAIT flag. The program does this by requesting that 1 be entered for yes or anything else for no (lines 57-65). The result is stored in the flag variable. If a 1 is entered, IPC_NOWAIT is logically ORed with msgflg; otherwise, msgflg is set to zero.
The msgsnd() system call is then performed (line 69). If it is unsuccessful, a failure message is displayed along with the error number (lines 70-72). If it is successful, the returned value (which should be zero) is printed (lines 73-76).
Every time a message is successfully sent, three members of the associated data structure are updated. They are:
msg_qnum | The total number of messages on the message queue. |
msg_lspid | The process identification (PID) number of the last process sending a message. |
msg_stime | The time in seconds since January 1, 1970, Greenwich Mean Time (GMT) of the last message sent. |
These members are displayed after every successful message send operation (lines 79-92).
The msgp pointer is initialized to the rcvbuf data structure (line 99).
Next, the message queue identifier of the message queue from which to receive the message is requested, and it is stored in msqid (lines 100-103).
The message type is requested, and it is stored in msgtyp (lines 104-107).
The code for the desired combination of control flags is requested next, and it is stored in flags (lines 108-117). Depending upon the selected combination, msgflg is set accordingly (lines 118-133).
Finally, the number of bytes to be received is requested, and it is stored in msgsz (lines 134-137).
The msgrcv() system call is performed (line 144). If it is unsuccessful, a message and error number are displayed (lines 145-148). If the call is successful, a message indicates success, and the number of bytes returned is displayed followed by the received message (lines 153-159).
When a message is successfully received, three members of the associated data structure are updated; they are described as follows:
Example 2-6 : msgsnd() and msgrcv() System Call Example
1 /*This is a program to illustrate 2 * the message operations, msgop(), 3 * system call capabilities: msgsnd() and msgrcv(). 4 */ 5 /*Include necessary header files.*/ 6 #include <stdio.h> 7 #include <sys/types.h> 8 #include <sys/ipc.h> 9 #include <sys/msg.h> 10 struct msgbuf1 { 11 long mtype; 12 char mtext[8192]; 13 } sndbuf, rcvbuf, *msgp; 14 /*Start of main C language program*/ 15 main() 16 { 17 extern int errno; 18 int i, c, flag, flags, choice; 19 int rtrn, msqid, msgsz, msgflg; 20 long mtype, msgtyp; 21 struct msqid_ds msqid_ds, *buf; 22 buf = &msqid_ds; 23 /*Select the desired operation.*/ 24 printf("Enter the corresponding\n"); 25 printf("code to send or\n"); 26 printf("receive a message:\n"); 27 printf("Send = 1\n"); 28 printf("Receive = 2\n"); 29 printf("Entry = "); 30 scanf("%d", &choice); 31 if(choice == 1) /*Send a message.*/ 32 { 33 msgp = &sndbuf; /*Point to user send structure.*/ 34 printf("\nEnter the msqid or\n"); 35 printf("the message queue to\n"); 36 printf("handle the message = "); 37 scanf("%d", &msqid); 38 /*Set the message type.*/ 39 printf("\nEnter a positive integer\n"); 40 printf("message type (long) for the\n"); 41 printf("message = "); 42 scanf("%d", &msgtyp); 43 msgp->mtype = msgtyp; 44 /*Enter the message to send.*/ 45 printf("\nEnter a message: \n"); 46 /*A control-d (^d) terminates as 47 EOF.*/ 48 /*Get each character of the message 49 and put it in the mtext array.*/ 50 for(i = 0; ((c = getchar()) != EOF); i++) 51 sndbuf.mtext[i] = c; 52 /*Determine the message size.*/ 53 msgsz = i + 1; 54 /*Echo the message to send.*/ 55 for(i = 0; i < msgsz; i++) 56 putchar(sndbuf.mtext[i]); 57 /*Set the IPC_NOWAIT flag if 58 desired.*/ 59 printf("\nEnter a 1 if you want the\n"); 60 printf("the IPC_NOWAIT flag set: "); 61 scanf("%d", &flag); 62 if(flag == 1) 63 msgflg |= IPC_NOWAIT; 64 else 65 msgflg = 0; 66 /*Check the msgflg.*/ 67 printf("\nmsgflg = 0%o\n", msgflg); 68 /*Send the message.*/ 69 rtrn = msgsnd(msqid, msgp, msgsz, msgflg); 70 if(rtrn == -1) 71 printf("\nMsgsnd failed. Error = %d\n", 72 errno); 73 else { 74 /*Print the value of test which 75 should be zero for successful.*/ 76 printf("\nValue returned = %d\n", rtrn); 77 /*Print the size of the message 78 sent.*/ 79 printf("\nMsgsz = %d\n", msgsz); 80 /*Check the data structure update.*/ 81 msgctl(msqid, IPC_STAT, buf); 82 /*Print out the affected members.*/ 83 /*Print the incremented number of 84 messages on the queue.*/ 85 printf("\nThe msg_qnum = %d\n", 86 buf->msg_qnum); 87 /*Print the process id of the last sender.*/ 88 printf("The msg_lspid = %d\n", 89 buf->msg_lspid); 90 /*Print the last send time.*/ 91 printf("The msg_stime = %d\n", 92 buf->msg_stime); 93 } 94 } 95 if(choice == 2) /*Receive a message.*/ 96 { 97 /*Initialize the message pointer 98 to the receive buffer.*/ 99 msgp = &rcvbuf; 100 /*Specify the message queue which contains 101 the desired message.*/ 102 printf("\nEnter the msqid = "); 103 scanf("%d", &msqid); 104 /*Specify the specific message on the queue 105 by using its type.*/ 106 printf("\nEnter the msgtyp = "); 107 scanf("%d", &msgtyp); 108 /*Configure the control flags for the 109 desired actions.*/ 110 printf("\nEnter the corresponding codeen"); 111 printf("to select the desired flags: \n"); 112 printf("No flags = 0\n"); 113 printf("MSG_NOERROR = 1\n"); 114 printf("IPC_NOWAIT = 2\n"); 115 printf("MSG_NOERROR and IPC_NOWAIT = 3\n"); 116 printf(" Flags = "); 117 scanf("%d", &flags); 118 switch(flags) { 119 /*Set msgflg by ORing it with the appropriate 120 flags (constants).*/ 121 case 0: 122 msgflg = 0; 123 break; 124 case 1: 125 msgflg |= MSG_NOERROR; 126 break; 127 case 2: 128 msgflg |= IPC_NOWAIT; 129 break; 130 case 3: 131 msgflg |= MSG_NOERROR | IPC_NOWAIT; 132 break; 133 } 134 /*Specify the number of bytes to receive.*/ 135 printf("\nEnter the number of bytes\n"); 136 printf("to receive (msgsz) = "); 137 scanf("%d", &msgsz); 138 /*Check the values for the arguments.*/ 139 printf("\nmsqid =%d\n", msqid); 140 printf("\nmsgtyp = %d\n", msgtyp); 141 printf("\nmsgsz = %d\n", msgsz); 142 printf("\nmsgflg = 0%o\n", msgflg); 143 /*Call msgrcv to receive the message.*/ 144 rtrn = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); 145 if(rtrn == -1) { 146 printf("\nMsgrcv failed. "); 147 printf("Error = %d\n", errno); 148 } 149 else { 150 printf ("\nMsgctl was successful\n"); 151 printf("for msqid = %d\n", 152 msqid); 153 /*Print the number of bytes received, 154 it is equal to the return 155 value.*/ 156 printf("Bytes received = %d\n", rtrn); 157 /*Print the received message.*/ 158 for(i = 0; i<=rtrn; i++) 159 putchar(rcvbuf.mtext[i]); 160 } 161 /*Check the associated data structure.*/ 162 msgctl(msqid, IPC_STAT, buf); 163 /*Print the decremented number of messages.*/ 164 printf("\nThe msg_qnum = %d\n", buf->msg_qnum); 165 /*Print the process id of the last receiver.*/ 166 printf("The msg_lrpid = %d\n", buf->msg_lrpid); 167 /*Print the last message receive time*/ 168 printf("The msg_rtime = %d\n", buf->msg_rtime); 169 } 170 }