POSIX 메시지 큐 함수의 예 (mq_open, mq_getattr, mq_send, mq_receive)

개요

메시지 큐는 Linux IPC에서 매우 일반적으로 사용되는 통신 방법으로, 일반적으로 서로 다른 프로세스간에 특정 형식으로 메시지 데이터 를 보내는 데 사용됩니다 .

메시지 대기열은 주로 다음 두 가지 점에서 앞서 논의한 파이프 및 FIFO와 매우 다릅니다.

  1. 프로세스가 메시지 대기열에 메시지를 쓰기 전에 프로세스가 메시지가 대기열에 도착할 때까지 기다릴 필요가 없습니다. 파이프와 FIFO는 반대입니다. 프로세스가 메시지를 쓸 때 파이프와 FIFO를 열어서 읽어야합니다. 그러면 커널이 SIGPIPE 신호를 생성합니다.
  2. IPC의 지속성은 다릅니다. 파이프와 선입 선출 (FIFO)은이 과정을 계속 하며, 선 입선과 선입 선출 (FIFO)이 마지막으로 닫히면 파이프와 선입 선출 (FIFO)에 남아있는 데이터는 폐기됩니다. 메시지 큐는 커널과 연속적 입니다. 즉, 프로세스가 메시지 큐에 메시지를 쓰고 종료 한 후 다른 프로세스가 큐를 열어 나중에 메시지를 읽을 수 있습니다. 커널이 재부트 스트랩하지 않는 한 메시지 대기열은 삭제되지 않습니다 .

메시지 큐는 연결된 목록으로 생각할 수 있습니다. 프로세스 (스레드)는 메시지를 작성하거나 메시지를 가져올 수 있습니다. 프로세스는 메시지 큐에 메시지를 쓴 다음 종료 할 수 있으며 다른 프로세스는 언제든지 메시지 큐에서 이러한 메시지를 가져올 수 있습니다. 또한 여기서는 메시지 큐가 커널과 함께 지속된다는 점을 설명합니다. 즉, 메시지 큐는 시스템을 다시 시작하지 않고도 영원히 존재합니다.

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

다음 사항에주의해야합니다.

1. 메시지 큐의 이름은 '/'로만 시작할 수 있으며 이름은 '/'를 포함 할 수 없습니다
. 2. mq_receive ()의 세 번째 매개 변수는 읽은 메시지의 길이를 나타내며 큐에 쓸 수있는 메시지보다 작을 수 없습니다. 대기열의 mq_attr 구조에있는 mq_msgsize의 크기보다 크거나 같아야하는 최대 크기 .
3. 메시지의 우선 순위 : MQ_PRIO_MAX보다 작은 숫자, 값이 클수록 우선 순위가 높습니다. POSIX 메시지 큐는 mq_receive가 호출 될 때 항상 큐에서 가장 높은 우선 순위를 가진 가장 오래된 메시지를 리턴합니다. 메시지가 우선 순위를 설정할 필요가없는 경우 mq_send에서 msg_prio를 0으로 설정하고 mq_receive에서 msg_prio를 NULL로 설정할 수 있습니다.
4. 기본적으로 mq_send 및 mq_receive는 호출을 위해 차단되며, 다음과 같이 mq_setattr에 의해 O_NONBLOCK으로 설정할 수 있습니다.
struct mq_attr new_attr;
mq_getattr (mqID, & new_attr); // Get current attributes
new_attr.mq_flags = O_NONBLOCK; // Set 블록
mq_setattr (mqID, & new_attr, NULL) // 속성 설정

1 POSIX 메시지 대기열 생성 및 종료

다음 세 가지 기능 인터페이스는 POSIX 메시지 대기열을 생성, 닫기 및 삭제하는 데 사용됩니다.

