Examples of POSIX message queue functions (mq_open, mq_getattr, mq_send, mq_receive)

Overview

Message queue is a very commonly used communication method in Linux IPC. It is usually used to send message data in a specific format between different processes .

The message queue is very different from the pipes and FIFOs discussed before, mainly in the following two points:

  1. Before a process writes a message to the message queue, there is no need for a process to wait for the message to arrive on the queue. Pipes and FIFOs are the opposite. When a process writes messages to it, the pipes and FIFOs must be opened for reading. Then the kernel will generate the SIGPIPE signal.
  2. The persistence of IPC is different. The pipe and FIFO are continuous with the process . When the pipe and FIFO are closed for the last time, the data still in the pipe and FIFO will be discarded. The message queue is consistent with the kernel , that is, after a process writes a message to the message queue, and then terminates, another process can open the queue to read the message at some later time. As long as the kernel does not re-bootstrap, the message queue is not deleted .

The message queue can be thought of as a linked list. The process (thread) can write messages into it, or take out messages from it. A process can write messages to a message queue and then terminate, and another process can take these messages from the message queue at any time. It is also explained here that the message queue has persistence with the kernel, that is, the message queue will exist permanently without restarting the system.

https://blog.csdn.net/anonymalias/article/details/9799645
https://blog.csdn.net/liuhongxiangm/article/details/8716232

Need to pay attention to the following points:

1. The name of the message queue can only start with a'/', and the name cannot contain other'/'
2. The third parameter of mq_receive() indicates the length of the read message, which cannot be less than the message that can be written to the queue The maximum size, which must be greater than or equal to the size of mq_msgsize in the mq_attr structure of the queue .
3. The priority of the message: it is a number less than MQ_PRIO_MAX, the larger the value, the higher the priority. The POSIX message queue always returns the oldest message with the highest priority in the queue when mq_receive is called. If the message does not need to set the priority, you can set msg_prio to 0 in mq_send, and set msg_prio in mq_receive to NULL.
4. By default, mq_send and mq_receive are blocked for calling, which can be set to O_NONBLOCK by mq_setattr, such as:
struct mq_attr new_attr;
mq_getattr(mqID, &new_attr);//Get current attributes
new_attr.mq_flags = O_NONBLOCK;//Set Block
mq_setattr(mqID, &new_attr, NULL)//Set attributes

1 Creation and closure of POSIX message queue

The following three function interfaces are used to create, close and delete POSIX message queues:

#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, /* mode_t mode, struct mq_attr *attr */);
                       //成功返回消息队列描述符,失败返回-1
mqd_t mq_close(mqd_t mqdes);
mqd_t mq_unlink(const char *name);
		//成功返回0,失败返回-1

mq_open is used to open or create a message queue.

  1. name: Represents the name of the message queue, which complies with the POSIX IPC name rules.
  2. oflag: Indicates the way of opening, similar to that of the open function. There are required options: O_RDONLY, O_WRONLY, O_RDWR, and optional options: O_NONBLOCK, O_CREAT, O_EXCL.
  3. mode: is an optional parameter. This parameter needs to be provided only when the O_CREAT flag is contained in oflag and the message queue does not exist. Represents the default access permission. You can refer to open.
  4. attr: It is also an optional parameter, which is only required when the O_CREAT flag is contained in oflag and the message queue does not exist. This parameter is used to set certain attributes for the new queue. If it is a null pointer, the default attributes are used.

The return value of mq_open is a value of type mqd_t, which is called a message queue descriptor. In Linux 2.6.18, this type is defined as an integer:

#include <bits/mqueue.h>
typedef int mqd_t;

mq_close is used to close a message queue and the close type of a file. After closing, the message queue is not deleted from the system. When a process ends, it will automatically call to close the open message queue.

mq_unlink is used to delete a message queue. After the message queue is created, it can only be deleted by calling this function or by kernel bootstrapping. Each message queue has a reference counter that saves the number of currently open descriptors, which is the same as a file, so this function can implement a mechanism similar to the unlink function to delete a file.

The real path name created by the name of the POSIX message queue is related to the specific system implementation. For the specific POSIX IPC name rules, please refer to P14 of "UNIX Network Programming Volume 2: Interprocess Communication".

