Posix消息队列

概述

消息队列可看做消息链表,线程可在队列中进行消息读写。

在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。

一个进程可以往某个队列写入一些消息,然后终止,再让另一个进程在以后某个时刻读取这些消息。而管道或FIFO的最后一次关闭发生时,仍在该管道或FIFO上的数据将被清除。


posix消息队列特点

对posix消息队列的读总是返回最高优先级的最早消息。

当往一个空队列放置一个消息时,posix消息队列允许产生一个信号或者启动一个线程。


队列中的每个消息具有如下属性:

优先级

消息的数据部分长度

数据本身


消息队列的布局:



mq_open mq_close mq_unlink函数

mq_open 函数创建一个新的消息队列或者打开一个应存在的消息队列。

#include <mqueue.h>

mqd_t mq_open(const char *name,int oflag,.../*mode_t mode,struct mq_attr *attr    */);

name:posix IPC 名字,可能是某个文件系统中一个真正的路径名,也可能不是。

oflag参数:O_RDONLY,O_WRONLY,O_RDWR,O_CREAT,O_EXCEL,O_NONBLOCK

mode:该参数在当创建一个新队列时需要。

常值说明

S_IRUSR

S_IWUSR

用户(属主)读

用户(属主)写

S_IRGRP

S_IWGRP

(属)组成员读

(属)组成员写

S_IROTH

S_IWOTH

其他用户读

其他用户写


attr:该参数在当创建一个新队列时需要。用于给新队列指定某些属性,若为空指针,则为默认属性。

mq_open返回值:消息队列描述符。


已打开的消息队列由mq_close关闭

#include <mqueue.h>
int mq_close(mqd_t mqdes);

从系统中删除用作mq_open第一个参数的某个name,使用mq_unlink

#include <mqueue.h>
int mq_unlink(const char *name);
posix消息队列至少具备随内核持续性,即使当前没有进程打开某个消息队列,该队列及其上的各个消息也将一直存在,直到调用mq_unlink并让它的引用计数达到0以删除该队列为止。


程序例子
int main(int argc,char *argv[])

{

    int c,flags;

    mqd_t mqd;

    flags = O_RDWR | O_CREAT;

    while((c= getopt(argc,argv,"e"))  != -1)

    {

        switch(c)

        {

            case 'e':

                flags |= O_EXCL;//若有-e存在。表示需要独占打开

                break; 

        }

    }

    if (optind != argc -1)

        printf("usage : mqcreate [-e]  <name>\n");

        mqd = mq_open(argv[optind],flags,FILE_MODE,NULL);//命令行输入IPC名字

        mq_close(mqd);//关闭

        exit(0);

}



从系统中删除消息队列:

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

{

    if (argc != 2)

        printf("usage: mqunlink <name>\n");

    mq_unlink(argv[1]);//命令行输入需要删除的消息队列名字。

}


mq_getattr 和 mq_setattr函数

每个消息队列有四个属性,mq_getattr返回所有属性,mq_setattr设置其中某个属性。

struct mq_attr

{

    long mq_flags;//0:O_NONBLOCK

    long mq_maxmsg;//max number of message allow on queue

    long mq_msgsize;//max size of a message

    long mq_curmsgs;//number of message currently on queue

}

#include <mqueue.h>

int mq_getattr(mqd_t mqdes,struct mq_attr *attr);//将当前属性填入attr指向的结构

int mq_setattr(mqd_t mqdes,const struct mq_attr *attr,struct mq_attr *oattr);//给所指定的队列设置属性,但是只使用attr指向
的mq_attr结构的mq_flags成员,以设置或清除非阻塞标志。其他三个属性被忽略:mq_maxmsg与mq_msgsize只能在创建队列时设定,mq_curmsgs只能获取不能设定。

mqgetattr程序
int mian(int argc,char** argv)
{

    mqd_t mqd;

    struct mq_attr attr;

    if(argc != 2)

        err_quit("usage: mqgetattr <name>\n");

    mqd = mq_open(argv[1],O_RDONLY);//打开消息队列

    mq_getattr(mqd,&arrt);//获取消息队列属性

    printf("max msgs=%ld,max bytes=%ld,current on queue=%ld \n",attr.mq_maxmsg,arrt.mq_msgsize,attr.mq_curmsgs);

}


mq_send和mq_receive函数

这两个函数分别是往一个队列里放置一个消息和取出一个消息。

#include <mqueue.h>

int mq_send(mqd_t mqdes,const char *ptr,size_t len,unsigned int prio);//prio为要设置的优先级

ssize_t mq_receive(mqd_t mqdes,char *ptr,size_t len,unsigned int *priop);//priop为返回的消息优先级,该函数返回值为消息中的字节数。


消息队列限制:

消息队列的两个限制在创建队列时建立:

mq_mqxmsg 队列中的最大消息数

mq_msgsize 给定消息的最大字节数

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

MQ_OPEN_MAX一个进程能够同时拥有打开着消息队列的最大数目

MQ_PRIO_MAX 任意消息的最大优先级值加1

这两个常值定义在<unistd.h>头文件中,也可以运行时通过调用sysconf函数获取:

int main(int argc,char **argv)

{

    printf("MQ_OPEN_MAX = %ld,MQ_PRIO_MAX = %ld \n",sysconf(_SC_MQ_OPEN_MAX),sysconf(_SC_MQ_PRIO_MAX));

    return;

}

mq_notify函数

posix消息队列允许异步事件通知以告知(发送信号)何时有一个消息放置到了某个空消息队列中,这种通知由两种方式可供选择:

1.产生一个信号

2.创建一个线程来执行一个指定的函数

这种通知通过调用mq_notify建立,为指定队列建立或删除异步事件通知:

union sigval

{

    int sival_int;

    void *sival_ptr;

}

struct sigevent

{

    int sigev_notify;//SIGEV_{NONE,SIGNAL,THREAD}

    int sigev_signo;//signal number if SIGEV_SIGNAL

    union sigval sigev_value;

    void (*sigev_notify_function)(union sigval);

    pthread_attr_t *sigev_notify_attributes;

}

#include <mqueue.h>

int mq_notify(mqd_t mqdes,const struct sigevent *notification);

规则:

  1. 若notification参数非空,那么当前进程希望在有一个消息到达所指定的先前为空的队列时得到通知,则称:该进程被注册为接收该队列的通知。
  2. 若notification参数为空指针,而且当前进程目前被注册为接收所指定队列的通知,那么已存在的注册将被撤销。
  3. 任何时刻只有一个进程可以被注册为接收某个给定队列的通知。
  4. 当有一个消息到达某个先前为空的队列,而且已有一个进程被注册为接收该队列的通知时,只有在没有任何线程阻塞在该队列的mq_receive调用的前提下,通知才会发出。也就是说,在mq_reveive调用中的阻塞比任何通知的注册都优先。
  5. 当该通知被发送给它的注册进程时,其注册即被注销,该进程必须再次调用mq_notify以重新注册。


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页