标签:des style color 使用 os io 文件 数据
1.概述
消息队列可认为是消息链表。有足够写权限的线程可以往队列中放置消息,有足够读权限的进程可以从队列中取走消息。每个消息是一个记录,由发送着赋予一个优先级。
在像队列中写入消息时,不需要某个进程在该队列上等待消息到达。这与管道不同,管道必须现有读再有写。
消息队列具有随内核的持续性,与管道不同。进程结束后,消息队列中消息不会消失。当管道最后一次关闭,其中的数据将丢弃。
消息队列具有名字,可用于非亲缘关系的进程间。
Posix消息队列读总是返回最高优先级的最早消息,而System V消息队列的读可以返回任意优先级的。
往空消息队列中放置消息,Posix消息队列会产生一个信号或启动一个线程。而System V不提供这个功能。
2.消息队列相关函数
2.1. 创建,关闭,删除
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, …
/* mode_t mode, struct mq_qttr *attr */);
返回:成功:消息队列描述符 出错:-1
用于创建一个新的或者打开一个已存在的消息队列。
oflag是O_RDONLY, O_WRONLY或O_RDWR之一,可能按位或上O_CREAT, O_EXCL或O_NONBLOCK。
创建新队列时,mode和attr是必须的。attr=NULL使用默认属性
mq_open返回的消息队列描述符,它不必是像普通描述符那么样的短整形(很可能不是)。
int mq_close(mqd_t mqdes);
关闭已打开的消息队列。调用进程不能在使用这个描述符了,但是消息队列没有从系统中删除。
进程终止时,所有打开者的消息队列都关闭,就像调用mq_close();
从系统中删除name
int mq_unlink(const char *name);
成功:0;出错:-1;
消息队列保存着当前打开着的文件的引用计数,mq_unlink会删除name,但是消息队列的删除要到最后一个mq_close()发生为止。
消息队列至少具有随内核的持续性。当没有进程打开某个消息队列时,该队列的消息仍然存在,知道mq_unlink并让应用计数为0才删除该队列。
2.2. 消息队列属性
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);
成功:0;失败:-1;
struct mq_attr {
long mq_flags; //message queue flag: 0, O_NONBLOCK
long mq_maxmsg; //max number of message allowed on queue
long mq_msgsize; //max size of a message (in bytes)
long mq_curmsgs; //number of message currlently on queue
}
创建新队列时,可以指定mq_maxmsg和mq_msgsize属性,但是mq_open会忽略另外两个参数。
mq_setattr设置队列属性时,只使用mq_flags用于设置或着清除非阻塞标志,另两个忽略。
消息大小和数目只能在创建队列时指定。
2.3. 接受和发送
#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);
成功:0;出错: -1;
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio);
成功: 消息中字节数; 出错: -1;
用于向队列中放置消息和从队列中取出消息。
mq_receive中len不能小于mq_msgsize,否则会返回EMSGSIZE。
prio为消息优先级,必须小于MQ_PRIO_MAX。
若不需要使用优先级不同的消息,指定mq_send为0优先级,mq_receive优先级为NULL。
3. 异步事件通知
Posix消息队列允许异步消息通知,即当有一个消息放到某个空消息队列中,会产生某种通知:
(1) 产生一个信号
(2) 创建一个线程执行指定函数
#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
成功: 0; 出错:-1;
#include <signal.h>
union sigval {
int sival_int;
int *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_attrbutes;
}
在接受到通知之后,会被复位成默认行为,因此必须再注册。
在队列变空前不会再接受到通知,要保证在读出消息之前重新注册。否则在消息变为空后再注册,这段时间内可能又有消息放到队列中造成不会再通知。
4.通知的处理方式
4.1. 信号通知
不可以简单的在信号处理程序中调用mq_receive, 因为它不是异步信号安全函数(可以从信号处理程序中调用的函数)。一般只有sem_pos,read,write是信号安全的。
也不可以简单使用mq_receive接受消息,因为这只能接受到一个消息,其他的消息可能永远不会被取出,应该总是使用非阻塞方式读数据。
程序处理方式:
方法1:使用sigsuspend()等待信号的发生,通过在信号处理程序中设置全局变量,被信号唤醒后再处理数据。
方法2:由于消息队列描述符不是普通描述符,不可以直接使用select。通过管道和在信号处理程序中调用write唤醒select的方式。
方法3:通过sigwait()函数等待某个信号发生,然后再处理,类似于select。这是最好的方式。
#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
成功:0; 出错:正的EXXX值。
函数阻塞到set中某个信号发生,sig返回信号值。
这叫做“同步地等待一个异步信号”,使用信号却没有涉及异步信号处理。
此函数往往在多线程话程序中使用,有EXXX错误。在多线程话程序中不能使用sigprocmask,要使用pthread_sigmask。
代码示例:
4.2线程处理方式,这是最好的
注意sigev的设置。
示例:
进程间通信(二)——Posix消息队列,布布扣,bubuko.com
标签:des style color 使用 os io 文件 数据
原文地址:http://www.cnblogs.com/hancm/p/3885824.html