Linux进程间通信——使用消息队列
消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。
----->双向通信
----->用于随意进程
------>面向数据块(链表)
------>自带同步互斥
------->生命周期随内核
消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。
进程间通过消息队列通信,主要是:创建或打开消息队列,添加消息,读取消息和控制消息队列。
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)------Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。
Linux提供了一系列消息队列的函数接口来让我们方便地使用它来实现进程间的通信
key -->某个消息队列的名字
msgflg -->由9个权限标识符构成,
返回值:成功时返回一个非负整数,即该消息队列的标识符,失败返回-1.
long mtype;//消息的类型
char mtext[1];消息数据
};
msg_sz是msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。(结构里mtext数组的大小)
msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.
代码实例:
系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。
当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。
server.c
1 #include "messages.h"
2 int main(){
3 int msgid = createMsgQueue();
4 char buf[1024];
5 while(1){
6 buf[0]=0;
7 recvMsg(msgid,CLIENT_TYPE,buf);
8 printf("client# %s\n", buf);
9 fflush(stdout);
10 ssize_t s = read(0,buf,sizeof(buf));
11 if(s>0){
12 buf[s-1]=0;
13 sendMsg(msgid,SERVER_TYPE,buf);
14 printf("send done,wait recv...\n");
15 }
16 }
17 destroyMsgQueue(msgid);
18 return 0;
19 }
client.c
1 #include "messages.h"
2 #include<stdio.h>
3 int main(){
4 int msgid = getMsgQueue();
5 char buf[1024];
6 while(1){
7 buf[0]=0;
8 printf("please enter# ");
9 fflush(stdout);
10 size_t s = read(0,buf,sizeof(buf));
11 if(s>0){
12 buf[s-1]=0;
13 sendMsg(msgid,CLIENT_TYPE,buf);
14 printf("send done,wait recv...\n");
15 }
16 }
17 return 0;
18 }
messages.c
1 #include "messages.h"
2 static int commMsgQueue(int flags){
3 key_t key = ftok(PATHNAME,PROJ_ID);
4 if(key<0){
5 perror("ftok");
6 return -1;
7 }
8 int msgid = msgget(key,flags);
9 if(msgid<0){
10 perror("msgget");
11 }
12 return msgid;
13 }
14 int createMsgQueue()
15 {
16 return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
17
18 }
19 int getMsgQueue(){
20 return commMsgQueue(IPC_CREAT);
21 }
22 int destroyMsgQueue(int msgid){
23 //控制消息队列
24 if(msgctl(msgid,IPC_RMID,NULL)<0){
25 perror("msgctl");
26 return -1;
27 }
28 return 0;
29 }
30 int sendMsg(int msgid,int who,char *msg){
31 struct msgbuf buf;
32 buf.mtype = who;
33 strcpy(buf.mtext,msg);
34 //把消息添加到队列中
35 if(msgsnd(msgid,(void *)&buf,sizeof(buf.mtext),0)<0){
36 perror("msgnd");
37 return -1;
38 }
39 return 0;
40 }
41 int recvMsg(int msgid,int recvType,char out[]){
42 struct msgbuf buf;
43 if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0)<0){
44 perror("msgrcv");
45 return -1;
46
47 }
messages.h
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct msgbuf{
long mtype;
char mtext[1024];
};
int createMsgQueue();
int getMsgQueue();
int destoryMsgQueue(int msgid);
int sendMsg(int msgid,int who,char *msg);
int recvMsg(int msgid,int recvType,char out[]);