读书笔记:第5章 Posix消息队列 (8)

《UNIX网络编程:卷2》P76:图5-14

/*
 * mqnotifysig5.c
 * P76 图5-14 伴随管道使用信号通知
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <mqueue.h>

static void sig_usr1(int signo);

int	pipefd[2];

int main(int argc, char *argv[])
{
	int			nfds;
	char		c;
	fd_set		rset;
	mqd_t		mqd;
	void		*buff;
	ssize_t		n;
	struct mq_attr	attr;
	struct sigevent	sigev;

	if (argc != 2) {
		fprintf(stderr, "usage: mqnotifysig1 <name>\n");
		exit(0);
	}

	// 打开一个消息队列,非阻塞模式读取消息队列
	if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) < 0) {
		fprintf(stderr, "mq_open error: %s\n", strerror(errno));
		exit(1);
	}

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

	// 分配缓冲区
	if ((buff = malloc(attr.mq_msgsize)) == NULL) {
		fprintf(stderr, "malloc error: %s\n", strerror(errno));
		exit(1);
	}

	// 创建一个管道,当接收到消息队列的异步通知时,信号处理程序就会往该管道中写入数据
	if (pipe(pipefd) < 0) {
		fprintf(stderr, "pipe error: %s\n", strerror(errno));
		exit(1);
	}

	// 建立信号处理程序
	signal(SIGUSR1, sig_usr1);
	sigev.sigev_notify = SIGEV_SIGNAL;	// 队列由空变为非空时,产生信号
	sigev.sigev_signo = SIGUSR1;		// 希望产生的信号编号

	//  为指定队列建立异步通知
	if (mq_notify(mqd, &sigev) < 0) {
		fprintf(stderr, "mq_notify error: %s\n", strerror(errno));
		exit(1);
	}

	FD_ZERO(&rset);						// 初始化描述符集
	for ( ; ; ) {
		FD_SET(pipefd[0], &rset);		// 打开pipefd[0]的那一位,调用select时等待该描述符

		nfds = select(pipefd[0] + 1, &rset, NULL, NULL, NULL);
		if ((nfds < 0) && (errno != EINTR)) {
			fprintf(stderr, "select error: %s\n", strerror(errno));
			exit(1);
		}

		if (FD_ISSET(pipefd[0], &rset)) {	// 检查是否被置位

			// 读取一个字节
			if (read(pipefd[0], &c, 1) < 0) {
				fprintf(stderr, "read error: %s\n", strerror(errno));
				exit(1);
			}

			// 当通知被发送给它的注册进程时,其注册被撤销,所以再次注册 
			if (mq_notify(mqd, &sigev) < 0) {
				fprintf(stderr, "mq_notify error: %s\n", strerror(errno));
			}

			// 从消息队列中取出消息,使用循环
			while ((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) {
				printf("SIGUSR1 received, read %ld types\n", n);
			}
			if (errno != EAGAIN)			// EAGAIN,意味着暂时没有消息可读
				fprintf(stderr, "mq_receive error\n");

		}
	}

	exit(0);
}

static void sig_usr1(int signo)
{
	write(pipefd[1], "", 1);		// 写入一个字节
	return;
}

运行程序:

$ ./mqcreate /test1
$ ./mqnotifysig5 /test1

从另一个窗口中运行:

$ ./mqsend /test1 50 16

程序mqnotifysig5输出为:

SIGUSR1 received, read 50 types

注意write是异步信号安全的,因此可以从信号处理程序中调用它。

当接收到消息队列的异步信号通知时,信号处理程序就会往该管道中写入数据。

你可能感兴趣的:(读书笔记,《UNIX网络编程》)