Quick Overview of Message Queues


Let s begin by taking a whirlwind tour of the POSIX-compliant message queue API. We ll look at code examples that illustrate creating a message queue, configuring its size , sending and receiving a message, and then removing the message queue. Once we ve had a taste of the message queue API, we ll dive in deeper in the following sections.

Using the message queue API requires that the function prototypes and symbols be available to the application. This is done by including the msg.h header file as:

 #include <sys/msg.h> 

We ll first introduce a common header file that defines some common information needed for the writer and reader of the message. We define our system-wide queue ID (111) at line 3. This isn t the best way to define the queue, but later on we ll look at a way to define a unique system ID. Lines 5 “10 define our message type, with the required long type at the head of the structure (line 6).

Listing 15.1: Common Header File Used by the Sample Applications (on the CD-ROM at ./source/ch15/common.h )
start example
  1  :       #define MAX_LINE        80  2  :  3  :       #define MY_MQ_ID        111  4  :  5  :       typedef struct {  6  :               long  type;              // Msg Type (> 0)  7  :               float fval;              // User Message  8  :               unsigned int uival;      // User Message  9  :               char strval[MAX_LINE+1]; // User Message  10  :       } MY_TYPE_T; 
end example
 

Creating a Message Queue

To create a message queue, we use the msgget API function. This function takes a message queue ID (a unique identifier, or key, within a given host) and another argument identifying the message flags. The flags, in the queue create example (see Listing 15.2) specify that a queue is to be created ( IPC_CREAT ) as well as the access permissions of the message queue (read/write permission for system, user, and group ).

Note  

The result of the msgget function is a handle, which is similar to a file descriptor, pointing to the message queue with the particular ID.