After testing, in Linux 2.6.18, the created POSIX message queue will not create a real path name in the file system. And the name of POSIX can only start with a'/', and the name cannot contain other'/'.

Create example:

#define TEST_MQ_NAME ("/test_mq")

static struct mq_attr test_mq_attr;
static mqd_t test_mq;
test_mq_attr.mq_maxmsg = LOCK_ALARM_MAX_NUM;
test_mq_attr.mq_msgsize = LOCK_ALARM_MAX_SIZE;
mq_unlink(TEST_MQ_NAME );
test_mq = mq_open(TEST_MQ_NAME , O_RDWR | O_CREAT | O_EXCL | O_NONBLOCK, 0644, &lock_mq_attr);
//后面两个参数为可选参数

mq_open:
https://www.cnblogs.com/LubinLew/p/POSIX-mq_open.html

2 Attributes of POSIX message queue

The POSIX standard stipulates that the message queue attribute mq_attr must contain the following four contents:

long    mq_flags //消息队列的标志:0或O_NONBLOCK,用来表示是否阻塞 
long    mq_maxmsg  //消息队列的最大消息数
long    mq_msgsize  //消息队列中每个消息的最大字节数
long    mq_curmsgs  //消息队列中当前的消息数目

The definition of mq_attr structure in Linux 2.6.18 is as follows:

#include <bits/mqueue.h>
struct mq_attr
{
  long int mq_flags;      /* Message queue flags.  */
  long int mq_maxmsg;   /* Maximum number of messages.  */
  long int mq_msgsize;   /* Maximum message size.  */
  long int mq_curmsgs;   /* Number of messages currently queued.  */
  long int __pad[4];
};
  • The attribute setting and acquisition of the queue can be achieved through the following two functions:
#include <mqueue.h>
mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);

mq_getattr is used to obtain the attributes of the current message queue, and mq_setattr is used to set the attributes of the current message queue. The oldattr in mq_setattr is used to save the attributes of the message queue before modification and can be empty.

The only attribute that mq_setattr can set is mq_flags , which is used to set or clear the non-blocking flag of the message queue. The other attributes of the newattr structure are ignored. The mq_maxmsg and mq_msgsize attributes can only be set through mq_open when creating a message queue. mq_open will only set these two attributes and ignore the other two attributes. The mq_curmsgs attribute can only be obtained but not set.

Here is the test code:

#include <iostream>
#include <cstring>
 
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <mqueue.h>
 
using namespace std;
 
int main()
{
    mqd_t mqID;
    mqID = mq_open("/testmQueue", O_RDWR | O_CREAT, 0666, NULL);
 
    if (mqID < 0)
    {
        cout<<"open message queue error..."<<strerror(errno)<<endl;
        return -1;
    }
 
    mq_attr mqAttr;
    if (mq_getattr(mqID, &mqAttr) < 0)
    {
        cout<<"get the message queue attribute error"<<endl;
        return -1;
    }
 
    cout<<"mq_flags:"<<mqAttr.mq_flags<<endl;
    cout<<"mq_maxmsg:"<<mqAttr.mq_maxmsg<<endl;
    cout<<"mq_msgsize:"<<mqAttr.mq_msgsize<<endl;
    cout<<"mq_curmsgs:"<<mqAttr.mq_curmsgs<<endl;
}

The result of execution in Linux 2.6.18 is:

mq_flags:0
mq_maxmsg:10
mq_msgsize:8192
mq_curmsgs:0

3 Use of POSIX message queue

The POSIX message queue can send and receive messages through the following two functions:

#include <mqueue.h>
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr,
                      size_t msg_len, unsigned msg_prio);
                     //成功返回0,出错返回-1
 
mqd_t mq_receive(mqd_t mqdes, char *msg_ptr,
                      size_t msg_len, unsigned *msg_prio);
                     //成功返回接收到消息的字节数,出错返回-1
 
#ifdef __USE_XOPEN2K
mqd_t mq_timedsend(mqd_t mqdes, const char *msg_ptr,
                      size_t msg_len, unsigned msg_prio,
                      const struct timespec *abs_timeout);
 
