【IPC通信】Posix消息队列使用异步事件通知

mq_notify()函数为指定队列建立或删除异步事件通知,建立通知即某空消息队列中如有新的消息,会发送一个信号或创建一个线程来执行指定的函数。

#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
成功返回0,出错则为-1

给出struct sigevent定义:

union signal
{
    int  sival_int;  /*整数值*/
    void *sival_ptr; /*指针值*/
};

struct sigevent
{
    int sigev_notify; /*通知类型:SIGEV_NONE、SIGEV_SIGNAL、SIGEV_THREAD*/
    int sigev_signo; /*信号值*/
    union sigval sigev_value; /*传递给信号处理函数或线程的信号值*/
    void (*sigev_notify_function)(union sigval); /*线程处理函数*/
    pthread_attr_t *sigev_notify_attributes; /*线程属性*/
};

下面给出示例代码:

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


mqd_t mqd;
struct mq_attr attr;
struct sigevent sigev;
char *ptr;
unsigned int prio;
size_t n;
int rc;

void sig_usr1(int signo);

/*读取某消息队列,消息队列名通过参数传递*/
/*当有消息放置到某个空的队列中时产生SIGUSR1信号*/
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Usage: mqnotifysig1 <name>\n");
        exit(1);
    }

    /*只读模式打开消息队列*/
    mqd = mq_open(argv[1], O_RDONLY);
    if(mqd < 0)
    {
        perror("打开消息队列失败");
        exit(1);
    }

    // 取得消息队列属性,根据mq_msgsize动态申请内存
    rc = mq_getattr(mqd, &attr);
    if(rc < 0)
    {
        perror("取得消息队列属性失败");
        exit(1);
    }

    /*动态申请保证能存放单条消息的内存*/
    ptr = calloc(attr.mq_msgsize, sizeof(char));
    if(NULL == ptr)
    {
        printf("动态申请内存失败\n");
        mq_close(mqd);
        exit(1);
    }

    //注册信号函数
    signal(SIGUSR1, sig_usr1);
    sigev.sigev_notify = SIGEV_SIGNAL;
    sigev.sigev_signo = SIGUSR1;

    //注册通知
    rc = mq_notify(mqd, &sigev); // 读取前需要再次注册
    if(rc < 0)
    {
        perror("通知注册失败");
        mq_close(mqd);
        free(ptr);
        exit(1);
    }

    for(;;)
    {
        pause();
    }

    return 0;
}

void sig_usr1(int signo)
{
    rc = mq_notify(mqd, &sigev); // 读取前需要再次注册
    if(rc < 0)
    {
        perror("通知注册失败");
        mq_close(mqd);
        free(ptr);
        exit(1);
    }

    /*接收一条消息*/
    n = mq_receive(mqd, ptr, attr.mq_msgsize, &prio);
    if(n < 0)
    {
        perror("读取失败");
        mq_close(mqd);
        free(ptr);
        exit(1);
    }
    printf("读取 %ld 字节\n优先级为 %u\n", (long)n, prio);
}

编译并执行:

[infor@s123 PosixMq]$ gcc -o mqnotifysig1 mqnotifysig1.c -lrt
[infor@s123 PosixMq]$ ./mqnotifysig1 /tmp

此时消息队列内没有消息,程序进入睡眠,等待信号到来。再开启一个终端,通过另外的程序往消息队列写消息:

[infor@s123 PosixMq]$ ./sendmq /tmp 200 16
[infor@s123 PosixMq]$ ./sendmq /tmp 100 17
[infor@s123 PosixMq]$ ./sendmq /tmp 50 18

可以看到先前的程序:

[infor@s123 PosixMq]$ gcc -o mqnotifysig1 mqnotifysig1.c -lrt
[infor@s123 PosixMq]$ ./mqnotifysig1 /tmp
读取 200 字节
优先级为 16
读取 100 字节
优先级为 17
读取 50 字节
优先级为 18

mq_notify函数使用规则:

1、如果mq_notify()的notification参数非空,则表明将当前进程注册为接收某队列的通知;
2、如果mq_notify()的notification参数为空,则表明之前已存在的注册将被撤消;
3、一个消息队列任意时刻只能由一个进程注册;
4、如有其他进程mq_receive阻塞在该消息队列时,信号不会发出,即mq_receive调用优先级更高;
5、通知被发送给注册进程时,注册即被撤消,须重新注册。

注:上面程序其实是有问题,本处只做mq_notify函数的使用示例。

 

2011-11-18  任洪彩  [email protected]

转载请注明出处。

你可能感兴趣的:(消息队列,posix,IPC通信)