#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은 메시지 대기열을 열거 나 만드는 데 사용됩니다.

  1. name : POSIX IPC 이름 규칙을 준수하는 메시지 큐의 이름을 나타냅니다.
  2. oflag : 열기 기능과 유사한 개방 방법을 나타냅니다. 필수 옵션 : O_RDONLY, O_WRONLY, O_RDWR 및 선택적 옵션 : O_NONBLOCK, O_CREAT, O_EXCL.
  3. mode : 선택적 매개 변수이며이 매개 변수는 O_CREAT 플래그가 oflag에 포함되어 있고 메시지 큐가없는 경우에만 제공되어야합니다. 기본 액세스 권한을 나타냅니다. 오픈을 참고할 수 있습니다.
  4. attr : O_CREAT 플래그가 oflag에 포함되어 있고 메시지 큐가없는 경우에만 필요한 선택적 매개 변수이기도합니다. 이 매개 변수는 새 대기열에 대한 특정 속성을 설정하는 데 사용됩니다. 널 포인터 인 경우 기본 속성이 사용됩니다.

mq_open의 리턴 값은 메시지 큐 설명 자라고하는 mqd_t 유형의 값입니다. Linux 2.6.18에서이 유형은 정수로 정의됩니다.

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

mq_close는 메시지 큐와 파일의 닫기 유형을 닫는 데 사용되며, 닫은 후에도 메시지 큐는 시스템에서 삭제되지 않습니다. 프로세스가 종료되면 열려있는 메시지 대기열을 닫기 위해 자동으로 호출됩니다.

mq_unlink는 메시지 큐를 삭제하는 데 사용됩니다. 메시지 큐가 생성 된 후에는이 함수를 호출하거나 커널 부트 스트랩을 통해서만 삭제할 수 있습니다. 각 메시지 큐에는 파일과 동일한 현재 열려있는 설명 자의 수를 저장하는 참조 카운터가 있으므로이 함수는 파일을 삭제하는 링크 해제 기능과 유사한 메커니즘을 구현할 수 있습니다.

POSIX 메시지 대기열의 이름으로 생성 된 실제 경로 이름은 특정 시스템 구현과 관련이 있습니다. 특정 POSIX IPC 이름 규칙에 대해서는 "UNIX 네트워크 프로그래밍 볼륨 2 : 프로세스 간 통신"의 P14를 참조하십시오.

테스트 후 Linux 2.6.18에서 생성 된 POSIX 메시지 대기열은 파일 시스템에 실제 경로 이름을 생성하지 않습니다. 그리고 POSIX의 이름은 '/'로만 시작할 수 있으며 이름은 다른 '/'를 포함 할 수 없습니다.

예제 만들기 :

#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 POSIX 메시지 큐의 속성

POSIX 표준은 메시지 큐 속성 mq_attr이 다음 네 가지 컨텐츠를 포함해야한다고 규정합니다.

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

Linux 2.6.18에서 mq_attr 구조의 정의는 다음과 같습니다.

#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];
};
  • 큐의 속성 설정 및 획득은 다음 두 가지 기능을 통해 수행 할 수 있습니다.
#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은 현재 메시지 큐의 속성을 가져 오는 데 사용되며 mq_setattr은 현재 메시지 큐의 속성을 설정하는 데 사용됩니다. mq_setattr의 oldattr은 수정하기 전에 메시지 큐의 속성을 저장하는 데 사용되며 비어있을 수 있습니다.

mq_setattr 가 설정할 수 있는 유일한 속성 은 메시지 큐의 비 차단 플래그를 설정하거나 지우는 데 사용되는 mq_flags 입니다. newattr 구조의 다른 속성은 무시됩니다. mq_maxmsg 및 mq_msgsize 속성은 메시지 큐를 작성할 때 mq_open을 통해서만 설정할 수 있습니다. mq_open은이 두 속성 만 설정하고 다른 두 속성은 무시합니다. mq_curmsgs 속성은 얻을 수만 있고 설정할 수는 없습니다.

다음은 테스트 코드입니다.

#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;
}

Linux 2.6.18에서 실행 한 결과는 다음과 같습니다.

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

3 POSIX 메시지 큐 사용

POSIX 메시지 큐는 다음 두 가지 기능을 통해 메시지를 보내고받을 수 있습니다.

