当前位置: 首页 > 工具软件 > Open-MQ > 使用案例 >

Linux的sigevent结构----mq_notify()实例

郤瀚
2023-12-01

sigevent简介

首先给出官方文档地址:http://man7.org/linux/man-pages/man7/sigevent.7.html

首先,要明确sigevent的作用。sigevent只是一个结构体,而结构体本质上是用于存储数据信息的。由此,我们认为,sigevent的作用是为Linux/Unix系统调用函数API提供一个调用接口的通用参数;这类API的作用以某种特定的方式来通知进程到达的事件。API根据sigevent提供的结构,来具体处理有关信息。

下面给出sigevent的具体结构:

#include <signal.h>

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) */
};

每个参数的作用已经在英文的注释中说明了,不在赘述。下面主要说一下sigev_notify参数。该参数几个取值和作用如下:

  • SIGEV_NONE:空的提醒,事件发生时不做任何事情
  • SIGEV_SIGNAL:向进程发送sigev_signo中指定的信号,具体详细的状况参照上面的文档,这涉及到sigaction的使用
  • SIGEV_THREAD:通知进程在一个新的线程中启动sigev_notify_function函数,函数的实参是sigev_value,系统API自动启动一个线程,我们不用显式启动。

综上可知,sigevent结构体实际上是为各类系统调用API提供了一个统一的处理结构,我们只需要对提供sigevent结构的API,提供一个具体赋值结构体参数即可。下面通过mq_notify来实际说明该问题

mq_notify()简介

这篇笔记中介绍了Posix消息队列的一般操作。

先给出该函数的接口

#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
  • mqdes:消息队列的标识符
  • sevp:实际调用的参数,参数的意义参照sigevent结构体,或者直接看官方文档.

说明函数的作用:如果有新消息到达一个空的队列,允许调用线程注册一个异步通知,该通知会在消息到达时触发。核心的思想在异步这两个字上,也就是说,我们希望从消息队列中获取消息,但是不想让当前进程阻塞在一个空的消息队列上,那么通过该函数可以注册异步通知,来处理将来到达的事件。

几个注意事项:

  • 一个消息队列只能注册一个处理process
  • 如果当前进程想要移除注册的事件,只需要把sevp设置为NULL即可
  • 如果已经存在一个注册的事件进程,那么其他注册进程不会收到信号
  • 只有空消息队列在接收到第一个消息是,才会触发通知

代码实例

主进程创建一个消息队列,并注册一个事件,然后睡眠2秒模拟将来消息到达。之后主进程阻塞,模拟处理其他事件,在子进程中进行数据读取和程序退出。

#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <signal.h>
#include <cstring>

const char *mq_name = "/mq_test";


inline void handle_error(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}


static void tfunc(union sigval sv) {
    struct mq_attr attr;
    ssize_t nr;
    void *buf;
    mqd_t mqdes = *((mqd_t *) sv.sival_ptr);

    if (mq_getattr(mqdes, &attr) == -1) {
        handle_error("mq_getattr() error\n");
    }

    buf = malloc(attr.mq_msgsize);
    if (buf == nullptr) {
        handle_error("malloc() error\n");
    }

    nr = mq_receive(mqdes, (char *) buf, attr.mq_msgsize, nullptr);
    if (nr == -1) {
        handle_error("mq_receive() error\n");
    }

    printf("Read %zd bytes from MQ\n", nr);
    free(buf);
    exit(EXIT_SUCCESS);
}


int main() {
    mqd_t mqdes = mq_open(mq_name, O_CREAT | O_RDWR, 0777, nullptr);
    if (mqdes < (mqd_t) 0) {
        handle_error("mq_open() error\n");
    }

    struct sigevent sev;
    bzero(&sev, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = tfunc;
    sev.sigev_notify_attributes = nullptr;
    sev.sigev_value.sival_ptr = &mqdes;  // 传递给tfunc的参数

    if (mq_notify(mqdes, &sev) == -1) {
        handle_error("mq_notify() error\n");
    }
    puts("sleep for 2 second...");
    sleep(2);

    const char *msg = "hello world!";
    mq_send(mqdes, msg, strlen(msg), 1);
    pause();

    exit(EXIT_SUCCESS);
}
 类似资料: