第三十四章 POSIX消息队列

POSIX消息队列相关函数

mq_open

功能:
    用来创建和访问一个消息队列
原型:
    mqd_t mq_open(const char *name, int oflag); //只能用来打开消息队列,不能用来创建消息队列
    mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
参数:
    name : 某个消息队列的名字
        1. 必须以/开头,并且后续不能有其它/,形如/somename
        2. 长度不能超过NAME_MAX
    oflag : 与open函数类似,可以是O_RDONLY、O_WRONLY、O_RDWR、O_CREAT、O_EXCL、O_NONBLOCK等
    mode :如果oflag指定了O_CREAT,需要设置mode
    attr : 指定消息队列的属性
    Link with -lrt.
返回值:
    成功 : 返回消息队列文件描述符
    失败 : -1

mq_close

功能:
    关闭消息队列(并不代表删除一个消息队列,仅仅只是关闭了消息队列描述符,不能再通过描述符去访问消息队列)
原型:
    mqd_t mq_close(mqd_t mqdes); 
参数:
    mqdes : 消息队列描述符
返回值:
    成功 : 0
    失败 : -1
功能:
    删除消息队列(删除一个连接数,当连接数为0的时候,才真正将文件删除)
原型:
    mqd_t mq_unlink(const char* name); 
参数:
    name : 消息队列名字
返回值:
    成功 : 0
    失败 : -1

mq_getattr|mq_setattr

功能:
    获取、设置消息队列属性
原型:
    int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
    int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
参数:
    mqdes : 消息队列的描述字
    attr :获取消息队列的属性放到这个结构体中
    newattr : 需要设置的新属性
    oldattr : 返回原来的属性

        struct mq_attr {
                long mq_flags;       /* Flags: 0 or O_NONBLOCK */
                long mq_maxmsg;      /* Max. # of messages on queue */
                long mq_msgsize;     /* Max. message size (bytes) */
                long mq_curmsgs;     /* # of messages currently in queue */
            };

返回值:
    成功 : 0
    失败 : -1


Link with -lrt.

mq_send

功能:
    发送消息
原型:
    int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
参数:
    mqdes : 消息队列的描述字
    msg_ptr :指向消息的指针
    msg_len : 消息的长度
    msg_prio : 消息的优先级

返回值:
    成功 : 0
    失败 : -1

mq_receive

功能:
    接收消息
原型:
    ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
参数:
    mqdes : 消息队列的描述字
    msg_ptr :接收消息的指针
    msg_len : 消息的长度(这个长度一定要等于消息队列中每条消息的最大长度)
    msg_prio : 消息的优先级
 
返回值:
    成功 : 返回接收到的消息字节数
    失败 : -1

注意:
    返回指定消息队列中最高优先级的最早消息

mq_notify

功能:
    建立或者删除消息到达通知事件(system V没有这个能力,也就是消息队列从空到有消息这样的事件会被通知到某个进程,只要这个进程注册了这种通知事件,就可以被通知)
原型:
    int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
参数:
    mqdes : 消息队列的描述字
    sevp 
        非空表示当消息到达且消息队列先前为空,那么将得到通知
        NULL表示撤销已注册的通知事件

        union sigval {          /* Data passed with notification */
            int     sival_int;         /* Integer value */
            void   *sival_ptr;         /* Pointer value */
        };

       struct sigevent {
           int          sigev_notify; /* Notification method */
           int          sigev_signo;  /* Notification signal */
           union sigval sigev_value;  /* Data passed with notification */
           void       (*sigev_notify_function) (union sigval);  /* Function used for thread notification (SIGEV_THREAD) */
           void        *sigev_notify_attributes;                /* Attributes for notification thread (SIGEV_THREAD) */
           pid_t        sigev_notify_thread_id;                 /* ID of thread to signal (SIGEV_THREAD_ID) */
       };

返回值:
    成功 : 0
    失败 : -1

通知方式:
    产生一个信号
    创建一个线程执行一个指定的函数
注意:
    任何时刻只能有一个进程可以被注册为接收某个给定队列的通知
    当有一个消息到达某个先前为空的队列,而且已有一个进程被注册为接收该队列的通知时,只有没有任何线程阻塞在该队列的mq_receive调用的前提下,通知才会发出
    当通知被发送给它的注册进程时,其注册被撤销。进程必须再次调用mq_nitify以重新注册(如果需要的话),重新注册要放在从消息队列读出消息之前而不是之后

Link with -lrt

POSIX消息队列示例

mq_open.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)


int main(int argc, char const *argv[])
{

    mqd_t mqid;
    mqid = mq_open("/abc", O_CREAT|O_RDWR, 0666, NULL);
    if(mqid == (mqd_t)-1)
        ERR_EXIT("mq_open");

    printf("\n");

    mq_close(mqid);
    return 0;
}

  对于SYSTEM IPC可以使用ipcs查看;对于POSIX IPC,当我们创建一个消息队列后,实际上在系统中已经存在了,存在于一个虚拟的文件系统中,但是这个虚拟的文件系统我们需要挂载到一个目录下才能使用·

