Before a process can send or receive messages, the process must request that the operating system generate a new message queue (the mechanism used to control and keep track of messages) and an associated data structure. A process makes this request by using the msgget() system call. The requesting process becomes the owner and creator of the resulting message queue, and specifies the initial operation permissions for all processes that might use that queue (including itself). Subsequently, the owning process can relinquish ownership or change the operation permissions using the msgctl() system call. However, the creator remains the creator as long as the queue exists. Other processes with permission can use msgctl() to perform various other control functions, as described in "Controlling Message Queues: msgctl()."
A process that is attempting to send a message can suspend execution temporarily in order to wait until the process that is to receive the message is ready; similarly, a receiving process can suspend execution until the sending process is ready. Message operations that suspend execution in this fashion are called "blocking message operations." A process that specifies that its execution is not to be suspended--that is, a process that does not wait for communication if such is not immediately available--is performing a "nonblocking message operation."
A blocking message operation can be told to suspend a calling process until one of three conditions occurs:
Each message queue has a data structure associated with it. This data structure is declared as struct msqid_ds in the /usr/include/sys/msg.h header file and contains the following fields:
msg_perm | Permisssions information as struct ipc_perm |
msg_first, msg_last | Pointers to queue of message structures |
msg_cbytes | Number of bytes on the queue |
msg_qnum | Number of messages on the queue |
msg_qbytes | Maximum number of bytes on the queue |
msg_lspid | Process ID (PID) of last message sender |
msg_lrpid | PID of last message receiver |
msg_stime | Last message sent time |
msg_rtime | Last message receive time. |
msg_ctime | Last change time |
The permissions structure msg_perm is based on another structure called ipc_perm, which is also used as a template for permissions for other forms of IPC. The ipc_perm data structure format can be found in the /usr/include/sys/ipc.h header file.
The message queue points to a chain of header structures that define the messages in the queue. This per-message header is declared as struct msg in /usr/include/msg.h, and includes the following fields:
msg_next | Pointer to next message in the queue |
msg_type | Message type code |
msg_ts | Text size of message |
msg_spot | Message text address |
msgget(2) | Get access to a message queue. |
msgctl(2) | Get information about a message queue; change ownership of a queue; or destroy the queue. |
msgsnd(2) | Send a message to a queue. |
msgrcv(2) | Receive a message from a queue. |
The msgget() system call receives an argument msgflg, which can be set to indicate various flags. When only the IPC_CREAT flag is set in msgflg, msgget() performs one of two tasks:
Instead of requesting a specific key number, you may indicate a key of value zero, which is known as the private key (the constant IPC_PRIVATE is defined to be zero). When you specify IPC_PRIVATE for the key value, a new msqid is always returned with an associated message queue and data structure created for it unless a system tuning parameter would be exceeded. When the ipcs command is performed, the KEY field for the msqid is all zeros, for security reasons.
For the second task, if a msqid exists for the key specified, the value of the existing msqid is returned. If you do not want an existing msqid returned, you can specify a control command (IPC_EXCL) in the msgflg argument passed to the system call. The details of using this system call are discussed in "Getting Message Queues With msgget()"in this chapter.
When performing the first task, the process that calls msgget() becomes the owner/creator, and the associated data structure is initialized accordingly. Remember, ownership can be changed but the creating process always remains the creator; see "Controlling Message Queues: msgctl()." The creator of the message queue also determines the initial operation permissions for it.
Once a uniquely identified message queue and data structure are created, message operations and message control can be used.
The available message operations are sending and receiving. System calls are provided for these operations: msgsnd() and msgrcv(), respectively. Refer to "Operations for Messages: msgsnd() and msgrcv()," for details of these system calls.
Message control is done by using the msgctl() system call. It permits you to control the message facility in the following ways:
The type of the key parameter, key_t, is defined in the types.h header file to be equivalent to an integer.int msgget (key_t key, int msgflg)
Upon successful completion, msgget() returns a message queue identifier. A new msqid with an associated message queue and data structure is provided if either of the following is true:
A specific octal value is derived by adding the octal values for the operation permissions desired. For instance, if you want a message queue to be readable by its owner and both readable and writable by others, use the code value 00406 (00400 plus 00004 plus 00002). The constants MSG_R and MSG_W, defined in the msg.h header file, can be used instead of 00400 and 00200, respectively.
Control commands are constants defined in the ipc.h header file. See Table 2-3, which contains the names of the constants that apply to the msgget() system call and their values.
Control Command | Value |
---|---|
IPC_CREAT | 0001000 |
IPC_EXCL | 0002000 |
The value for msgflg is therefore a combination of operation permissions and control commands. To accomplish this combination, perform a bitwise OR (|) on the flags with the operation permissions. The bit positions and values for the control commands in relation to those of the operation permissions make this possible.
Two examples:
msqid = msgget (key, (IPC_CREAT | MSG_R)); msqid = msgget (key, (IPC_CREAT | IPC_EXCL | 0400));The msgget() system call attempts to return a new msqid if either of the following conditions is true:
msqid = msgget (IPC_PRIVATE, msgflg);The second condition is satisfied if the value for key is not already associated with an msqid and a bitwise AND of msgflg and IPC_CREAT gives "true" (1). This means that the given key is not currently being used to refer to any message queue on the computer the program is running on, and that the IPC_CREAT flag is set in msgflg.
Note: The system tuning parameter MSGMNI determines the maximum number of unique message queues (msqids) in the UNIX operating system. Attempting to exceed MSGMNI always causes a failure. IPC_EXCL is another control flag used with IPC_CREAT to exclusively have the system call fail if, and only if, a msqid exists for the specified key provided. This is necessary to prevent the process from thinking that it has received a new (unique) msqid when it has not. In other words, when both IPC_CREAT and IPC_EXCL are specified, a new msqid is returned if the system call is successful.
Refer to the intro(2) reference page for details about the data structures associated with a message queue. The specific failure conditions, with error names, are listed there as well.
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 4-8) by including the required header files as specified by the msgget(2) reference page. Note that the errno.h header file is included instead of declaring errno as an external variable; either method works.
Variable names have been chosen to be as close as possible to those in the synopsis for the system call. Their declarations are self-explanatory. This choice of names makes the program more readable, and it is perfectly legal since the variables are local to this program.
The variables in this program and their purposes are as follows:
key | The value for the desired message queue key |
opperm | The desired operation permissions |
opperm_flags | The combination from the logical ORing of the opperm and flags variables; used in the system call to pass the msgflg argument |
msqid | The message queue identification number for a successful system call or the error code (-1) for an unsuccessful one |
The program begins by prompting for a hexadecimal key, an octal operation permissions code, and finally for the control command combinations (flags), which are selected from a menu (lines 15-32). All possible combinations are allowed even though they might not be viable. This allows observing the errors for illegal combinations.
Next, the menu selection for the flags is combined with the operation permissions, and the result is stored in the opperm_flags variable (lines 36-51).
The system call is made next, and the result is stored in the msqid variable (line 53).
Since the msqid variable now contains a valid message queue identifier or the error code (-1), it is tested to see if an error occurred (line 55). If msqid equals -1, a message indicates that an error resulted, and the external errno variable is displayed (lines 57, 58).
If no error occurred, the returned message queue identifier is displayed (line 62).
Example 2-4 : msgget() System Call Example
1 /*This is a program to illustrate the capabilities of 2 *the msgget() (message-get) system call. 3 */ 4 #include <stdio.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/msg.h> 8 #include <errno.h> 9 /*Start of main C program*/ 10 main() 11 { 12 key_t key; 13 int opperm, flags; 14 int msqid, opperm_flags; 15 /*Enter the desired key*/ 16 printf("Enter the desired key in hex = "); 17 scanf("%x", &key); 18 /*Enter the desired octal operation 19 permissions.*/ 20 printf("\nEnter the operation "); 21 printf("permissions in octal = "); 22 scanf("%o", &opperm); 23 /*Set the desired flags.*/ 24 printf("\nEnter corresponding number to\n"); 25 printf("set the desired flags:\n"); 26 printf("No flags = 0\n"); 27 printf("IPC_CREAT = 1\n"); 28 printf("IPC_EXCL = 2\n"); 29 printf("IPC_CREAT and IPC_EXCL = 3\n"); 30 printf(" Flags = "); 31 /*Get the flag(s) to be set.*/ 32 scanf("%d", &flags); 33 /*Check the values.*/ 34 printf ("\nkey =0x%x, opperm = 0%o, flags = 0%o\n", 35 key, opperm, flags); 36 /*Incorporate the control fields (flags) with 37 the operation permissions*/ 38 switch (flags) 39 { 40 case 0: /*No flags are to be set.*/ 41 opperm_flags = (opperm | 0); 42 break; 43 case 1: /*Set the IPC_CREAT flag.*/ 44 opperm_flags = (opperm | IPC_CREAT); 45 break; 46 case 2: /*Set the IPC_EXCL flag.*/ 47 opperm_flags = (opperm | IPC_EXCL); 48 break; 49 case 3: /*Set the IPC_CREAT and IPC_EXCL flags.*/ 50 opperm_flags = (opperm | IPC_CREAT | IPC_EXCL); 51 } 52 /*Call the msgget() system call.*/ 53 msqid = msgget (key, opperm_flags); 54 /*Perform the following if the call failed.*/ 55 if(msqid == -1) 56 { 57 printf ("\nThe msgget system call failed!\n"); 58 printf ("The error number was %d.\n", errno); 59 } 60 /*Return the msqid upon successful completion.*/ 61 else 62 printf ("\nThe msqid is %d.\n", msqid); 63 exit(0); 64 }