进程间通信之System V消息队列和 Posix 消息队列

消息队列可以认为是一个消息链表,System V 消息队列使用消息队列标识符标识。具有足够特权的任何进程都可以往一个队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。System V 消息队列是随内核持续的,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。

消息队列和之前讨论过的管道和FIFO有很大的区别,主要有以下两点:

1)一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必需已经打开来读,否则写进程就会阻塞(默认情况下)。

2)IPC的持续性不同。管道和FIFO是随进程的持续性,当管道和FIFO最后一次关闭发生时,仍在管道和FIFO中的数据会被丢弃。消息队列是随内核的持续性,即一个进程向消息队列写入消息后,然后终止,另外一个进程可以在以后某个时刻打开该队列读取消息。只要内核没有重新自举,消息队列没有被删除。

 

System V 消息队列操作函数

系统V消息队列API共有四个,使用时需要包括几个头文件:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

1)int msgget(key_t key, int msgflg)

参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。

在以下两种情况下,该调用将创建一个新的消息队列:

1)如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;

2)key参数为IPC_PRIVATE;

参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。

调用返回:成功返回消息队列描述字,否则返回-1。

注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列。

当创建一个新消息队列时,msqid_ds结构的如下成员被初始化。

(1)msg_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cgid成员被设置成当前进程的有效组ID。

(2)oflag中的读写权限位存放在msg_perm.mode中。

(3)msg_qnum,msg_lspid,msg_lrpid,msg_stime和msg_rtime被置为0.

(4)msg_ctime被设置成当前时间。

(5)msg_qbytes被设置成系统限制值

ipcs -q  —-查看消息队列

 

 

2)int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);
该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。

msqid为消息队列描述字;消息返回后存储在msgp指向的地址,msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),msgtyp为请求读取的消息类型;读消息标志msgflg可以为以下几个常值的或:

IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG

IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息

IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。

msgrcv手册中详细给出了消息类型取不同值时,调用将返回消息队列中的哪个消息。

(1)如果type为0,那就返回该队列中的第一个消息,既然每个消息队列都是作为一个FIFO链表维护的,因此type为0指定返回该队列中最早的消息。

(2)如果type大于0,那就返回其类型值为type的第一个消息。

(3)如果type小于0,那就返回其类型小于或等于type参数的绝对值的消息中类型值最小的第一个消息。

 

msgrcv的flag参数指定所请求类型的消息不在所指定的队列中时该做何处理。在没有消息可得的情况下,如果设置了flag中的IPC_NOWAIT位,msgrcv函数就立即返回一个ENOMSG错误。否则,设置了flag为0,调用者被阻塞到下列某个事件发生为止:

(1)有一个所请求类型的消息可获取;

(2)由msqid标志的消息队列从系统中删除(这种情况下返回一个EIDRM错误);

(3)调用线程被某个捕获的信号所中断(这种情况下返回一个EINTR错误)。

flag参数中另有一位可以指定:MSG_NOERROR。当所接收消息的真正数据部分大于length参数时,如果设置了该位,msgrcv函数就只是截短数据部分,而不返回错误。否则,ms_grcv返回一个E2BIC错误。

成功返回时,msgrcv返回的是所接收消息中数据的字节数。它不包括也通过ptr参数返回的长整数消息类型所需的几个字节。

调用返回:成功返回读出消息的实际字节数,否则返回-1。

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。

struct msgbuf {

long mtype;       /* message type, must be > 0 */

char mtext[1];    /* message data */

};

 

对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,IPC_NOWAIT标志使得msgsnd调用非阻塞。造成msgsnd()等待的条件有两种:

(1)在指定的队列中已有太多的字节(对于该队列的msqid_ds结构中的msg_qbytes值);

(2)在系统范围存在太多的消息。

如果这两个条件中有一个存在,而且IPC_NUWAIT标志已指定,msgsnd就返回一个EAGAIN错误。如果这两个条件有一个存在,但是IPC_NUWAIT标志未指定,那么调用线程被投入睡眠直到:

(1)具备存放新消息的空间;

(2)由msqid标志的消息队列从系统中删除(这种情况下返回一个EIDRM错误);

(3)调用线程被某个捕获的信号所中断(这种情况下返回一个EINTR错误)。

调用返回:成功返回0,否则返回-1。

4)int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。

IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;

IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。

IPC_RMID:删除msqid标识的消息队列;

调用返回:成功返回0,否则返回-1。

 

 POSIX消息队列

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

功能:用来创建和访问一个消息队列
原型
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
参数
name: 某个消息队列的名字,必须以/打头,并且后续不能有其它/ ,形如/somename长度不能超过NAME_MAX(255)
oflag:与open函数类似,可以是O_RDONLY、O_WRONLY、O_RDWR,还可以按位或上O_CREAT、O_EXCL、O_NONBLOCK;
mode:如果oflag指定了O_CREAT,需要设置mode。
返回值:成功返回消息队列文件描述符;失败返回-1

 

功能:关闭消息队列
原型
mqd_t mq_close(mqd_t mqdes);
参数
mqdes : 消息队列描述符
返回值:成功返回0;失败返回-1

 

功能:删除消息队列
原型
mqd_t mq_unlink(const char *name);
参数
name: 消息队列的名字
返回值:成功返回0;失败返回-1

 

功能:获取/设置消息队列属性
原型
mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
返回值:成功返回0;失败返回-1

struct mq_attr {
long mq_flags;       /* Flags: 0 or O_NONBLOCK */
long mq_maxmsg;      /* Max. # of messages on queue */
long mq_msgsize;     /* Max. message size (bytes) */
long mq_curmsgs;     /* # of messages currently in queue */
};

mq_flags 是标志;mq_maxmsg 即一个消息队列消息个数上限;mq_msgsize即一条消息的数据上限;mq_curmsgs即当前消息个数

 

功能:发送消息
原型
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
参数
mqdes:消息队列描述符
msg_ptr:指向消息的指针
msg_len:消息长度
msg_prio:消息优先级
返回值:成功返回0;失败返回-1

 

功能:接收消息
原型
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
参数
mqdes:消息队列描述符
msg_ptr:返回接收到的消息
msg_len:消息长度,一般需要指定为msgsize_max
msg_prio:返回接收到的消息优先级
返回值:成功返回接收到的消息字节数;失败返回-1
注意:返回指定消息队列中最高优先级的最早消息,优先级最低为0

 

功能:建立或者删除消息到达通知事件
原型
mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification);
参数
mqdes:消息队列描述符
notification:
非空表示当消息到达且消息队列先前为空,那么将得到通知;
NULL表示撤消已注册的通知
返回值:成功返回0;失败返回-1
通知方式:
产生一个信号
创建一个线程执行一个指定的函数

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 for thread notification */
void *sigev_notify_attributes;
/* Thread function attributes */
};

sigev_notify 的取值有3个:

SIGEV_NONE:消息到达不会发出通知

SIGEV_SIGNAL:以信号方式发送通知,当设置此选项时,sigev_signo 设置信号的编号,且只有当信号为实时信号时才可以通过sigev_value顺带数据,参考这里

SIGEV_THREAD:以线程方式通知,当设置此选项时,sigev_notify_function 即一个函数指针,sigev_notify_attributes 即线程的属性

 

mq_notify 函数注意点:

1、任何时刻只能有一个进程可以被注册为接收某个给定队列的通知
2、当有一个消息到达某个先前为空的队列,而且已有一个进程被注册为接收该队列的通知时,只有在没有任何线程阻塞在该队列的mq_receive调用的前提下,通知才会发出。
3、当通知被发送给它的注册进程时,其注册被撤消。进程必须再次调用mq_notify以重新注册(如果需要的话),重新注册要放在从消息队列读出消息之前而不是之后。

参考博客

http://blog.csdn.net/jnu_simba/article/details/9089377

http://www.cnblogs.com/Anker/archive/2013/01/07/2848869.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">