Listing 15.2: Creating a Message Queue with msgget (on the CD-ROM at ./source/ch15/mqcreate.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/msg.h>  3  :       #include "common.h"  4  :  5  :       int main()  6  :       {  7  :         int msgid;  8  :  9  :         /* Create the message queue with the id MY_MQ_ID */  10:  msgid  = msgget  (MY_MQ_ID, 0666  IPC_CREAT);  11  :  12  :         if (msgid >= 0) {  13  :  14  :           printf("Created a Message Queue %d\n", msgid);  15  :  16  :         }  17  :  18  :         return 0;  19  :       } 
end example
 

Upon creating the message queue at line 10 (in Listing 15.2), we get a return integer that represents a handle for the message queue. This message queue ID can be used in subsequent message queue calls to send or receive messages.

Configuring a Message Queue

When we create a message queue, some of the details of the process that created the queue are automatically stored with it (for permissions) as well as a default queue size in bytes (16KB). We can adjust this size using the msgctl API function. Listing 15.3 illustrates reading the defaults for the message queue, adjusting the queue size, and then configuring the queue with the new set.

Listing 15.3: Configuring a Message Queue with msgctl (on the CD-ROM at ./source/ch15/mqconf.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/msg.h>  3  :       #include "common.h"  4  :  5  :       int main()  6  :       {  7  :         int msgid, ret;  8  :         struct msqid_ds buf;  9  :  10  :         /* Get the message queue for the id MY_MQ_ID */  11  :         msgid =  msgget  (MY_MQ_ID, 0);  12  :  13  :         /* Check successful completion of msgget */  14  :         if (msgid >= 0) {  15  :  16  :           ret =  msgctl  (msgid, IPC_STAT, &buf);  17  :  18  :           buf.msg_qbytes = 4096;  19  :  20  :           ret =  msgctl  (msgid, IPC_SET, &buf);  21  :  22  :           if (ret == 0) {  23  :  24  :             printf("Size successfully changed for queue %d.\n", msgid);  25  :  26  :           }  27  :  28  :         }  29  :  30  :         return 0;  31  :       } 
end example
 

First, at line 11, we get the message queue ID using msgget . Note that the second argument here is 0 because we re not creating the message queue, just retrieving its ID. We use this at line 16 to get the current queue data structure using the IPC_STAT command and our local buffer (for which the function will fill in the defaults). We adjust the queue size at line 18 (by modifying the msg_qbytes field of the structure) and then write it back at line 20 using the msgctl API function with the IPC_SET command. We could also modify the user or group ID of the message queue or its mode. We ll discuss these in more detail later.

Writing a Message to a Message Queue

Now let s look at actually sending a message through a message queue. A message within the context of a message queue has only one constraint. The object that s being sent must include a long variable at its head that defines the message type. We ll discuss this more later, but it s simply a way to differentiate messages that have been loaded onto a queue (and also how those messages can be read from the queue). The general structure for a message is:

 typedef struct {       long type;       char message[80];     } MSG_TYPE_T; 

In this example ( MSG_TYPE_T ), we have our required long at the head of the message, followed by the user-defined message (in this case, a string of 80 characters ).

To send a message to a message queue (see Listing 15.4), we use the msgsnd API function. Following a similar pattern to our previous examples, we first identify the message queue ID using the msgget API function (line 11). Once this is known, we can send a message to it. Next , we initialize our message at lines 16 “19. This includes specifying the mandatory type (must be greater than 0), a floating-point value ( fval ) and unsigned int value ( uival ), and a character string ( strval ). To send this message, we call the msgsnd API function. The arguments for this function are the message queue ID ( qid ), our message (a reference to myObject ), the size of the message we re sending (the size of MY_TYPE_T ), and finally a set of message flags (for now, , but we ll investigate more later).

Listing 15.4: Sending a Message with msgsnd . (on the CD-ROM at ./source/ch15/_mqsend.c )
start example
  1  :       #include <sys/msg.h>  2  :       #include <stdio.h>  3  :       #include "common.h"  4  :  5  :       int main()  6  :       {  7  :         MY_TYPE_T myObject;  8  :         int qid, ret;  9  :  10  :         /* Get the queue ID for the existing queue */  11  :         qid =  msgget  (MY_MQ_ID, 0);  12  :  13  :         if (qid >= 0) {  14  :  15  :           /* Create our message with a message queue type of 1 */  16  :           myObject.type = 1L;  17  :           myObject.fval = 128.256;  18  :           myObject.uival = 512;  19  :           strncpy(myObject.strval, "This is a test.\n", MAX_LINE);  20  :  21  :           /* Send the message to the queue defined by the queue ID */  22  :           ret =  msgsnd  (qid, (struct msgbuf *)&myObject,  23  :                          sizeof(MY_TYPE_T), 0);  24  :  25  :           if (ret != -1) {  26  :  27  :             printf("Message successfully sent to queue %d\n", qid);  28  :  29  :           }  30  :  31  :         }  32  :  33  :         return 0;  34  :       } 
end example
 

That s it! This message is now held in the message queue, and at any point in the future, it can be read (and consumed) by the same or a different process.

Reading a Message from a Message Queue

Now that we have a message in our message queue, let s look at reading that message and displaying its contents. We retrieve the ID of the message queue using msgget at line 12 and then use this as the target queue from which to read using the msgrcv API function at lines 16 “17. The arguments to msgrcv are first the message queue ID ( qid ), the message buffer into which our message will be copied _( myObject ), the size of the object ( sizeof(MY_TYPE_T) ), the message type that we want to read ( 1 ), and the message flags ( ). Note that when we sent our message (in Listing 15.4), we specified our message type as 1 . We use this same value here to read the message from the queue. Had we used another value, the message would not have been read. More on this subject in the msgrcv section later in this chapter.

Listing 15.5: Reading a Message with msgrcv (on the CD-ROM at ./source/ch15/_mqrecv.c )
start example
  1  :       #include <sys/msg.h>  2  :       #include <stdio.h>  3  :       #include "common.h"  4  :  5  :       int main()  6  :       {  7  :         MY_TYPE_T myObject;  8  :         int qid, ret;  9  :  10  :         qid =  msgget  (MY_MQ_ID, 0);  11  :  12  :         if (qid >= 0) {  13  :  14  :           ret =  msgrcv  (qid, (struct msgbuf *)&myObject,  15  :                          sizeof(MY_TYPE_T), 1, 0);  16  :  17  :           if (ret != -1) {  18  :  19  :             printf("Message Type: %ld\n", myObject.type);  20  :             printf("Float Value:  %f\n",  myObject.fval);  21  :             printf("Uint Value:   %d\n",  myObject.uival);  22  :             printf("String Value: %s\n",  myObject.strval);  23  :  24  :           }  25  :  26  :         }  27  :  28  :         return 0;  29  :       } 
end example
 

The final step in our application in Listing 15.5 is to emit the message read from the message queue. We use our object type to access the fields in the structure and simply emit them with printf .

Removing a Message Queue

As a final step, let s look at how we can remove a message queue (and any messages that may be held on it). We use the msgctl API function for this purpose with the command of IPC_RMID . This is illustrated in Listing 15.6.

Listing 15.6: Removing a Message Queue with msgctl (on the CD-ROM at ./source/_ch15/mqdel.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/msg.h>  3  :       #include "common.h"  4  :  5  :       int main()  6  :       {  7  :         int   msgid, ret;  8  :  9  :         msgid =  msgget  (MY_MQ_ID, 0);  10  :  11  :         if (msgid >= 0) {  12  :  13  :           /* Remove the message queue */  14  :           ret =  msgctl  (msgid, IPC_RMID, NULL);  15  :  16  :           if (ret != -1) {  17  :  18  :             printf("Queue %d successfully removed.\n", msgid);  19  :  20  :           }  21  :  22  :         }  23  :  24  :         return 0;  25  :       } 
end example
 

In Listing 15.6, we first identify the message queue ID using msgget and then use this with msgctl to remove the message queue. Any messages that happened to be on the message queue when msgctl was called would be immediately removed.

That does it for our whirlwind tour. In the next section, we ll dig deeper into message queue API and look at some of the behaviors of the commands that weren t covered already.

The Message Queue API

Let s now dig into the message queue API and investigate each of the functions in more detail. For a quick review, Table 15.1 provides the API functions and their purposes.

Table 15.1: Message Queue API Functions and Uses

API Function

Uses

msgget

Create a new message queue

 

Get a message queue ID

msgsnd

Send a message to a message queue

msgrcv

Receive a message from a message queue

msgctl

Get the info about a message queue

 

Set the info for a message queue

 

Remove a message queue

Figure 15.1 graphically illustrates the message queue API functions and their relationship in the process.

click to expand
Figure 15.1:    Message queue API functions.

We ll address these functions now in detail, identifying each of the uses with descriptive examples.

msgget

The msgget API function serves two basic roles: to create a message queue or to get the identifier of a message queue that already exists. The result of the msgget function (unless an error occurs) is the message queue identifier (used by all other message queue API functions). The prototype for the msgget function is defined as follows :

 int  msgget  (key_t key, int msgflag); 

The key argument defines a system-wide identifier that uniquely identifies a message queue. key must be a nonzero value or the special symbol IPC_PRIVATE . The IPC_PRIVATE variable simply tells the msgget function that no key is provided and to simply make one up. The problem with this is that no other process can then find the message queue, but for local message queues (private queues), this method works fine.

The msgflag argument allows the user to specify two distinct parameters: a command and an optional set of access permissions. Permissions replicate those found as modes for the file creation functions (see Table 15.2). The command can take three forms. The first is simply IPC_CREAT , which instructs msgget to create a new message queue (or return the ID for the queue if it already exists). The second includes two commands ( IPC_CREAT IPC_EXCL ), which request that the message queue be created, but if it already exists, the API function should fail and return an error response ( EEXIST ). The third possible command argument is simply 0. This form tells msgget that the message queue identifier for an existing queue is being requested .

242

Table 15.2: Message Queue Permissions for the msgget msgflag Argument

Symbol

Value

Meaning

S_IRUSR

0400

User has read permission

S_IWUSR

0200

User has write permission

S_IRGRP

0040

Group has read permission

S_IWGRP

0020

Group has write permission

S_IROTH

0004

Other has read permission

S_IWOTH

0002

Other has write permission

Let s look at a few examples of the msgget function to create message queues or access existing ones. Assume in the following code snippets that msgid is an int value ( int msgid ). Let s start by creating a private queue (no key is provided).

 msgid =  msgget  (IPC_PRIVATE, IPC_CREAT  0666); 

If the msgget API function fails, -1 is returned with the actual error value provided within the process s errno variable.

Let s now say that we want to create a message queue with a key value of 0x111 . We also want to know if the queue already exists, so we ll use the IPC_EXCL in this example:

 // Create a new message queue    msgid =  msgget  (0x111, IPC_CREAT  IPC_EXCL  0666);     if (msgid == -1) {       printf("Queue already exists...\n");     } else {       printf("Queue created...\n");     } 

An interesting question you ve probably asked yourself now is how can you coordinate the creation of queues using IDs that may not be unique? What happens if someone already used the 0x111 key? Luckily, there s a way to create keys in a system-wide fashion that ensures uniqueness. The ftok system function provides the means to create system-wide unique keys using a file in the filesystem and a number. As the file (and its path ) will by default be unique in the filesystem, a unique key can be created easily. Let s look at an example of using ftok to create a unique key. Assume that the file with path /home/mtj/queues/myqueue exists.

 key_t myKey;     int   msgid;     // Create a key based upon the defined path and number     myKey = ftok("/home/mtj/queues/myqueue", 0);     msgid =  msgget  (myKey, IPC_CREAT  0666); 

This will create a key for this path and number. Each time ftok is called with this path and number, the same key will be generated. Therefore, it provides a useful way to generate a key based upon a file in the filesystem.

One last example is getting the message queue ID of an existing message queue. The only difference in this example is that we provide no command, only the key:

 msgid =  msgget  (0x111, 0);     if (msgid == -1) {       printf("Queue doesnt exist...\n");     } 

The msgflags (second argument to msgget ) is zero in this case, which indicates to this API function that an existing message queue is being sought.

One final note on message queues is the default settings that are given to a message queue when it is created. The configuration of the message queue is noted in the parameters shown in Table 15.3. Note that there s no way to change these defaults within msgget . In the next section, we ll look at some of the parameters that can be changed and their effects.

The user can override the msg_perm.uid , msg_perm.gid , msg_perm.mode , and msg_qbytes directly. More on this topic in the next section.

msgctl

The msgctl API function provides three distinct features for message queues. The first is the ability to read the current set of message queue defaults (via the IPC_STAT command). The second is the ability to modify a subset of the defaults (via IPC_SET ). Finally, the ability to remove a message queue is provided (via IPC_RMID ). The msgctl prototype function is defined as:

 #include <sys/msg.h>     int  msgctl  (int msgid, int cmd, struct msqid_ds *buf); 
Table 15.3: Message Queue Configuration and Defaults in msgget

Parameter

Default Value

msg_perm.cuid

Effective user ID of the calling process (creator)

msg_perm.uid

Effective user ID of the calling process (owner)

msg_perm.cgid

Effective group ID of the calling process (creator)

msg_perm.gid

Effective group ID of the calling process (owner)

msg_perm.mode

Permissions (lower 9 bits of msgflag )

msg_qnum

0 (Number of messages in the queue)

msg_lspid

0 (Process ID of last msgsnd )

msg_lrpid

0 (Process ID of last msgrcv )

msg_stime

0 (last msgsnd time)

msg_rtime

0 (Last msgrcv time)

msg_ctime

Current time (last change time)

msg_qbytes

Queue size in bytes (system limit) ”(16KB)

Let s start by looking at msgctl as a means to remove a message queue from the system. This is the simplest use of msgctl and can be demonstrated very easily. In order to remove a message queue, all that s needed is the message queue identifier that is returned by msgctl .

Note  

While a system-wide unique key is required to create a message queue, only the message queue ID (returned from msgget ) is required to configure a queue, send a message from a queue, receive a message from a queue, or remove a queue.

Let s look at an example of message queue removal using msgctl . We first get the message queue identifier using msgget and then use this ID in our call to msgctl .

 int msgid, ret;     ...     msgid =  msgget  (QUEUE_KEY, 0);     if (msgid != -1) {       ret =  msgctl  (msgid, IPC_RMID, NULL);           if (ret == 0) {         // queue was successfully removed.       }     } 

If any processes are currently blocked on a msgsnd or msgrcv API function, those functions will return with an error ( -1 ) with the errno process variable set to EIDRM . The process performing the IPC_RMID must have adequate permissions to remove the message queue. If permissions do not allow the removal, an error return is generated with an errno variable set to EPERM .

Now let s look at IPC_STAT (read configuration) and IPC_SET (write configuration) commands together for msgctl . In the previous section, we identified the range of parameters that make up the configuration and status parameters. Now let s look at which of the parameters can be directly manipulated or used by the application developer. Table 15.4 lists the parameters that can be updated once a message queue has been created.

Table 15.4: Message Queue Parameters That May Be Updated

Parameter

Description

msg_perm.uid

Message queue user owner

msg_perm.gid

Message queue group owner

msg_perm.mode

Permissions (see Table 15.2)

msg_qbytes

Size of message queue in bytes

Changing these parameters is a very simple process. The process should be that the application first reads the current set of parameters (via IPC_STAT ) and then modifies the parameters of interest before writing them back out (via IPC_SET ). See Listing 15.7 for an illustration of this process.

Listing 15.7: Setting All Possible Options in msgctl (on the CD-ROM at ./source/_ch15/mqrdset.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/msg.h>  3  :       #include <unistd.h>  4  :       #include <sys/types.h>  5  :       #include <errno.h>  6  :       #include "common.h"  7  :  8  :       int main()  9  :       {  10  :         int msgid, ret;  11  :         struct msqid_ds buf;  12  :  13  :         /* Get the message queue for the id MY_MQ_ID */  14  :         msgid =  msgget  (MY_MQ_ID, 0);  15  :  16  :         /* Check successful completion of msgget */  17  :         if (msgid >= 0) {  18  :  19  :           ret =  msgctl  (msgid, IPC_STAT, &buf);  20  :  21  :           buf.msg_perm.uid = geteuid();  22  :           buf.msg_perm.gid = getegid();  23  :           buf.msg_perm.mode = 0644;  24  :           buf.msg_qbytes = 4096;  25  :  26  :           ret =  msgctl  (msgid, IPC_SET, &buf);  27  :  28  :           if (ret == 0) {  29  :  30  :             printf("Parameters successfully changed.\n");  31  :  32  :           } else {  33  :  34  :             printf("Error %d\n", errno);  35  :  36  :           }  37  :  38  :         }  39  :  40  :         return 0;  41  :       } 
end example
 

At line 14, we get our message queue identifier, and then we use this at line 19 to retrieve the current set of parameters. At line 21, we set the msg_perm.uid (effective user ID) with the current effective user ID using the geteuid() function. Similarly, we set the msg_perm.gid (effective group ID) at line 22 using the getegid() function. At line 23 we set the mode, and at line 24 we set the maximum queue size (in bytes). In this case we set it to 4KB. We now take this structure and set the parameters for the current message queue using the msgctl API function. This is done with the IPC_SET command in msgctl .

Note  

When setting the msg_perm.mode (permissions), it s important to note that this is traditionally defined as an octal value. Note at line 23 of Listing 15.7 that a leading zero is shown, indicating that the value is octal. If, for example, a decimal value of 666 were provided instead of octal 0666 , permissions would be invalid, and therefore undesirable behavior would result. For this reason, it can be beneficial to use the symbols as shown in Table 15.2.

We can also use the msgctl API function to identify certain message queue-_specific parameters, such as the number of messages currently on the message queue. Listing 15.8 illustrates the collection and printing of the accessible parameters.

Listing 15.8: Reading Current Message Queue Settings (on the CD-ROM at ./source/ch15/mqstats.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/msg.h>  3  :       #include <unistd.h>  4  :       #include <sys/types.h>  5  :       #include <time.h>  6  :       #include "common.h"  7  :  8  :       int main()  9  :       {  10  :         int msgid, ret;  11  :         struct msqid_ds buf;  12  :  13  :         /* Get the message queue for the id MY_MQ_ID */  14  :         msgid = msgget(MY_MQ_ID, 0);  15  :  16  :         /* Check successful completion of msgget */  17  :         if (msgid >= 0) {  18  :  19  :           ret = msgctl(msgid, IPC_STAT, &buf);  20  :  21  :           if (ret == 0) {  22  :  23  :             printf("Number of messages queued: %ld\n",  24  :                      buf.msg_qnum);  25  :             printf("Number of bytes on queue : %ld\n",  26  :                      buf.msg_cbytes);  27  :             printf("Limit of bytes on queue  : %ld\n",  28  :                      buf.msg_qbytes);  29  :  30  :             printf("Last message writer (pid): %d\n",  31  :                      buf.msg_lspid);  32  :             printf("Last message reader (pid): %d\n",  33  :                      buf.msg_lrpid);  34  :  35  :             printf("Last change time         : %s",  36  :                      ctime(&buf.msg_ctime));  37  :  38  :             if (buf.msg_stime) {  39  :               printf("Last msgsnd time         : %s",  40  :                        ctime(&buf.msg_stime));  41  :             }  42  :             if (buf.msg_rtime) {  43  :               printf("Last msgrcv time         : %s",  44  :                        ctime(&buf.msg_rtime));  45  :             }  46  :  47  :           }  48  :  49  :         }  50  :  51  :         return 0;  52  :       } 
end example
 

Listing 15.8 begins as most other message queue examples, with the collection of the message queue ID from msgget . Once we have our ID, we use this to collect the message queue structure using msgctl and the command IPC_STAT . We pass in a reference to the msqid_ds structure, which is filled in by the msgctl API function. We then emit the information collected in lines 23 “45.

At lines 23 “24, we emit the number of messages that are currently enqueued on the message queue ( msg_qnum ). The current total number of bytes that are enqueued is identified by msg_cbytes (lines 25 “26), and the maximum number of bytes that may be enqueued is defined by msg_qbytes (lines 27 “28).

We can also identify the last reader and writer process pids (lines 30 “33). These refer to the effective process ID of the calling process that called msgrcv or msgsnd .

The msg_ctime element refers to the last time the message queue was changed (or when it was created). It s in standard time_t format, so we pass msg_ctime to ctime to grab the ASCII text version of the calendar date and time. We do the same for msg_stime (last msgsnd time) and msg_rtime (last msgrcv time). Note that in the case of msg_stime and msg_rtime , we emit the sting dates only if their values are nonzero. If the values are zero, there have been no msgrcv or msgsnd API functions called.

msgsnd

The msgsnd API function allows a process to send a message to a queue. As we saw in the introduction, the message is purely user defined except that the first element in the message must be a long word for the type field. The function prototype for the msgsnd function is defined as:

 int  msgsnd  (int msgid, struct msgbuf *msgp, size_t msgsz,                       int msgflg); 

The msgid argument is the message queue ID (returned from the msgget function). The msgbuf represents the message to be sent; at a minimum it is a long value representing the message type. The msgsz argument identifies the size of the msgbuf passed in to msgsend , in bytes. Finally, the msgflag argument allows the developer to alter the behavior of the msgsnd API function.

The msgsnd function has some default behavior that the developer should consider. If insufficient room exists on the message queue to write the message, the process will be blocked until sufficient room exists. Otherwise, if room exists, the call succeeds immediately with a zero return to the caller.

Since we ve already looked at some of the standard uses of msgsnd , let s look at some of the more specialized cases. The blocking behavior is desirable in most cases as it can be the most efficient. In some cases, we may want to try to send a message, and if we re unable (due to the insufficient space on the message queue), do something else. Let s look at this example in the following code snippet:

 ret =  msgsnd  (msgid, (struct msgbuf *)&myMessage,                        sizeof(myMessage), IPC_NOWAIT);     if (ret == 0) {       // Message was successfully enqueued     } else {       if (errno == EAGAIN) {         // Insufficient space, do something else...       }     } 

The IPC_NOWAIT symbol (passed in as the msgflags ) tells the msgsnd API function that if insufficient space exists, don t block but instead return immediately. We know this because an error was returned (indicated by the -1 return value), and the errno variable was set to EAGAIN . Otherwise, with a zero return, the message was successfully enqueued on the message queue for the receiver.

While a message queue should not be deleted while processes pend on msgsnd , a special error return is surfaced when this occurs. If a process is currently blocked on a msgsnd and the message queue is deleted, then a -1 value is returned with an errno value set to EIDRM .

One final item to note on msgsnd is the parameters that are modified when the msgsnd API call finishes. Table 15.3 lists the entire structure, but the items modified after successful completion of the msgsnd API function are listed in Table 15.5.

Note  

Note that the msg_stime is the time that the message was enqueued and not the time that the msgsnd API function was called. This can be important if the msgsnd function blocks (due to a full message queue).

Table 15.5: Structure Updates after Successful msgsnd Completion

Parameter

Update

msg_lspid

Set to the process ID of the process that called msgsnd

msg_qnum

Incremented by one

msg_stime

Set to the current time

msgrcv

Let s now focus on the last function in the message queue API. The msgrcv API function provides the means to read a message from the queue. The user provides a message buffer (filled in within msgrcv ) and the message type of interest. The function prototype for msgrcv is defined as:

 ssize_t  msgrcv  (int msgid, struct msgbuf *msgp, size_t msgsz,                          long msgtyp, int msgflg); 

The arguments passed to msgrcv include the msgid (message queue identifier received from msgget ), a reference to a message buffer ( msgp ), the size of the buffer ( msgsz ), the message type of interest ( msgtyp ), and finally a set of flags ( msgflag ). The first three arguments are self-explanatory, so let s concentrate on the latter two: msgtyp and msgflag .

The msgtyp argument (message type) specifies to msgrcv the messages to be received. Each message within the queue contains a message type. The msgtyp argument to msgrcv defines that only those types of messages are sought. If no messages of that type are found, the calling process blocks until a message of the desired type is enqueued. Otherwise, the first message of the given type is returned to the caller. The caller could provide a 0 as the msgtyp , which tells msgrcv to ignore the message type and return the first message on the queue. One exception to the message type request is discussed with msgflg .

The msgflg argument allows the caller to alter the default behavior of the msgrcv API function. As with msgsnd , we can instruct msgrcv not to block if no messages are waiting on the queue. This is done also with the IPC_NOWAIT flag. We discussed the use of msgtyp with a 0 and nonzero value, but what if we were interested in any flag except a certain one? This can be accomplished by setting msgtyp with the undesired message type and setting the flag MSG_EXCEPT within msgflg . Finally, the use of flag MSG_NOERROR instructs msgrcv to ignore the size check of the incoming message and the available buffer passed from the user and simply truncate the message if the user buffer isn t large enough. All of the options for msgtyp are described in Table 15.6, and options for msgflg are shown in Table 15.7.

Table 15.6: msgtyp Arguments for msgrcv

msgtyp

Description

Read the first message available on the queue.

>0

If the msgflg MSG_EXCEPT is set, read the first message on the queue not equal to the msgtyp . Otherwise, if MSG_EXCEPT is not set, read the first message on the queue with the defined msgtyp .

<0

The first message on the queue that is less than or equal to the absolute value of msgtyp is returned.

Table 15.7: msgflg Arguments for msgrcv

Flag

Description

IPC_NOWAIT

Return immediately if no messages awaiting of the given msgtyp (no blocking).

MSG_EXCEPT

Return first message available other than msgtyp .

MSG_NOERROR

Truncate the message if user buffer isn t of sufficient size.

When a message is read from the queue, the internal structure representing the queue is automatically updated as shown in Table 15.8.

Note  

Note that msg_rtime is the time that the message was dequeued and not the time that the msgrcv API function was called. This can be important if the msgrcv function blocks (due to an empty message queue).

Table 15.8: Structure Updates after Successful msgsnd Completion

Parameter

Update

msg_lrpid

Set to the process ID of process calling msgrcv .

msg_qnum

Decremented by one.

msg_rtime

Set to the current time.

Let s now look at some examples to illustrate msgrcv and the use of msgtyp and msgflg options. The most common use of msgrcv is to read the next available message from the queue:

 ret =  msgrcv  (msgid, (struct msgbuf *)&buf, sizeof(buf), 0, 0);     if (ret != -1) {       printf("Message of type %ld received\n", *(long *)&buf);     } 

Note that we check specifically for a return value that s not -1 . We do this because msgrcv actually returns the number of bytes read. If the return is -1 , errno contains the error that occurred.

If we desired not to block on the call, we could do this very simply as:

 ret =  msgrcv  (msgid, (struct msgbuf *)&buf, sizeof(buf),                         0, IPC_NOWAIT);     if (ret != -1) {       printf("Message of type %ld received\n", *(long *)&buf);     } else if (errno == EAGAIN) {       printf("Message unavailable\n");     } 

With the presence of an error return from msgrcv and errno set to EAGAIN , it s understood that no messages were available for read. This isn t actually an error, just an indication that no messages were available in the nonblocking scenario.

Message queues permit multiple writers and readers to the same queue. These could be the same process, but very likely each is a different process. Let s say that we have a process that manages only a certain type of message. We identify this particular message by its message type. In the next example, we provide a snippet from a process whose job it is to manage only messages of type 5.

 ret =  msgrcv  (msgid, (struct msgbuf *)&buf, sizeof(buf), 5, 0); 

Any message sent of type 5 would be received by the process executing this code snippet. To manage all other message types (other than 5), we could use the MSG_EXCEPT flag to receive these. Take for example:

 ret =  msgrcv  (msgid, (struct msgbuf *)&buf, sizeof(buf),                       5, MSG_EXCEPT); 

Any message received on the queue other than type 5 will be read using this line. If only messages of type 5 are available, this function blocks until a message not of type 5 is enqueued.

One final note on msgrcv is what happens if a process is blocked on a queue that is removed. The removal is permitted to occur, and the process blocked on the queue receives an error return with the errno set to EIDRM (as with blocked msgsnd calls). It s therefore important to fully recognize the error returns that are possible.




GNU/Linux Application Programming
GNU/Linux Application Programming (Programming Series)
ISBN: 1584505680
EAN: 2147483647
Year: 2006
Pages: 203
Authors: M. Tim Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net