Linux ipc System V 消息队列
消息队列提供了从一个进程向另一个进程发送有类型数据块的方法。
消息队列由操作系统内核提供,可以同时被两个进程同时看到。
每个数据块都被认为是有一个类型值的,接受者可以接受不同种类型的值。
每个消息对列的最大长度是有上限的(MSGMAX),每个消息队列的总字节数是有上限的(MSGMNB),系统上消息队列的总数也是有上限的(MSGMNI)。
I P C 标识符:每一个内核中的IPC结构(消息队列,信号量,共享存储段)都用一个非负整数的标识符(identifier)加以引用。当一个消息队列发送或取消息,只需要知道其队列标示符。
// 内核为每个IPC对象维护一个数据结构(/usr/include/linux/ipc.h)
struct ipc_perm
{
key_t __key; /* key supplied to xxxget(2) */
//每一个ipc结构都有一个唯一的表示符,其值不重要,重要的是其具有唯一性,使用消息队列的双方,通过_key来找到该消息队列
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permission */ //权限
unsigned short __seq; /* Sequeence number*/
}
获取_key值的两种方法:
- IPC_PRIVATE :/由操作系统指定
- 通过ftok()获取_key值
ftok()函数
#include <sys/ipc.h>
#include <sys/types.h>
key_t ftok(const char* path, int id);
//ftok 函数把一个已存在的路径名和一个整数标识转换成一个key_t值,即IPC关键字
//path 参数就是你指定的文件名(已经存在的文件名),一般使用当前目录。当产生键时,只使用id参数的低8位。
//id 是子序号, 只使用8bit (1-255)
//返回值:若成功返回键值,若出错返回(key_t)-1
//在一般的UNIX实现中,是将文件的索引节点号取出(inode),前面加上子序号的到key_t的返回值
//消息队列的结构 ( /usr/include/linux/msg.h)
// message queue id
// defined in <linux/ipc.h>
struct msqid_ds
{
struct ipc_perm msg_perm;
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 butes on queue */
unsigned short msg_qnum; /* number of messages in queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
}
消息队列函数
msgget函数
//创建和访问一个消息队列
#include <sys/msg.h>
#include <sys/ipc.h>
int msgget(key_t key, int msgflag);
//参数:
//key 使用ftok生成的标识队列唯一性的键值
msflg:由九个权限标志构成,它们的用法与创建文件时的mode模式标志一样
当IPC_CREAT和IPC_EXCL同时被使设置:若消息队列不存在,则创建一个全新的消息队列,如果存在则出错返回
当IPC_CREAT单独使用时,如果消息队列不存在则新建一个,如果存在则打开
IPC_EXCL单独使用无意义
通过|使用 也可以与权限|在一起
//返回值:成功返回一个非负整数,及消息队列的表示码(不是key),失败返回-1;
msgctl函数
//消息队列控制函数
int msgctl(int msgqid,int cmd,struct msgqid_ds *buf)
参数:msgqid:由msgget返回的消息队列标识码
cmd:将要采取的动作
//返回值:成功返回0,失败返回-1
cmd采取的三个操作:
IPC_STAT | 把msqid_ds结构中的数据设置为消息队列的当前关联值 |
---|---|
IPC_SET | 在进程由足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的关联值 |
IPC_RMID | 删除消息队列 |
msgsnd函数
//把一条消息添加到消息队列中
int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflg);
参数:
msqid: msgget函数返回的标识码
msgp: 指向准备发送信息的指针
msgsz: msgp指向消息的长度,不包含保存消息类型的long int 的长度
msgflg=IPC_NOWWAIT表示队列不满
由系统提供的消息结构
struct msgbuf{
long mtype;
char mtext[1];//数组的大小可以任意设置
}
//消息结构必须小于系统规定的上限值
//必须以一个long int长整数开始,接收函数利用这个长整数确定消息的类型
msgrcv函数
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int msgflag);
//msgsz:消息的大小
//ptr :指向一个长整形数,将返回的消息类型存储在//其中(即结构体 struct msgbuf 的mtype成员)
//nbyte :是存放实际消息数据的缓冲区的长度
//type :可以指定想要哪一种消息
/* type == 0 返回队列的第一个消息
type > 0 返回队列中消息类型type的第一个消息
type < 0 返回队列中消息类型值小于或等于type绝对值的消息,如果这种消息有若干个,则类型值最小的消息*/
msgflag:消息类型,这个参数是控制函数行为的标识。取值可以是0,标识忽略。
IPC_NOWAIT:如果消息队列为空,则返回一个ENOMSG,并将控制权交回给调用函数的进程。如果不指定这个参数,那么进程将被阻塞知道函数可以从队列中取得符合条件的消息为止。
0 表示不关心,忽略此行为
返回值:成功执行返回消息的数据部分的长度,若出错则返回-1
msgrcv成功执行时,内核更新与该消息队列相关联的msqid_ds结构以指示调用者的进程ID(msg_lrpid)和调用时间(msg_rtime),并将队列中的消息数(msg_qnum)减1。*
查看消息队列&&删除消息队列
- ipcs -q //查看消息队列
- ipcrm -q msgid //删除一个消息队列
[wens@MiWiFi-R1CL-srv msgqueue]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x6603b8fa 262144 wens 644 0 0
[wens@MiWiFi-R1CL-srv msgqueue]$ ipcrm -q 262144
[wens@MiWiFi-R1CL-srv msgqueue]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
//键值 //消息队列标识码 //权限 //数据量 //消息条数
消息队列创建实例:实现client&&server间的通信
1
2 .PHONY:all
3 all:client server
4
5
6 client:client.c common.c
7 gcc -o $@ $^
8 server:server.c common.c
9 gcc -o $@ $^
10 .PHONY:clean
11 clean:
12 rm -f client server all
common.h
1 #ifndef __COMMON_H_
2 #define __COMMON_H_
3
4
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11
12 #include <sys/ipc.h>
13 #include <sys/msg.h>
14
15 #define PATHNAME "." //ftok 的路径参数设置为当前路径
16 #define PROJ_ID 0x666 //ftok 的另一个参数
17 #define SERVER_TYPE 1
18 #define CLIENT_TYPE 2
19
E> 20 struct msgbuf
21 {
22 long mtype;
23 char mtext[1024];
24 };
25
26 int creatMsgque(); //创建消息队列
27 int getMsgque(); //打开消息队列
28 int destoryMsg(int msgid); //删除一个消息队列
29 int sendMsg(int msgid,const void* msgp,int type);//向消>
息对列中添加消息
30 int recvMsg(int msgid,void* str,int type);
31 #endif
common.c
1 #include "common.h"
2
3 static int commonsqueue(int flags)//将该函数设置为静态外
部不可见
4 {
5
6 key_t _key=ftok(PATHNAME,PROJ_ID); //生成key值
7 if(_key<0)
8 {
9 perror("ftok error");
10 exit(1);
11 }
12
13 int msgid=msgget(_key,flags|0644); //创建一个消息队列
14 if(msgid<0)
15 {
16 perror("msgid error");
17 return -1;
18 }
19
20
21 return msgid; //返回创建队列后的识别码
22
23 }
24
25 int creatMsgque() //创建消息队列
26 {
27 return commonsqueue(IPC_CREAT|IPC_EXCL);
28
29 }
30 int getMsgque()
31 {
32 return commonsqueue(IPC_CREAT);//仅用于打开一个消息队>
列
33 }//打开消息队列
34 int destoryMsg(int msgid)//删除一个消息队列
35 {
36
37 if( msgctl(msgid,IPC_RMID ,NULL ) <0) //第三个参数表
示不需要队列的属性信息
38 {
39 perror("delete error");
40 return -1;
41 }
42 }
43
44
45 int sendMsg(int msgid,const void* msgp,int type)//向消息
对列中添加消息
46 {
47
48 struct msgbuf buf;
49 buf.mtype=type;
E> 50 strcpy(buf.mtext,msgp);
51 if( msgsnd (msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
52 {
53 perror("senmsg error");
54 return -1;
55 }
56 }
57
58
59
60
61 int recvMsg(int msgid,void* str,int type)//从消息队列中>
取消息
62 {
63 struct msgbuf buf;
64 if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),type,0 )
<0)
65 {
66 perror("receive msg error");
67 return -1;
68 }
E> 69 strcpy(str,buf.mtext);
70 return 0;
71 }
72
server.c
1 #include "common.h"
2
3 int main()
4 {
5
6
7 int msgid=creatMsgque();
8
9 char buf[1024];
10
11 while(1)
12 {
13 buf[0]=0;
14 recvMsg(msgid,buf,CLIENT_TYPE);
15 if(strcmp(buf,"exit")==0)
16 {
17 printf("client quit\n");
18 break;
19 }
20 printf("client # %s \n",buf);
21 printf("please enter # ");
22 fflush(stdout);
23 ssize_t s=read(0,buf,sizeof(buf)-1);
24 if(s>0)
25 {
26 buf[s-1]=0;//消除回车并以'\0’结尾
27 sendMsg(msgid,buf,SERVER_TYPE);
28 }
29 else if(s<0)
30 {
31 perror("sendmsg error");
32 break;
33 }
34 }
35 destoryMsg(msgid);
36 return 0;
37 }
client.c
1 #include "common.h"
2
3 int main()
4 {
5 char buf[1024];
6
7 int msgid=getMsgque();
8
9 while(1)
10 {
11
12 printf("please enter # ");
13 fflush(stdout);
14 ssize_t s=read(0,buf,sizeof(buf)-1);
15 if(s>0)
16 {
17 buf[s-1]=0; //消除在发送信息时输入的回车符
18
19 sendMsg(msgid,buf,CLIENT_TYPE);
20 if(strcmp(buf,"exit")==0)
21 {
22 break;
23 }
24 }else if(s<0){
25
26 perror("read error\n");
27 exit(1);
28 }
29 recvMsg(msgid,buf,SERVER_TYPE);
30 printf("server # %s\n",buf);
31 }
32 return 0;
33 }