Next | Prev | Up | Top | Contents | Index

Operations for Messages: msgsnd() and msgrcv()

This section describes the msgsnd() and msgrcv() system calls and presents an example program that exercises all of their capabilities.

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


Sending a Message

The msgsnd() system call requires four arguments to be passed to it. It returns an integer value.

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.


Receiving Messages

The msgrcv() system call requires five arguments to be passed to it, and it returns an integer value. Upon successful completion, msgrcv() returns a value equal to the number of bytes received returned. When unsuccessful it returns -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 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.


Example Program

The example program in this section (Example 2-6) is a menu-driven program that exercises all possible combinations of the msgsnd() and msgrcv() system calls.

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:

sndbufUsed 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.
rcvbufUsed as a buffer to receive a message (line 13); it uses the msgbuf1 data structure as a template (lines 10-13).
*msgpUsed as a pointer (line 13) to both the sndbuf and rcvbuf buffers.
iUsed 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.
cUsed to receive the input character from the getchar() function (line 50).
flagUsed to store the code of IPC_NOWAIT for the msgsnd() system call (line 61).
flagsUsed to store the code of the IPC_NOWAIT or MSG_NOERROR flags for the msgrcv() system call (line 117).
choiceUsed to store the code for sending or receiving (line 30).
rtrnUsed to store the return values from all system calls.
msqidUsed to store and pass the desired message queue identifier for both system calls.
msgszUsed to store and pass the size of the message to be sent or received.
msgflgUsed to pass the value of flag for sending or the value of flags for receiving.
msgtypUsed 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.


Example Use of msgsnd

When the code is to send a message, the msgp pointer is initialized (line 33) to the address of the send data structure, sndbuf. Next, a message type must be entered for the message; it is stored in the variable msgtyp (line 42), and then (line 43) it is put into the mtype member of the data structure pointed to by msgp.

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_qnumThe total number of messages on the message queue.
msg_lspidThe process identification (PID) number of the last process sending a message.
msg_stimeThe 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).


Example Use of msgrcv

If the code specifies that a message is to be received, the program continues execution as in the following paragraphs.

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:

msg_qnum

contains the number of messages on the message queue; it is decremented by one

msg_lrpid

contains the process identification (PID) of the last process receiving a message; it is set accordingly

msg_rtime

contains the time in seconds since January 1, 1970, Greenwich Mean Time (GMT) that the last process received a message; it is set accordingly
The example program for the msgop() system calls is shown in Example 2-6.

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 }

Next | Prev | Up | Top | Contents | Index