Message Queue
Now we will discuss the third and last System v ipv tool: Message queue. In many ways, message queues are similar to famous pipelines, but they are not associated with opening or closing pipelines. However, the use of message queues does not solve the problems we encounter when using famous pipelines, such as blocking on pipelines.
Message Queue provides a simple and efficient way to transmit data between two unrelated processes. Compared with the famous Pipeline, the advantages of message queue are independent of the sending and receiving processes, which reduces the difficulty of synchronizing between opening and closing famous pipelines.
Message Queue provides a method for sending block data to another process by one process. In addition, each data block is regarded as a type, and the receiving process can independently receive data blocks of different types. The advantage of message queue is that we can almost completely avoid synchronization problems and shield famous pipelines by sending messages. Better, we can use some emergency methods to send messages. The disadvantage is that, like pipelines, each data block has a maximum size limit, and the block size limit on all message queues in the system also exists.
Despite these limitations, the X/open specification does not define the specific values of these limits, except that exceeding these dimensions is the cause of some failed Message Queue functions. Linux has two definitions: msgmax and msgmnb, which respectively define the maximum size of a single message and a queue. These macros may not exist in other systems.
The message queue function is defined as follows:
# Include <sys/msg. h>
Int msgctl (INT msqid, int cmd, struct msqid_ds * BUF );
Int msgget (key_t key, int msgflg );
Int msgrcv (INT msqid, void * msg_ptr, size_t msg_sz, long int msgtype, int msgflg );
Int msgsnd (INT msqid, const void * msg_ptr, size_t msg_sz, int msgflg );
Like information numbers and shared memory, the header files sys/types. h and sys/IPC. h are also required.
Msgget
We can use the msgget function to create and access a message queue:
Int msgget (key_t key, int msgflg );
Similar to other IPC tools, a program must provide a key value that specifies a specific message queue. The special value ipc_private creates a private queue, which can only be accessed by the current process theoretically. Like information and shared memory, message queues are not private in some Linux systems. Because private queues are rarely used, this is not a serious problem. As before, the second parameter, msgflg, consists of nine permission tags. To create a new message queue, the special bits of ipc_creat must perform or operate with other permission bits. It is not an error to set the ipc_creat tag and specify an existing message queue. If a Message Queue already exists, the ipc_creat tag is ignored.
If the operation succeeds, the msgget function returns a positive number as the queue identifier. If the operation fails,-1 is returned.
Msgsnd
The msgsnd function allows us to add messages to a message queue:
Int msgsnd (INT msqid, const void * msg_ptr, size_t msg_sz, int msgflg );
The message structure is limited by two methods. First, it must be smaller than the system limit. Second, it must start with a long int, which will be used as a message type in the receiving Letter Number. We recommend that you define the message structure as follows when using messages:
Struct my_message {
Long int message_type;
/* The data you wish to transfer */
}
Because message_type is used to receive messages, we cannot simply ignore it. We must define our own data structure to include and initialize it so that it can contain a known value.
The first parameter, msgid, is the message queue identifier returned by the msgget function.
The second parameter, msg_ptr, is a pointer to the message to be sent. As described earlier, the message must start with the long int type.
The third parameter, msg_sz, is the size of the message directed by msg_ptr. This dimension must not contain the long int message type.
The fourth parameter, msgflg, controls how to handle a message when the current message queue is full or has reached the System Limit of the queue message. If ipc_nowait is set for the msgflg flag, the function returns immediately without sending a message, and the returned value is-1. If the msgflg flag clears the ipc_nowait flag, the sending process will be suspended, waiting for available space in the queue.
If the call succeeds, the function returns 0. If the call fails, the return value is-1. If the call is successful, the system will complicate a message data and put it into the message queue.
Msgrcv
The msgrcv function collects messages from a message queue:
Int msgrcv (INT msqid, void * msg_ptr, size_t msg_sz, long int msgtype, int msgflg );
The first parameter, msqid, is the message queue identifier returned by the msgget function.
The second parameter, msg_ptr, is a pointer to the message to be received. As described in the msgsnd function, the message must start with the long int type.
The third parameter, msg_sz, is the size of the message directed by msg_ptr and does not contain the long int message type.
The fourth parameter, msgtype, is a long int type that allows an implementation in the form of receiving priority. If the msgtype value is 0, the first available message in the queue will be received. If the value is greater than 0, the first message with the same message type will be received. If the value is less than 0, the first message with the same type or smaller than the absolute value of msgtype will be received.
This sounds much more complicated than the actual operation. If we simply want to receive messages in the order they are sent, we can set msgtype to 0. If we want to receive messages of special message types, we can set msgtype to equal to this value. If we want to receive a message whose type is N or less than N, we can set msgtype to-n.
The fifth parameter, msgflg, controls how messages are processed when there is no suitable type of message waiting to be received. If ipc_nowait bit is set in msgflg, the call will return immediately, and the return value is-1. If the ipc_nowait bit is removed from the msgflg tag, the process will be suspended, waiting for a suitable type of message to arrive.
If the request succeeds, msgrcv returns the number of bytes in the receiving buffer. The message is copied to the user allocation buffer pointed by msg_ptr, and the data is deleted from the message queue. If it fails,-1 is returned.
Msgctl
The last message queue function is msgctl, which is very similar to the control function in the shared memory.
Int msgctl (INT msqid, int command, struct msqid_ds * BUF );
The msqid_ds structure must contain at least the following members:
Struct msqid_ds {
Uid_t msg_perm.uid;
Uid_t msg_perm.gid
Mode_t msg_perm.mode;
}
The first parameter, msqid, is a token returned by the msgget function.
The second parameter, command, is the action to be executed. He can take the following three values:
Command description
Ipc_stat sets the data in the msqid_ds structure to reflect the values associated with the message queue.
Ipc_set if the process has the permission to do so, this command sets the value associated with the message queue provided in the msqid_ds data structure.
Ipc_rmid: deletes a message queue.
If it succeeds, 0 is returned. If it fails,-1 is returned. If the message queue is deleted when the process is waiting for msgsnd or msgrcv function, the sending or receiving function fails.
Test-Message Queue
Now we have understood the definition of message queue. Let's take a look at how they actually work. As before, we will write two programs: msg1.c for receiving and msg2.c for sending. We allow any program to create a message queue, but use the receiver to delete the Message Queue after receiving the last message.
1 The following is the receiving program:
# Include <stdio. h>
# Include <stdlib. h>
# Include <string. h>
# Include <errno. h>
# Include <unistd. h>
# Include <sys/types. h>
# Include <sys/IPC. h>
# Include <sys/msg. h>
Struct my_msg_st
{
Long int my_msg_type;
Char some_text [bufsiz];
};
Int main ()
{
Int running = 1;
Int msgid;
Struct my_msg_st some_data;
Long int msg_to_receive = 0;
2 first, we set the Message Queue:
Msgid = msgget (key_t) 1234,0666 | ipc_creat );
If (msgid =-1)
{
Fprintf (stderr, "msgget failed with error: % d/N", errno );
Exit (exit_failure );
}
3. Then, receive the message in the message queue until an end message is encountered. Finally, the message queue is deleted:
While (running)
{
If (msgrcv (msgid, (void *) & some_data, bufsiz, msg_to_receive, 0) =-1)
{
Fprintf (stderr, "msgrcv failed with errno: % d/N", errno );
Exit (exit_failure );
}
Printf ("You wrote: % s", some_data.some_text );
If (strncmp (some_data.some_text, "end", 3) = 0)
{
Running = 0;
}
}
If (msgctl (msgid, ipc_rmid, 0) =-1)
{
Fprintf (stderr, "msgctl (ipc_rmid) failed/N ");
Exit (exit_failure );
}
Exit (exit_success );
}
4. The sender program is similar to msg1.c. In the main function, delete the msg_to_receive declaration and replace it with buffer [bufsiz]. Remove the Message Queue deletion code and make the following changes in the running cycle. Now we call msgsnd to send the input text to the queue.
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>
# Include <string. h>
# Include <errno. h>
# Include <sys/types. h>
# Include <sys/IPC. h>
# Include <sys/msg. h>
# Define max_text 512
Struct my_msg_st
{
Long int my_msg_type;
Char some_text [max_text];
};
Int main ()
{
Int running = 1;
Struct my_msg_st some_data;
Int msgid;
Char buffer [bufsiz];
Msgid = msgget (key_t) 1234,066 6 | ipc_creat );
If (msgid =-1)
{
Fprintf (stderr, "msgget failed with errno: % d/N", errno );
Exit (exit_failure );
}
While (running)
{
Printf ("Enter some text :");
Fgets (buffer, bufsiz, stdin );
Some_data.my_msg_type = 1;
Strcpy (some_data.some_text, buffer );
If (msgsnd (msgid, (void *) & some_data, max_text, 0) =-1)
{
Fprintf (stderr, "msgsnd failed/N ");
Exit (exit_failure );
}
If (strncmp (buffer, "end", 3) = 0)
{
Running = 0;
}
}
Exit (exit_success );
}
Unlike the example in the pipeline, the process does not have to provide its own synchronization mechanism. This is a great advantage of message queues over pipelines.
If the message queue has space, the sender can create a queue, put some data in the queue, and even exit before the receiver starts. We will first run the sender. The following is an example output:
$./Msg2
Enter some text: Hello
Enter some text: How are you today?
Enter some text: End
$./Msg1
You wrote: Hello
You wrote: How are you today?
You wrote: End
$
Working Principle
The sender program uses msgget to create a message queue, and then uses the msgsnd function to add messages to the queue. The receiver uses msgget to obtain the Message Queue identifier and receives the message until it receives the Special Message end. Then he will use msgctl to delete the message queue for some cleanup work.