一、消息队列
(1)消息队列提供了一个从一个进程像另外一个进程发送一块数据的方法
(2)每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
(4)消息队列的生命周期是随内核的
二、相关的数据结构和函数
1、IPC对象数据结构(/usr/include/linux/ipc.h)
内核为每一个IPC对象维护一个数据结构,其结构体内容如下:
struct ipc_perm
{
__kernel_key_t key;//某个消息队列的名字
__kernel_uid_t uid;//用户的户标识
__kernel_gid_t gid;// Effective GID of owner
__kernel_uid_t cuid;// Effective UID of creator
__kernel_gid_t cgid;//Effective GID of creator
__kernel_mode_t mode; //Permissions
unsigned short seq;//Sequence number
};
2、消息队列结构(/usr/include/linux/msg.h)
struct msqid_ds {
struct ipc_perm msg_perm; //IPC结构体为共有的
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
3、消息队列函数
1)创建一个消息队列
msgget函数
注释:(1)这个函数的功能是用来创建和访问一个消息队列;
(2)参数key:代表某个消息队列的名字; key 值可由ftok函数获得
(3)msgflg:由九个权限标志构成,包括IPC_CREAT和IPC_EXCL两个属性,这俩个属性可以保证创建一个全新的消息队列
(4) 返回值:成功返回0,失败返回-1
2)消息队列控制函数
msgctl函数
注释:
(1)参数msqid:由msgget函数返回的消息队列标识符得到
(2)参数cmd:是将要采取的动作,有三个可取值:
IPC_STAT—从内核数据结构复制信息,找到msqid_ds数据结构,并将其信 息保存到buf所指向的空间
IPC_SET—写入一些msqid_ds结构成员的值,并且由buf指向与之相关的内核数据结构。
IPC_RMID—删除消息队列
(3)返回值:成功返回0,失败返回-1
3)msgsnd函数
注释:(1)功能:把一条消息添加到消息队列中
(2)参数msqid:由msgget函数返回的消息队列标识符
(3)参数msgp:是一个指向准备发送消息的指针
(4)参数msgz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
(5)参数msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情,msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误
(6)返回值:成功返回0,失败返回-1
4)msgrcf函数
注释:
(1)功能:从一个消息队列接收消息
(2)参数:
msqid:由msgget函数返回的消息队列标识码
msgp:只想准备接收消息的指针
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgtype:可以实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
(3)返回值:成功返回实际放入接收缓冲区里去的字符个数,失败返回-1;
三、实例验证
我们可以利用消息队列实现一个server/client通信的例子
首先是makefile文件代码:
1 # cat Makefile
2 .PHONY:all
3 all:client server
4
5 client:client.c comm.c
6 gcc -o $@ $^
7 server:server.c comm.c
8 gcc -o $@ $^
9
10 .PHOMY:clean
11 clean:
12 rm -f client server
client.c文件代码:
1 include"comm.h"
2
3 int main()
4 {
5 int msgid=getMsgQueue();
6
7 char buf[1024];
8 while(1)
9 {
10 buf[0]=0;
11 printf("Please Enter:> ");
12 fflush(stdout);
13 ssize_t s=read(0,buf,sizeof(buf));
14 if(s>0)
15 {
16 buf[s-1]=0;
17 sendMsg(msgid,CLIENT_TYPE,buf);
18 printf("send done,wait recv... \n");
19 }
20 recvMsg(msgid,SERVER_TYPE,buf);
21 printf("server:> %s\n",buf);
22 }
23 return 0;
24 }
server.c文件代码
1 #include"comm.h"
2
3 int main()
4 {
5 int msgid=creatMsgQueue();
6
7 char buf[1024];
8 while(1)
9 {
10 buf[0]=0;
11 recvMsg(msgid,CLIENT_TYPE,buf);
12 printf("client:> %s\n",buf);
13
14 printf("please Enter:> ");
15 fflush(stdout);
16 ssize_t s=read(0,buf,sizeof(buf));
17 if(s>0)
18 {
19 buf[s-1]=0;
20 sendMsg(msgid,SERVER_TYPE,buf);
21 printf("send done,wait recv...\n");
22 }
23 }
24 destoryMsgQueue(msgid);
25 return 0;
26 }
comm.h文件代码
1 #ifndef _COMM_H_
2 #define _COMM_H_
3
4 #include<stdio.h>
5 #include<sys/types.h>
6 #include<sys/ipc.h>
7 #include<sys/msg.h>
8 #include<string.h>
9
10 #define SERVER_TYPE 1
11 #define CLIENT_TYPE 2
12
13 #define PATHNAME "."
14 #define PROJ_ID 0X6666
15
16 struct msgbuf
17 {
18 long mtype;
19 char mtext[1024];
20 };
21
22 int creatMsgQueue();//创建消息队列函数
23 int getMsgQueue();//获得消息队列函数
24 int destoryMsgQueue(int msgid);//销毁消息队列函数
25 int sendMsg(int msgid,int who,char*msg);//发送消息函数
26 int recvMsg(int msgid,int recvType,char out[]);//接收消息函数
27
28 #endif
comm.c文件代码
1 #include "comm.h"
2
3 static int commMsgQueue(int flags)
4 {
5 key_t _key=ftok(PATHNAME,PROJ_ID);
6 if(_key<0)
7 {
8 perror("ftok");
9 return -1;
10 }
11 int msgid=msgget(_key,flags);
12 if(msgid<0)
13 {
14 perror("msgget");
15 }
16 return msgid;
17 }
18
19 int creatMsgQueue()
20 {
21 return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
22 }
23
23
24 int getMsgQueue()
25 {
26 return commMsgQueue(IPC_CREAT);
27 }
28
29 int destoryMsgQueue(int msgid)
30 {
31 if(msgctl(msgid,IPC_RMID,NULL)<0){
32 perror("msgctl");
33 return -1;
34 }
35 }
36
37 int sendMsg(int msgid,int who,char*msg)
38 {
39 struct msgbuf buf;
40 buf.mtype=who;
41 strcpy(buf.mtext,msg);
42
43 if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
44 {
45 perror("msgsnd");
46 return -1;
47 }
48 return 0;
49 }
50
51 int recvMsg(int msgid,int recvType,char out[])
52 {
53 struct msgbuf buf;
54 if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0)<0)
55 {
56 perror("msgrcv");
57 return -1;
58 }
59 strcpy(out,buf.mtext);
60 return 0;
61 }
运行结果
这里需要打开两个终端,一个运行server,一个运行client,如此便可以实现对话了。
运行client:
运行server:
四、ipcs&ipcrm指令
在重复运行的时候,会出现异常终止,再次运行server时就会报错
这个时候我们可以用ipcs 和ipcrm这两条指令进行处理
ipcs:用于查看IPC资源
ipcrm:手动删除IPC资源
具体使用方法如下:
1、当没有运行代码时,我们对IPC资源进行查看,可以发现是没有消息队列的
2、现在运行我们写好的关于消息队列的文件,再次查看IPC资源,会发现消息队列创建成功,消息队列id为327680
3、若要删除消息队列,则可以进行如下操作:输入ipcrm -q 消息队列id