Linux-进程间通信:消息队列
2018-03-31 13:14
232 查看
消息队列
之前学到了进程间通信主要是为了让两个毫不相关的进程之间看到一份公共的资源。管道提供了进程之间数据流的传输。
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法。
每个数据块被认为是有类型的,接收者进程接受数据块是可以有不同的类型的。
消息队列的不足:每个消息的最大长度是有上限的,每个消息队列的总的字节数是有上限的,系统上消息队列的总数也是有上限的。
ipc对象数据结构
操作系统内核为每个ipc对象都维护了一个数据结构
在/usr/include/linux/ipc.h中可以查看到:
也可以在/usr/include/linux/msg.h中查看到消息队列的数据结构:
如何创建
首先介绍相关接口函数;msgget函数:用来创建和获得一个消息队列。
返回值:成功返回一个非负整数,即该消息队列的标识码。失败返回-1.
注意:
这个函数的功能是用来区创建和获得,那么如何区分这两个功能?
参数的不同,决定了函数的功能。
1. IPC_CREAT + IPC_EXCL:
若消息队列已存在,出错返回
若消息队列不存在,则创建一个消息队列并且返回。
2. IPC_CREAT
若消息队列不存在,则创建消息队列然后返回
若消息队列已存在,则返回已存在的消息队列
可以看到该函数中参数有一个key_t key,是某个消息队列的名字,那么如何获得它?用以下函数:用来获得一个key值(是一个有符号整数),失败返回-1.
#include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);
msgctl函数:消息队列的控制函数
参数:msgid:由msgget函数返回的消息队列标识码
cmd:是将要采取的动作:
IPC_STAT:把msgid_ds中的数据设置为消息队列的当前关联值
IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msgid_ds数据结构中给出的值
IPC_RMID:删除消息队列(这里只使用删除消息队列该参数)
有了创建获得和销毁,下面就要有接收信息和发送消息:
msgsnd函数:把一条消息添加到消息队列中。
参数:
msgid:由msggst函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息
msgsz:是msgp指向的消息长度
msgflg:控制着当前消息队列满或达到系统上限时将要发生的事情。
返回值:
成功返回0;失败返回-1;
msgrcv函数:从一个消息队列接收消息
参数:msgid:由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备接受的消息
msgsz:是msgp指向的消息长度
msgtype:可以实现接受优先级的简单形式
msgflg:控制着当前消息队列中没有相应类型的消息可供接受时将要发生的事情
用消息队列实现两个进程通信
使用消息队列实现client与server聊天的效果:comm.c #include "comm.h" int CommMsgQueue(int flags) { key_t key = ftok(PATHNAME,PROJ_ID); if(key < 0) { perror("ftok"); return -1; } int msgid=msgget(key,flags); if(msgid<0) { perror("msgget"); } return msgid; } int CreatMsgQueue() { return CommMsgQueue(IPC_CREAT|IPC_EXCL|0666); } int GetMsgQueue() { return CommMsgQueue(IPC_CREAT); } int DestroyMsgQueue(int msgid) { if(msgctl(msgid,IPC_RMID,NULL)<0) { perror("msgctl"); return -1; } return 0; } int SendMsg(int msgid,int who,char* msg) { struct msgb buf; buf.mtype=who; strcpy(buf.mtext,msg); if(msgsnd(msgid,(void *)&buf,sizeof(buf.mtext),0)<0) { perror("msgsnd"); return -1; } return 0; } int RecvMsg(int msgid,int recvtype,char out[]) { struct msgb buf; if(msgrcv(msgid,(void* )&buf,sizeof(buf.mtext),recvtype,0)<0) { perror("msgrcv"); return -1; } strcpy(out,buf.mtext); return 0; }
在comm.c中封装以上函数,实现创建消息队列,获得消息队列,接收消息与传送消息以及销毁该消息队列的功能。
server.c #include "comm.h" int main() { int msgid=CreatMsgQueue(); char buf[1024]; while(1) { buf[0]=0; RecvMsg(msgid,CLIENT_TYPE,buf); if(strcmp("quit",buf)==0) { printf("client quit! server quit!\n"); break; } printf("client# %s\n",buf); printf("please input#"); fflush(stdout); ssize_t s=read(0,buf,sizeof(buf)); if(s>0) { buf[s-1]=0; SendMsg(msgid,SERVER_TYPE,buf); printf("send done,wait recv...\n"); } } DestroyMsgQueue(msgid); return 0; }
client.c #include "comm.h" int main() { int msgid=GetMsgQueue(); char buf[1024]; while(1) { printf("please input#"); fflush(stdout); ssize_t s=read(0,buf,sizeof(buf)); if b9ef (s>0) { buf[s-1]=0; SendMsg(msgid,CLIENT_TYPE,buf); printf("send done,wait recv...\n"); } if(strcmp("quit",buf)==0) { printf("client quit\n"); break; } RecvMsg(msgid,SERVER_TYPE,buf); printf("server# %s\n",buf); } return 0; }
效果如下:
但是将server与client异常终止之后,会发现再次运行server出错了。
原因是因为,该消息队列已经存在了,需要将它删除掉,如何查看与删除已存在的消息队列。
ipcs -q
查看已存在的消息队列
ipcsrm -q (msgid)
看到此时被删除了。再此运行server就没有问题了。
相关文章推荐
- Linux进程间通信——使用消息队列
- Linux进程间通信-消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——使用消息队列
- 嵌入式 Linux进程间通信(七)——消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信--信号,管道,消息队列,信号量,共享内存,socket
- Linux进程间通信—使用消息队列
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
- Linux进程间通信(IPC)编程实践(十二)Posix消息队列--基本API的使用
- Linux 进程间通信(一)(经典IPC:消息队列、信号量、共享存储)
- Linux进程间通信——使用消息队列
- Unix/Linux 进程间通信之消息队列应用实例
- Linux进程间通信——使用消息队列
- Linux 进程间通信--- 消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——使用消息队列
- linux 进程间通信---消息队列
- linux进程间通信之消息队列