mqd_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
                      size_t msg_len, unsigned *msg_prio,
                      const struct timespec *abs_timeout);
#endif

mq_send writes a message to the message queue, and mq_receive reads a message from the message queue.

  1. mqdes: message queue descriptor;
  2. msg_ptr: pointer to the message body buffer;
  3. msg_len: The length of the message body. The parameter of mq_receive cannot be less than the maximum size of the message that can be written in the queue, that is, it must be greater than or equal to the size of mq_msgsize in the mq_attr structure of the queue. If the msg_len in mq_receive is less than this value, an EMSGSIZE error will be returned. The length of the message sent by the POXIS message queue can be 0.
  4. msg_prio: the priority of the message; it is a number less than MQ_PRIO_MAX, the larger the value, the higher the priority. The POSIX message queue always returns the oldest message with the highest priority in the queue when mq_receive is called. If the message does not need to set the priority, you can set msg_prio to 0 in mq_send, and set msg_prio in mq_receive to NULL.

There are also two XSI-defined extended interface functions for sending and receiving messages within a limited time: mq_timedsend and mq_timedreceive functions. By default, mq_send and mq_receive are blocked for calling, which can be set to O_NONBLOCK by mq_setattr.

The following is the test code used by the message queue:

#include <iostream>
#include <cstring>
#include <errno.h>
 
#include <unistd.h>
#include <fcntl.h>
#include <mqueue.h>
 
using namespace std;
 
int main()
{
    mqd_t mqID;
    mqID = mq_open("/testmQueue", O_RDWR | O_CREAT | O_EXCL, 0666, NULL);
 
    if (mqID < 0)
    {
        if (errno == EEXIST)
        {
            mq_unlink("/testmQueue");
            mqID = mq_open("/testmQueue", O_RDWR | O_CREAT, 0666, NULL);
        }
        else
        {
            cout<<"open message queue error..."<<strerror(errno)<<endl;
            return -1;
        }
    }
 
    if (fork() == 0)
    {
        mq_attr mqAttr;
        mq_getattr(mqID, &mqAttr);
 
        char *buf = new char[mqAttr.mq_msgsize];
 
        for (int i = 1; i <= 5; ++i)
        {
            if (mq_receive(mqID, buf, mqAttr.mq_msgsize, NULL) < 0)
            {
                cout<<"receive message  failed. ";
                cout<<"error info:"<<strerror(errno)<<endl;
                continue;
            }
 
            cout<<"receive message "<<i<<": "<<buf<<endl;   
        }
        exit(0);
    }
 
    char msg[] = "yuki";
    for (int i = 1; i <= 5; ++i)
    {
        if (mq_send(mqID, msg, sizeof(msg), i) < 0)
        {
            cout<<"send message "<<i<<" failed. ";
            cout<<"error info:"<<strerror(errno)<<endl;
        }
        cout<<"send message "<<i<<" success. "<<endl;   
 
        sleep(1);
    }
} 

The execution structure under Linux 2.6.18 is as follows:

send message 1 success. 
receive message 1: yuki
send message 2 success. 
receive message 2: yuki
send message 3 success. 
receive message 3: yuki
send message 4 success. 
receive message 4: yuki
send message 5 success. 
receive message 5: yuki

4 Limitations of POSIX message queues

The limits of the POSIX message queue itself are mq_maxmsg and mq_msgsize in mq_attr, which are used to limit the maximum number of messages in the message queue and the maximum number of bytes per message, respectively. As mentioned earlier, these two parameters can be set when calling mq_open to create a message queue. When this setting is restricted by the system kernel.

The following are the shell limits on the size of the POSIX message queue of the startup process under Linux 2.6.18:

# ulimit -a |grep message
POSIX message queues     (bytes, -q) 819200

The limit size is 800KB, which is the size of the entire message queue, not only the maximum number of messages * the maximum size of the message; it also includes the additional overhead of the message queue. Earlier we know that the default maximum number of messages and the maximum size of messages in the POSIX message queue under Linux 2.6.18 are:

mq_maxmsg = 10
mq_msgsize = 8192

To illustrate that the above limit size includes the additional overhead of the message queue, here is the test code:

