linux c编程:Posix消息队列

 Posix消息队列可以认为是一个消息链表有足够写权限的线程可以往队列中放置消息有足够读权限的线程可以从队列中取走消息

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

这跟管道和FIFO是相反的因为对于管道,FIFO来说除非读出者已经存在光有写入者是没有意义的。一个进程在往某消息队列写入消息后终止进程另一个进程某时刻读出该消息;然而对于管带或FIFO而言当管道或FIFO的最后一次关闭发生时,仍在管道或FIFO中的数据将被抛弃

消息队列的布局如下图

下面介绍队列用到函数:

1 mqd_t  mq_openconst char* name, int oflag, mode_t mode, struct mq_attr* attr);

函数打开或者创建一个posix消息队列。当我们的实际操作是创建一个新的队列时(即所要创建的队列不存在oflag中已经指定O_CREAT), mode 和 attr参数是需要的.
函数的返回值称为消息队列描述符这个值用作其他消息队列操作函数的第一个参数值。另外根据man 7 mq_overview里面的介绍。在链接的时候必须链接librt库,采用-lrt的方法

如果没有链接的话,会出现下面的错误

root@zhf-maple:/home/zhf/codeblocks_prj/unix_network# gcc -c main.c -o main_tmp

root@zhf-maple:/home/zhf/codeblocks_prj/unix_network# ./main_tmp test2

bash: ./main_tmp: 无法执行二进制文件可执行文件格式错误

name

posix IPC名字。(必须以/开头,且后面不能再含有/

oflag

标志。

标志——————————作用

O_CREAT———————没有该对象则创建

O_EXCL————————如果O_CREAT指定,但name不存在,就返回错误

O_NONBLOCK—————以非阻塞方式打开消息队列

O_RDONLY———————只读

O_RDWR————————读写

O_WRONLY———————只写

 

mode

权限——————作用

S_IWUSR——用户/属主写

S_IRUSR——用户/属主读

S_IWGRP——组成员写

S_IRGRP——组成员读

S_IWOTH——其他用户写

S_IROTH——其他用户读

 

attr结构体如下:

struct mq_attr {

               long mq_flags;       /* Flags: 0 or O_NONBLOCK */

————mq_open创建时被初始化;

————mq_setattr中设置;

————其值为0(阻塞)或者O_NONBLOCK(非阻塞)。

               long mq_maxmsg;      /* Max. # of messages on queue */

——队列的消息个数最大值:

————只能在mq_open创建时被初始化。

               long mq_msgsize;     /* Max. message size (bytes) */

——队列中每个消息的最大值:

————只能在mq_open创建时被初始化。

               long mq_curmsgs;     /* # of messages currently in queue */

——当前队列的消息个数:

————mq_getattr中获得。

           };

2 int mq_close(mqd_t mqdes):关闭消息队列

关闭之后调用进程不在使用该描述符,但消息队列不会从系统中删除,进程终止时,会自动关闭已打开的消息队列,和调用mq_close一样。参数为mq_open()函数返回的值

 

3  int mq_unlink(const char *name): 从系统中删除某个消息队列

删除会马上发生,即使该队列的描述符引用计数仍然大于0。参数为mq_open()函数第一个参数。

 

4设置和获取队列属性

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

int mq_setattr(mqd_t mqdes, struct mq_attr *newattr,  struct mq_attr *oldattr);   

参数mqdesmq_open()函数返回的消息队列描述符。

参数attrnewattroldattr为消息队列属性结构体指针;

 

5向消息队列放置和取走消息

int mq_send(mqd_t mqdes, const char *msg_ptr,   size_t msg_len, unsigned msg_prio); 

ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,  size_t msg_len, unsigned *msg_prio); 

参数msg_ptr为指向消息的指针。

msg_len为消息长度,该值不能大于属性值中mq_msgsize的值。

msg_prio为优先级,消息在队列中将按照优先级大小顺序来排列消息。

如果消息队列已满,mq_send()函数将阻塞,直到队列有可用空间再次允许放置消息或该调用被信号打断;如果O_NONBLOCK被指定,mq_send()那么将不会阻塞,而是返回EAGAIN错误。如果队列空,mq_receive()函数将阻塞,直到消息队列中有新的消息;如果O_NONBLOCK被指定,mq_receive()那么将不会阻塞,而是返回EAGAIN错误。

 

下面就来看第一个例子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <mqueue.h>

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

