1. Overview
A message queue is a popular form of Inter-Process Communication or IPC to share messages between the threads of a process or various processes.
There are two types of message queues in Linux, the traditional System V message queues and the newer POSIX message queues.
Sometimes, we may need to get the details of a message queue, for example, the number of messages in the queue, from the command line.
In this tutorial, we’ll discuss how to get the details of a POSIX message queue from the command line. Firstly, we’ll learn about message queues. Then, we’ll look at an example of a POSIX message queue and see how to get the details.
2. Brief Information About Message Queues
A message queue looks like a linked list of messages. A process or a thread with adequate permissions can add messages to a message queue, and another thread or process with adequate permissions can read messages from the message queue.
A process can write messages to a message queue and terminate. Another process can read the messages later. Therefore, message queues have kernel persistence.
The two types of message queues in Linux, System V message queues and POSIX message queues, look like each other. However, there are also some differences.
For example, POSIX message queues can notify a process when a message is placed in an empty message queue. This isn’t possible for System V message queues.
Another difference is that a read operation from a POSIX thread returns the oldest message with the highest priority. However, System V message queues can return a message with any desired priority.
It’s possible to get information about System V message queues using ipcs or lsipc commands. For example, we can get the number of messages available in a System V message queue. But we can’t get information about POSIX message queues using the same commands.
However, it’s possible to get information about POSIX message queues using the mq_getattr() system call. We’ll see an example in the next section.
3. Code Example
We’ll use the C program, posix_mq.c, for analyzing POSIX message queues:
#include <stdio.h>
#include <mqueue.h>
#include <unistd.h>
#include <string.h>
#define NUMBER_OF_MESSAGES 5
void writer_mq(mqd_t mqd) {
struct mq_attr attr;
char str[NUMBER_OF_MESSAGES][80] = {"A", "posix", "message", "queue", "example"};
printf("Writing messages to the POSIX message queue\n\n");
for(int i = 0; i < NUMBER_OF_MESSAGES; i++) {
// Write to the POSIX message queue
mq_send(mqd, str[i], strlen(str[i]), 0);
printf("The message written to the queue: %s\n", str[i]);
// Read the number of messages in the queue
mq_getattr(mqd, &attr);
printf("Number of messages in the queue: %d\n", attr.mq_curmsgs);
}
}
void reader_mq(mqd_t mqd) {
struct mq_attr attr;
printf("Reading messages from the POSIX message queue\n\n");
for(int i = 0; i < NUMBER_OF_MESSAGES; i++) {
char buff[8192] = {'\0'};
// Read from the POSIX message queue
mq_receive(mqd, buff, attr.mq_msgsize, NULL);
printf("The message read from the queue: %s\n", buff);
// Read the number of messages in the queue
mq_getattr(mqd, &attr);
printf("Number of messages in the queue: %d\n", attr.mq_curmsgs);
}
}
void main() {
mqd_t mqd;
// Open the POSIX message queue
mqd = mq_open("/mqp", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, NULL);
// Write messages to the queue
writer_mq(mqd);
printf("\nPress enter to continue");
getchar();
// Read messages from the queue
reader_mq(mqd);
// Close the message queue
mq_close(mqd);
// Delete the corresponding file from the filesystem
mq_unlink("/mqp");
}
The program consists of three functions, namely main(), writer_mq() and reader_mq(). We’ll break down the source code and discuss it briefly in the next subsections.
3.1. Explanation of main()
We start with the creation of the POSIX message queue in main():
// Open the POSIX message queue
mqd = mq_open("/mqp", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, NULL);
The mq_open() system call creates a new POSIX message queue or opens the message queue if it already exists. In the code snippet above, the first argument of mq_open() is the name of the message queue, which is “/mqp”. This corresponds to the file /dev/mqueue/mqp in the filesystem.
We specify how to open the message queue using the second argument of mq_open(). O_RDWR specifies that we want to write to and read from the message queue. The O_CREAT flag is for creating the file if it doesn’t exist.
The third parameter of mq_open(), S_IRUSR | S_IWUSR, is for specifying the file permissions. In our case, we give read and write permissions to the owner of the file.
Finally, the last parameter of mq_open() is for specifying some attributes of the queue. If we pass NULL to it, we create the queue with the default values.
The return value of mq_open() is a message queue descriptor corresponding to the message queue. We store this descriptor in the mqd variable of type mqd_t.
Then, in the code snippet below, we write messages to the queue by calling the writer_mq() function:
// Write messages to the queue
writer_mq(mqd);
Following, we wait until the Enter key is pressed in the code snippet below:
printf("\nPress enter to continue");
getchar();
Then, we read the messages from the queue by calling the reader_mq() function:
// Read messages from the queue
reader_mq(mqd);
Finally, in the code snippet below, we close the message queue and delete the corresponding file, /dev/mqueue/mqp, from the filesystem:
// Close the message queue
mq_close(mqd);
// Delete the corresponding file from the filesystem
mq_unlink("/mqp");
3.2. Explanation of writer_mq()
The five messages that we place on the message queue are in the str variable:
char str[NUMBER_OF_MESSAGES][80] = {"A", "posix", "message", "queue", "example"};
We place the messages in the queue in a for loop using the mq_send() system call, and print the messages using printf():
// Write to the POSIX message queue
mq_send(mqd, str[i], strlen(str[i]), 0);
printf("The message written to the queue: %s\n", str[i]);
mq_send() gets the descriptor of the message queue as the first parameter.
The second and third parameters of mq_send() are the message and its length, respectively.
The fourth parameter of mq_send() is the priority of the message. Since we don’t need messages with different priorities, we specify it as 0.
After each write operation, we check the number of messages in the queue using the mq_getattr() system call, and print it using printf():
// Read the number of messages in the queue
mq_getattr(mqd, &attr);
printf("Number of messages in the queue: %d\n", attr.mq_curmsgs);
mq_getattr() gets the attributes of a message queue.
The attributes of a message queue are kept in a structure of type struct mq_attr. A message queue has four attributes:
- mq_flags: This attribute specifies the message queue flags.
- mq_maxmsg: This attribute specifies the maximum number of messages allowed on the queue. It can be set while creating the message queue using the fourth parameter of mq_open(). The default value of it is 10, which can be found in /proc/sys/fs/mqueue/msg_default.
- mq_msgsize: This attribute specifies the maximum message size. It can be set while creating the message queue using the fourth parameter of mq_open(). The default value of it is 8192, which can be found in /proc/sys/fs/mqueue/msgsize_default.
- mq_curmsgs: This attribute gives the number of messages currently queued.
3.3. Explanation of reader_mq()
We read the messages in the queue in a for loop using the mq_receive() system call, and then print the read message using printf():
// Read from the POSIX message queue
mq_receive(mqd, buff, attr.mq_msgsize, NULL);
printf("The message read from the queue: %s\n", buff);
mq_receive() reads the oldest message with the highest priority from the message queue. It gets the descriptor of the message queue as the first parameter.
mq_receive() places the read message from the queue to the buffer passed to it as its second parameter. The name of the buffer is buff in our case, which is an array of characters of length 8192.
The third parameter of mq_receive() specifies the length of the buffer. It must be as big as the maximum message size. We use the default value, which is the mq_msgsize field of the struct mq_attr.
The priority of the read message is stored in the fourth parameter of mq_receive(). As we aren’t interested in priorities, we pass it as NULL in our example.
Finally, we get the number of messages in the queue using the mq_getattr() system call, and print it using printf():
// Read the number of messages in the queue
mq_getattr(mqd, &attr);
printf("Number of messages in the queue: %d\n", attr.mq_curmsgs);
4. Building and Running the Example
We use gcc to build the example:
$ gcc -o posix_mq posix_mq.c -lrt
The name of the generated executable is posix_mq. We specify it using the -o option of gcc.
We link the executable with the real-time library, librt.so, using -lrt. The dependency of the example code on the POSIX message queue system calls might require this option.
Having generated the executable posix_mq, it’s time to run the application:
$ ./posix_mq
Writing messages to the POSIX message queue
The message written to the queue: A
Number of messages in the queue: 1
The message written to the queue: posix
Number of messages in the queue: 2
The message written to the queue: message
Number of messages in the queue: 3
The message written to the queue: queue
Number of messages in the queue: 4
The message written to the queue: example
Number of messages in the queue: 5
Press enter to continue
The program runs and expects us to press Enter to continue.
As the output of the program shows, we write five messages: A, posix, message, queue, example. After we place each message in the queue, the number of messages in the queue increases by one. The number of messages in the queue is 5 after the addition of the last message, as expected.
Now, let’s check the message queues in the system using the ipcs command:
$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
We use the -q option ipcs to list the message queues. But our message queue isn’t listed. However, this is the expected behavior as ipcs -q lists only System V message queues.
Now, let’s press Enter so that posiq_mq can continue execution:
Reading messages from the POSIX message queue
The message read from the queue: A
Number of messages in the queue: 4
The message read from the queue: posix
Number of messages in the queue: 3
The message read from the queue: message
Number of messages in the queue: 2
The message read from the queue: queue
Number of messages in the queue: 1
The message read from the queue: example
Number of messages in the queue: 0
As is apparent from the output, the process reads from the message queue starting from the oldest one. Each time it reads from the message queue, the number of messages in the queue decreases by one, as expected.
Therefore, we’re successful in checking the details of a POSIX message queue from the command line using the mq_getattr() system call.
5. Conclusion
In this article, we discussed how to get the details of a POSIX message queue from the command line.
Firstly, we learned about message queues. We learned that using the ipcs command with its -q option lists only System V message queues.
Then, we saw that we could use the mq_getattr() system call to get information about POSIX message queues. Finally, we looked at an example which uses mq_getattr().