System V IPC进程间通信——消息队列
System V IPC
IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行下列操作的一组机制
- 通过信号量与其他进程进行同步
- 向其他进程发送消息或者从其他进程接收消息
- 和其他进程共享一段内存
System V IPC 最初是在一个名为“Columbus Unix” 变体中引入的,现在大部分Unix系统中都可以找到。
IPC 资源
IPC资源包括信号量、消息队列、共享内存,IPC数据结构是在请求IPC时动态创建的,每个IPC资源都是持久的,除非被进程释放,否则永远驻留在内存中,直到系统关闭。根据IPC新资源时信号量、消息队列、还是共享内存区,分别调用semget(),msgget(),shmget()函数创建IPC资源。本章详细讲一下消息队列。
消息队列
进程间通过IPC消息队列通信,进程产生的每条消息都被发送到一个IPC消息队列中,这个消息一直存放在队列中,直到另外一个进程将其读走,故消息只适用于两个进程间通信。消息是由固定大小的首部,该首部是一个long int 型,用来存放消息类型和可变长度的正文组成。
创建、获取一个消息队列-msgget()
int msgget(key_t key, int msgflg)
key : 键值,每一个消息队列都有一个键值,一般有函数ftok生成,也可以自定定义,是一个32位整形
msgflg:消息队列标识符,同时设定该队列的权限,
msgflg的值为IPC_CREAT:如果不存在key值的消息队列,且权限不为0,则创建消息队列,并返回一个消息队列ID。如果存在,则直接返回消息队列ID。
msgflg的值为 IPC_CREAT | IPC_EXCL:如果不存在key值的消息队列,且权限不为0,则创建消息队列,并返回一个消息队列ID。如果存在,则产生错误。
发送消息函数-msgsnd()
int msgsnd(int msgid,const void *ptr,size_t nbytes,int flag);
msgid:通过函数msgget 返回的队列标识符。
ptr:发送消息结构,包括一个固定大小的首部和可变长度的正文
nbytes:正文长度
flag:值可以为0或者IPC_NOWAIT。为0时,当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列或者消息队列被删除。为IPC_NOWAIT时,当消息队列满了,msgsnd函数将不会等待,会立即出错返回EAGAIN
接收消息函数-msgrcv()
ssize_t msgrcv(int msgid,void *ptr,size_t nbytes,long type,int flag);
msgid:通过函数msgget 返回的队列标识符。
ptr:接收消息结构,包括一个固定大小的首部和可变长度的正文
nbytes:正文长度
type: 该值是发送消息结构的首部消息类型
type ==0 返回队列中的第一个消息
type > 0 返回队列中消息类型为type的第一个消息
type < 0 返回队列中消息类型值小于等于type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息
flag:可以为0、IPC_NOWAIT、IPC_EXCEPT
为0时,阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待
为IPC_NOWAIT时,如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
为IPC_EXCEPT时,与msgtype配合使用返回队列中第一个类型不为msgtype的消息
消息控制函数-msgctl()
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
msgid:通过函数msgget 返回的队列标识符。
cmd有三个,常用删除消息队列的为IPC_RMID;IPC_STAT:取此队列的msqid_ds结构,并将它存放在buf指向的结构中;IPC_SET:改变消息队列的状态,把buf所指的msqid_ds结构中的uid、gid、mode复制到消息队列的msqid_ds结构内。可设置的属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes
接下来是客户端和服务端的代码例子
server.c
<
key_t key = ftok("/home", 24);
int msgid = msgget(key,O_RDONLY);
if(msgid<0)
{
LOG_INFO("recv msgget error!");
exit(-1);
}
memset(&rcv, 0, sizeof(rcv));
msgrcv(msgid,&rcv,sizeof(rcv)-sizeof(rcv.msg_type),8,IPC_NOWAIT);
msgctl(msgid,IPC_RMID,NULL);
client.c
<
key = ftok("/home", 24);
msgid = msgget(key,IPC_CREAT|O_WRONLY|0777);
if(msgid<0)
{
LOG_INFO("msgget error!\n");
goto out;
}
msg.msg_type = 8;
strncpy(msg.modul_name,"hello", 5);
msgsnd(msgid,&msg,sizeof(msg)-sizeof(msg.msg_type),0);