#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 는 메시지를 메시지 큐에 쓰고 mq_receive 는 메시지 큐에서 메시지를 읽습니다.

  1. mqdes : 메시지 큐 설명자;
  2. msg_ptr : 메시지 본문 버퍼에 대한 포인터;
  3. msg_len : 메시지 본문의 길이입니다. mq_receive의 매개 변수는 큐에 쓸 수있는 메시지의 최대 크기보다 작을 수 없습니다 . 즉, 큐의 mq_attr 구조에서 mq_msgsize의 크기보다 크거나 같아야합니다. mq_receive의 msg_len이이 값보다 작 으면 EMSGSIZE 오류가 리턴됩니다. POXIS 메시지 큐에서 보낸 메시지의 길이는 0이 될 수 있습니다.
  4. msg_prio : 메시지의 우선 순위, MQ_PRIO_MAX보다 작은 숫자, 값이 클수록 우선 순위가 높습니다. POSIX 메시지 큐는 mq_receive가 호출 될 때 항상 큐에서 가장 높은 우선 순위를 가진 가장 오래된 메시지를 리턴합니다. 메시지가 우선 순위를 설정할 필요가없는 경우 mq_send에서 msg_prio를 0으로 설정하고 mq_receive에서 msg_prio를 NULL로 설정할 수 있습니다.

또한 제한된 시간 내에 메시지를 보내고 받기위한 두 개의 XSI 정의 확장 인터페이스 기능이 있습니다. mq_timedsend 및 mq_timedreceive 기능입니다. 기본적으로 mq_send 및 mq_receive는 호출을 위해 차단되며 mq_setattr에 의해 O_NONBLOCK으로 설정 될 수 있습니다.

다음은 메시지 큐에서 사용하는 테스트 코드입니다.

#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);
    }
} 

Linux 2.6.18에서의 실행 구조는 다음과 같습니다.

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 POSIX 메시지 큐의 한계

POSIX 메시지 큐 자체의 한계는 mq_attr의 mq_maxmsg 및 mq_msgsize이며, 각각 메시지 큐의 최대 메시지 수와 메시지 당 최대 바이트 수를 제한하는 데 사용됩니다. 앞서 언급했듯이이 두 매개 변수는 mq_open을 호출하여 메시지 대기열을 만들 때 설정할 수 있습니다. 이 설정이 시스템 커널에 의해 제한되는 경우.

다음은 Linux 2.6.18에서 시작 프로세스의 POSIX 메시지 대기열 크기에 대한 셸 제한입니다.

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

제한 크기는 800KB로 전체 메시지 큐의 크기이며 최대 메시지 수 * 메시지의 최대 크기뿐 아니라 메시지 큐의 추가 오버 헤드도 포함합니다. 이전에 Linux 2.6.18에서 POSIX 메시지 대기열의 기본 최대 메시지 수와 메시지의 최대 크기는 다음과 같습니다.

mq_maxmsg = 10
mq_msgsize = 8192

위의 제한 크기에 메시지 대기열의 추가 오버 헤드가 포함되어 있음을 설명하기 위해 다음은 테스트 코드입니다.

#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; 
}

다음은 메시지 대기열을 만들 때 테스트 할 최대 메시지 수와 메시지의 최대 크기를 설정하는 것입니다.

[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

위에서 살펴보면 메시지 큐에 저장된 메시지 데이터의 크기가 819200B가 아님을 알 수 있습니다. 제한 매개 변수를 수정하여 메시지 큐에 수용 할 수있는 메시지 수를 변경할 수 있습니다. 다음과 같은 방법으로 제한을 수정할 수 있지만 셸 시작 프로세스가 종료되면 무효화됩니다. .bashrc, rc.local과 같은 실행을 위해 시작 스크립트에 설정을 쓸 수 있습니다.

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

설정할 수있는 메시지 큐의 속성을 다시 테스트 해 보겠습니다.

[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

POSIX 메시지 큐 구현에는 두 가지 다른 제한 사항이 있습니다.

MQ_OPEN_MAX : 프로세스가 동시에 열 수있는 최대 메시지 큐 수, POSIX에는 최소 8 개가 필요합니다.

MQ_PRIO_MAX : 메시지의 최대 우선 순위, POSIX는 최소한 32가 필요합니다.

참고 자료 :

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

추천

출처blog.csdn.net/qingzhuyuxian/article/details/107233807