man 7 mq_overview 

# mkdir /dev/mqueue
# mount -t mqueue none /dev/mqueue

getattr.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)


int main(int argc, char const *argv[])
{

    mqd_t mqid;
    mqid = mq_open("/abc", O_RDONLY);
    if(mqid == (mqd_t)-1)
        ERR_EXIT("mq_open");

    printf("\n");

    struct mq_attr mqstat;
    int ret = mq_getattr(mqid, &mqstat);
    if(ret == -1)
        ERR_EXIT("mq_getattr");

    printf("Flags : %ld\n",mqstat.mq_flags);
    printf("mq_maxmsg : %ld\n",mqstat.mq_maxmsg);
    printf("mq_msgsize : %ld\n",mqstat.mq_msgsize);
    printf("mq_curmsgs : %ld\n",mqstat.mq_curmsgs);

    mq_close(mqid);
    return 0;
}

mq_snd.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

typedef struct str{
    char name[32];
    int age;
}STU;

int main(int argc, char *argv[])
{

    if(argc != 2)
    {
        fprintf(stderr, "Useage %s prio\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    mqd_t mqid;
    mqid = mq_open("/abc", O_WRONLY);
    if(mqid == (mqd_t)-1)
        ERR_EXIT("mq_open");
    printf("mq_open success\n");

    STU stu;
    strcpy(stu.name, "test");
    stu.age = 20;
    unsigned prio = atoi(argv[1]);

    int ret = mq_send(mqid, (const char*)&stu, sizeof(STU), prio);
    if(ret == -1)
        ERR_EXIT("mq_send");


    mq_close(mqid);
    return 0;
}

m_rcv.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

typedef struct str{
    char name[32];
    int age;
}STU;

int main(int argc, char *argv[])
{

    mqd_t mqid;
    mqid = mq_open("/abc", O_RDONLY);
    if(mqid == (mqd_t)-1)
        ERR_EXIT("mq_open");
    printf("mq_open success\n");

    STU stu;
    memset((void*)&stu, 0, sizeof(STU));
    unsigned prio;

    struct mq_attr mqstat;
    int ret = mq_getattr(mqid, &mqstat);
    if(ret == -1)
        ERR_EXIT("mq_getattr");

    // Message too long
    //int ret = mq_receive(mqid, (char*)&stu, sizeof(STU), &prio);

    ret = mq_receive(mqid, (char*)&stu, mqstat.mq_msgsize, &prio);
    if(ret == -1)
        ERR_EXIT("mq_receive");
    
    printf("name : %s\n",stu.name);
    printf("age : %d\n",stu.age);
    printf("prio : %u\n",prio);


    mq_close(mqid);
    return 0;
}

获取消息时的优先级

./mq_snd 2
./mq_snd 1
./mq_snd 0

./mq_rcv 
name : test
age : 20
prio : 2

./mq_rcv 
name : test
age : 20
prio : 1

./mq_rcv 
name : test
age : 20
prio : 0


./mq_snd 0
./mq_snd 1
./mq_snd 2

./mq_rcv 
name : test
age : 20
prio : 2

./mq_rcv 
name : test
age : 20
prio : 1

./mq_rcv 
name : test
age : 20
prio : 0

mq_notify.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>
#include <signal.h>


#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

typedef struct str{
    char name[32];
    int age;
}STU;

mqd_t mqid;
size_t size;
struct sigevent sigev;

void handler_user1(int sig)
{
    mq_notify(mqid, &sigev);

    STU stu;
    memset((void*)&stu, 0, sizeof(STU));
    unsigned prio;

    int ret = mq_receive(mqid, (char*)&stu, size, &prio);
    if(ret == -1)
        ERR_EXIT("mq_receive");
    
    printf("name : %s\n",stu.name);
    printf("age : %d\n",stu.age);
    printf("prio : %u\n",prio);
}

int main(int argc, char const *argv[])
{
    mqid = mq_open("/abc", O_RDONLY);
    if(mqid == (mqd_t)-1)
        ERR_EXIT("mq_open");
    
    struct mq_attr mqstat;
    int ret = mq_getattr(mqid, &mqstat);
    if(ret == -1)
        ERR_EXIT("mq_getattr");
    size = mqstat.mq_msgsize;

    signal(SIGUSR1, handler_user1);

    
    //通知方式
    sigev.sigev_notify = SIGEV_SIGNAL;
    //通知信号
    sigev.sigev_signo = SIGUSR1;
    mq_notify(mqid, &sigev);

    for(;;)
        pause();

    mq_close(mqid);
    return 0;
}

mq_unlink.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)


int main(int argc, char const *argv[])
{

    mq_unlink("/abc");
    return 0;
}
cat /dev/mqueue/abc 
QSIZE:0          NOTIFY:0     SIGNO:10    NOTIFY_PID:5139  

NOTIFY : 通知方式
SIGNO : 当前用那个信号来通知
NOTIFY_PID : 这种通知会发送给那个进程
posted @ 2019-10-08 14:34  sfdevs  阅读(175)  评论(0编辑  收藏  举报