linux 进程间通信中消息传递主要分为管道,FIFO,消息队列
(1)管道
管道由pipe函数创建,提供一个单路(单向)数据流。pipe函数返回两个文件描述符:fd[0]和fd[1]。前者打开来读,后者打开来写。管道没有名字,所以只能由有亲缘关系的进程使用。尽管管道是由单个进程创建的,它却很少在单个进程内使用。管道的典型用途为两个不同进程(一个是父进程,一个是子进程)提供进程间的通信手段。首先,由一个进程(它将成为父进程)创建一个管道后调用fork派生一个自身的副本。接着,父进程关闭这个管道的读出端,子进程关闭同一管道的写入端。或者父进程关闭这个管道的写入端,子进程关闭同一管道的读出端。这就在父子进程间提供了一个单向数据流。
(2)FIFO
FIFO指代先进先出(First in,First out),linux中的FIFO类似管道。它是一个单向(半双工)数据流。不同于管道的是,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO也称为有名管道。FIFO由mkfifo函数创建。其中pathname是一个普通的Unix路径名,它是该FIFO的名字。mkfifo 函数已隐含指定 O_CREAT|O_EXCL. 也就是说,它那么创建一个新的FIFO,要么返回一个EEXIST错误(如果所指定的名字的FIFO已经存在)。如果不想希望创建一个新的FIFO,那就改为调用open而不是mkfifo.要打开一个已存在的FIFO或创建一个新的FIFO,应先调用mkfifo,再检查它是否返回EEXIST错误,若返回该错误则改为调用open.mkfifo 命令也能创建FIFO。可以从shell脚本或命令行中使用它。在创建出一个FIFO后,它必须或者打开来读,或者打开来写,所用的可以是open函数,也可以是某个标准I/O打开函数。FIFO不能打开来既读又写,因为它是半双工的。对管道或FIFO的write总是往末尾添加数据,对它们的read则总是从开头返回数据。如果对管道或FIFO调用lseek,那就返回ESPIPE错误。
(3)Posix 消息队列
消息队列可认为是一个消息链表。有足够写权限的线程可往队列中放置消息,有足够读权限的线程可从队列中取走消息。每个消息都是一个记录,它由发送者赋予一个优先级。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这跟管道和FIFO是相反的,对后者来说,除非读出者已存在,否则先有写入者是没有意义的。一个进程可以往某个队列写入一些消息,然后终止,再让另外一个进程在以后某个时刻读出这些消息。消息队列具有随内核的持续性,这跟管道和FIFO不一样。Posix消息队列和System V消息队列。这两组函数间存在许多相似性,但也有主要的区别
1. 对Posix消息队列的读总是返回最高优先级的最早消息,对System V消息队列的读则可以返回任意指定优先级的消息。
2.当往一个空队列放置一个消息时,Posix 消息队列允许产生一个信号或启动一个线程。System V消息队列则不提供类似机制。
队列中的每个消息具有如下属性:
1.一个无符号整数优先级(Posix)或一个长整数类型(System V).
2.消息的数据部分长度(可以为0).
3.数据本身(如果长度大于0)
函数接口
1. mqd_t mq_open(const char *name,int oflag,...)
mq_open函数创建一个新的消息队列或打开一个已存在的消息队列
2.int mq_close(mqd_t mqdes);
mq_close函数关闭一个消息队列。
3.int mq_unlink(const char *name);
从系统中删除用作第一个参数的某个name.
4. 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);
每个消息队列有四个属性,mq_getattr返回所有这些属性,mq_setattr则设置其中某个属性。
struct mq_attr{
long mq_flags;
long mq_maxmsg;
long mq_msgsize;
long mq_curmsgs;
};
5.int mq_send(mqd_t mqdes,const char *ptr,size_t len,unsigned int prio);
int mq_receive(mqd_t mqdes,char *ptr,size_t len,unsigned int *priop);
mq_send函数往消息队列中写入消息,mq_receive函数从消息队列中读出消息。
6.int mq_notify(mqd_t mqdes,const struct sigevent *motification);
结构体:
union sigval{
int sival_int;
void *sival_ptr;
};
struct sigevent
{
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
mq_notify函数为指定队列建立或删除异步事件通知。一些普遍适用于该函数的若干规则
1).如果notification参数非空,那么当前进程希望在一个消息达到所指定的先前为空的队列时得到通知。我们说"该进程被注册为接收该队列的通知"。
2).如果notification参数为空指针,而且当前进程目前被注册为接收所指定队列的通知,那么已存在的注册将被撤销。
3).任意时刻只有一个进程可以被注册为接收某个给定队列的通知。
4).当有一个消息达到某个先前为空的队列,而且已有一个进程被注册为接收该队列的通知时,只有在没有任何线程阻塞在该队列的mq_receive调用中的前提下,通知才会发出。这就是说,在mq_receive调用中的阻塞比任何通知的注册都优先。
5).当该通知被发送给它的注册进程时,其注册即被撤销。该进程必须再次调用mq_notify以重新注册(想要的话)。