#include <iostream>
#include <cstring>
#include <errno.h>
 
#include <unistd.h>
#include <fcntl.h>
#include <mqueue.h>
 
using namespace std;
 
int main(int argc, char **argv)
{
    mqd_t mqID;
    mq_attr attr;
    attr.mq_maxmsg = atoi(argv[1]);
    attr.mq_msgsize = atoi(argv[2]);
 
    mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT | O_EXCL, 0666, &attr);
 
    if (mqID < 0)
    {
        if (errno == EEXIST)
        {
            mq_unlink("/anonymQueue");
            mqID = mq_open("/anonymQueue", O_RDWR | O_CREAT, 0666, &attr);
 
            if(mqID < 0)
            {
                cout<<"open message queue error..."<<strerror(errno)<<endl;
                return -1;
            }
        }
        else
        {
            cout<<"open message queue error..."<<strerror(errno)<<endl;
            return -1;
        }
    }
 
    mq_attr mqAttr;
    if (mq_getattr(mqID, &mqAttr) < 0)
    {
        cout<<"get the message queue attribute error"<<endl;
        return -1;
    }
 
    cout<<"mq_flags:"<<mqAttr.mq_flags<<endl;
    cout<<"mq_maxmsg:"<<mqAttr.mq_maxmsg<<endl;
    cout<<"mq_msgsize:"<<mqAttr.mq_msgsize<<endl;
    cout<<"mq_curmsgs:"<<mqAttr.mq_curmsgs<<endl; 
}

The following is to set the maximum number of messages and the maximum size of messages when creating a message queue to test:

[root@idcserver program]# g++ -g test.cpp -lrt
[root@idcserver program]# ./a.out 10 81920
open message queue error...Cannot allocate memory
[root@idcserver program]# ./a.out 10 80000
open message queue error...Cannot allocate memory
[root@idcserver program]# ./a.out 10 70000
open message queue error...Cannot allocate memory
[root@idcserver program]# ./a.out 10 60000
mq_flags:0
mq_maxmsg:10
mq_msgsize:60000
mq_curmsgs:0

It can be seen from the above that the size of the message data stored in the message queue is not 819200B. The number of messages that can be accommodated in the message queue can be changed by modifying the limit parameter. You can modify the restrictions in the following ways, but this will become invalid after the shell startup process ends. You can write the settings into the startup script for execution, such as .bashrc, rc.local.

[root@idcserver ~]# ulimit -q 1024000000
[root@idcserver ~]# ulimit -a |grep message
POSIX message queues     (bytes, -q) 1024000000

Let's test the properties of the message queue that can be set again.

[root@idcserver program]# ./a.out 10 81920
mq_flags:0
mq_maxmsg:10
mq_msgsize:81920
mq_curmsgs:0
[root@idcserver program]# ./a.out 10 819200
mq_flags:0
mq_maxmsg:10
mq_msgsize:819200
mq_curmsgs:0
[root@idcserver program]# ./a.out 1000 8192  
mq_flags:0
mq_maxmsg:1000
mq_msgsize:8192
mq_curmsgs:0

There are two other limitations in the implementation of POSIX message queues:

MQ_OPEN_MAX : The maximum number of message queues that a process can open at the same time, POSIX requires at least 8;

MQ_PRIO_MAX : The maximum priority of the message, POSIX requires at least 32;

Reference materials:

https://blog.csdn.net/anonymalias/article/details/9799645

https://blog.csdn.net/lingfeng2019/article/details/72417007

http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380147d8c8c4668d4e419ce3b4c413037bfa6663f405a8e906b6075a8425ebaa731723c0123b59b8d8c0997ac925f75ce786a6459db0144dc46ecdc4755d627e44de8df58b0e6a6&p=c2769a479f8002ff57ee9579505c82&newp=882a9644d18717dd0be296261641c4231610db2151d6d1156b82c825d7331b001c3bbfb423271206d0c27d6504ad4e59eff03174310123a3dda5c91d9fb4c57479cc7146&user=baidu&fm=sc&query=mq_send&qid=d127ac9c0006de51&p1=1

Guess you like

Origin blog.csdn.net/qingzhuyuxian/article/details/107233807