IPC--消息队列

0. 什么是消息队列

消息队列可以认为是一个消息列表。线程可以往消息队列中放置消息或者取出消息。每个消息都是一条记录,由发送者赋予一个优先级。一个进程在往一个消息队列中写入消息之前,不需要有某个进程在该队列上等待消息到达(这跟管道和FIFO是相反的)。

消息队列具有随内核的持续性, 也就是说在内核重新启动之前,不管发消息的进程或者取消息的进程是否结束,消息队列是始终存在的。

消息队列有两种,分别是Posix消息队列和System V消息队列, 下面的例程是Posix消息队列的使用。

1. 打开(或创建)、关闭消息队列

// 1.c
#include<stdio.h>
#include<mqueue.h>

#define  FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
int main(int argc, char* argv[]) 
{
    int     flag;
    mqd_t   mdq;
    flag = O_RDWR | O_CREAT;
    if (argc < 2) {
        printf("Please input mqueue name!\n");
    }
    // NULL 表示采用默认属性
    mdq = mq_open(argv[1], flag, FILE_MODE, NULL);
    return 0;
}

以上是一个创建消息队列的例程,使用如下命令进行编译得到可执行文件a.out

gcc -g 1.c -Wall -lrt

执行以下命令,即可得到一个名为temp.1234的消息队列, 这个消息队列保存在/dev/mqueue中

./a.out /temp.1234
[root@localhost IPC]#  ll /dev/mqueue/
total 0
-rw-r--r--. 1 root root 80 Nov  9 06:59 temp.1234

注意 消息队列的名字必须以’/'开头,才能创建成功。

2. 删除消息队列

删除消息队列使用mq_unlink函数.

// 2.c
#include<stdio.h>
#include<mqueue.h>

int main(int argc, char* argv[]) 
{
    if (argc < 2) {
        printf("Please input mqueue name!\n");
    }
    int ret = mq_unlink(argv[1]);
    printf("%d\n", ret);
    return 0;
}

编译

gcc -g 2.c -Wall -lrt

执行

./a.out /temp.1234

执行完之后,可以看到刚才创建的消息队列已经被删除了。

[root@localhost IPC]# ll /dev/mqueue/
total 0

3. get/set消息队列属性

每个消息队列有四个属性,mq_getattr函数返回所有这些属性。

struct mq_attr {
    long mq_flags;
    long mq_maxmsg; //消息队列上最多可以存储的消息数目
    long mq_msgsize; //一条消息最多可以容纳多少字节
    long mq_curmsgs; //当前队列中存储的消息数量
};
// 3.c
#include<stdio.h>
#include<mqueue.h>

int main(int argc, char* argv[]) 
{
    mqd_t           mqd;
    struct mq_attr  attr;

    if (argc < 2) {
        printf("Please input mqueue name!\n");
    }
    mqd = mq_open(argv[1], O_RDONLY);

    mq_getattr(mqd, &attr);
    printf("max msgs = %ld\nmax bytes/msg = %ld\ncurrently on queue = %ld\n", 
            attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
    mq_close(mqd);
    return 0;
}

编译执行,查看我们刚才创建的消息队列的属性

gcc -g 3.c -Wall -lrt
./a.out /temp.1234
max msgs = 10
max bytes/msg = 8192
currently on queue = 0

可以看到我们创建的消息队列最多容纳10条消息,每条消息最多8192字节,当前队列上一条消息也没有。

mq_setattr函数给队列设置属性,但是只使用mq_attr结构中的mq_flags字段以设置或者清除非阻塞标志位, 这个结构的其他三个成员均被忽略。

如果希望设置最大消息数量和每条消息的最大字节数量,需要在创建队列的时候指定。

4. 以指定属性创建队列

在前面的例子当中,以默认属性创建的队列所能容纳的最大消息数量是10, 每条消息最多8192个字节。现在,我们尝试以我们自己指定的属性创建队列。

// 4.c
#include<stdio.h>
#include<mqueue.h>

#define  FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)

struct mq_attr attr;
int main(int argc, char* argv[]) 
{
    int     flag;
    mqd_t   mdq;
    flag = O_RDWR | O_CREAT;
    if (argc < 2) {
        printf("Please input mqueue name!\n");
        return -1;
    }
    attr.mq_maxmsg = 20;
    attr.mq_msgsize = 4096;
    mdq = mq_open(argv[1], flag, FILE_MODE, &attr);
    mq_close(mdq);
    return 0;
}

在这个例子当中,我们设置最大的消息数量是20, 每条消息最多4096个字节。
编译运行,并查看一下属性

max msgs = 20
max bytes/msg = 4096
currently on queue = 0

可以看到,这次我们创建了一个不同的消息队列。

5.mq_send/mq_receive函数

这两个函数用于向一个队列添加和取出消息。每个消息有一个优先级, mq_receive总是返回队列中最高优先级的最早消息, 而且该优先级可以随该消息的内容和长度一并返回。

发送

// send
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<mqueue.h>

int main(int argc, char* argv[]) 
{
    int     priorty;
    mqd_t   mqd;

    if (argc != 4) {
        printf("usage: send <name> <message> <priorty>n");
        return -1;
    }
    priorty = atoi(argv[3]); 
    mqd = mq_open(argv[1], O_WRONLY);
    int ret = mq_send(mqd, argv[2], strlen(argv[2]), priorty);
    if (ret != 0) {
        printf("send message error!\n");
    }
    mq_close(mqd);
    return 0;
}

接收

// receive
#include<stdio.h>
#include<stdlib.h>
#include<mqueue.h>

struct mq_attr attr;
int main(int argc, char* argv[]) 
{
    unsigned int     priorty, cnt;
    mqd_t   mqd;
    void*   buff;

    if (argc != 2) {
        printf("usage: mqrece <name> \n");
        return -1;
    }

    mqd = mq_open(argv[1], O_RDONLY);
    mq_getattr(mqd, &attr);
    printf("size = %ld\n", attr.mq_msgsize);
    buff = malloc(attr.mq_msgsize);
    cnt = mq_receive(mqd, buff, attr.mq_msgsize, &priorty);

    printf("received %d bytes, priority = %u\n", cnt, priorty);
    printf("message = %s\n", (char*)buff);
    mq_close(mqd);
    return 0;
}

6. 消息队列限制

消息队列的实现定义了两个限制:

  • MQ_OPEN_MAX 一个进程能够同时打开的消息队列的最大数量(Posix要求最少为8)
  • MQ_PRIO_MAX 任意消息的最大优先级值加1(Posix要求至少为32)

这两个值通常定义在<unistd.h>中,可以通过sysconf函数得到

#include<stdio.h>
#include<unistd.h>

int main() 
{
    printf("MQ_OPEN_MAX = %ld\nMQ_PRIO_MAX = %ld\n", sysconf(_SC_MQ_OPEN_MAX), sysconf(_SC_MQ_PRIO_MAX));
    return 0;
}

在linux下运行得到的结果是

MQ_OPEN_MAX = -1
MQ_PRIO_MAX = 32768

-1 表示对数量没有限制

猜你喜欢

转载自blog.csdn.net/ww1473345713/article/details/102992076