int main(int argc,char **argv)
{
    char *msg="hello world";
    unsigned int prio;
    int     c,flags;
    mqd_t   mqd;
    struct mq_attr  attr;
    flags = O_RDWR|O_CREAT;
    printf("create mqueue.\n");
    if((mqd = mq_open(argv[1],flags,FILE_MODE,NULL)) == -1)
    {
        perror("mq_open() error");
        exit(-1);
    }
    prio=0;
    printf("len=%d,len1=%d",sizeof(msg),strlen(msg));
    mq_send(mqd,msg,strlen(msg),prio);
    mq_getattr(mqd,&attr);
    printf("max #msgs = %ld,max #bytes/msg = %ld,#currently on queue = %ld\n",attr.mq_maxmsg,attr.mq_msgsize,attr.mq_curmsgs);
    mq_close(mqd);
    exit(0);
}

在执行代码前,首先要创建消息队列存放的路径。一般是/dev/mqueue。没有的话需要创建一个。然后执行mount -t mqueue none /dev/mqueue进行挂载。参考说明如下

gcc main.c -o main_tmp -lrt

./main_tmp /test123

执行完后,在dev/mqueue路径下面就能看到创建的队列文件以及队列里面的信息。QSIZE显示队列中的大小

cat /dev/mqueue/test12
QSIZE:11 NOTIFY:0 SIGNO:0 NOTIFY_PID:0

读的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <mqueue.h>

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

int main(int argc,char **argv)
{
    char *buff;
    unsigned int prio;
    int     c,flags,n;
    mqd_t   mqd;
    struct mq_attr  attr;
    flags = O_RDONLY;
    if((mqd = mq_open(argv[1],flags)) == -1)
    {
        printf("mqueue open error\n");
        exit(-1);
    }
    mq_getattr(mqd,&attr);
    printf("max #msgs = %ld,max #bytes/msg = %ld,#currently on queue = %ld\n",attr.mq_maxmsg,attr.mq_msgsize,attr.mq_curmsgs);
    buff=malloc(attr.mq_msgsize);
    n=mq_receive(mqd,buff,attr.mq_msgsize,&prio);
    printf("read %ld bytes,priority=%d\n",n,prio);
    printf("msg is:%s",buff);
    mq_close(mqd);
    free(buff);
    exit(0);
}
执行读的操作

./mq_receive /test12
max #msgs = 10,max #bytes/msg = 8192,#currently on queue = 1
read 11 bytes,priority=0
msg is:hello world

 

前面的例子中是没法做到写入数据后通知另外一个进程去读,其他的进程要想就要一直去查询是否有消息进来。Posix 消息队列允许异步事件通知,以告知何时有一个消息放置到了某个空消息队列中,可以通过产生一个信号的方式来达到这个目的

这种通知通过调用mq_notify建立

static void sig_usr1(int signo)
{
    ssize_t n;
    //每次执行后重新注册
    mq_notify(mqd,&sigev);
    n=mq_receive(mqd,buff,attr.mq_msgsize,NULL);
    printf("SIGUSR1 received,read %ld bytes\n",(long)n);
    return;
}


static void mq_receive1()
{
    mq_getattr(mqd,&attr);
    buff=malloc(attr.mq_msgsize);
    //申明SIGUSR1信号,信号的处理函数为sig_usr1
    signal(SIGUSR1,sig_usr1);
    sigev.sigev_notify=SIGEV_SIGNAL;
    sigev.sigev_signo=SIGUSR1;
    //给该队列注册一个sig信号
    mq_notify(mqd,&sigev);
    for(;;)
        pause();
    exit(0);
}

上面程序的问题是从信号程序中调用mq_notify, mq_receive和printf, 这些函数实际上都不可以从信号处理程序中调用

 

 没有列在该表中的函数不可以从信号处理程序中调用。

//使用sigwait的方法
static void mq_receive2()
{
    int signo;
    void *buff;
    ssize_t n;
    sigset_t newmask;
    mq_getattr(mqd,&attr);
    buff=malloc(attr.mq_msgsize);
    printf("mq_size=%d\n",attr.mq_msgsize);
    sigemptyset(&newmask);
    sigaddset(&newmask,SIGUSR2);
    sigprocmask(SIG_BLOCK,&newmask,NULL);
    sigev.sigev_notify=SIGEV_SIGNAL;
    sigev.sigev_signo=SIGUSR2;
    mq_notify(mqd,&sigev);
    for(;;)
    {
        printf("wait here\n");
        sigwait(&newmask,&signo);
        printf("received signo=%d\n",signo);
        if(signo == SIGUSR2)
        {
             mq_notify(mqd,&sigev);
             while((n=mq_receive(mqd,buff,attr.mq_msgsize,NULL)) >=0)
             {
                printf("read %ld bytes\n",(long)n);
             }
        }
    }
    exit(0);
}

int main(int argc,char **argv)
{
    mqd=mq_open(argv[1],O_RDONLY);
    mq_receive2();
    mq_close(mqd);
}

 

posted @ 2018-11-24 21:09  red_leaf_412  阅读(3821)  评论(0编辑  收藏  举报