Linux支持system V 提供的进程间通信机制:消息队列、信号(semaphores)和共享内存。称为system V的IPC对象,可以通过系统调用(system call)对对象的创建者设置这些对象的存取权限。和文件的存取一样,这些对象的存取也要存取权限。
内核(Kernel)为每一个IPC对象维护了一个结构体如下:
struct ipc_perm {
key_t __key; /* Key supplied to xxxget(2) */
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; /* Permissions */
unsigned short __seq; /* Sequence number */
};
信号量、消息队列、共享内存都有一个这种结构体。
·消息队列
一、消息队列概念
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认
为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息
来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,
⽽而管道是基于字节流的,且消息队列的读取不⼀一定是先⼊入先出。
消息队列的上限:
消息队列与管道也有一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。
二、创建消息队列
函数:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
返回值:成功0-为消息队列标示符,失败返回-1。
参数:
①.key:可以认为是⼀一个端⼜⼝口号,也可以由函数ftok⽣生成。
②.msgflg:
IPC_CREAT 如果IPC不存在,则创建⼀一个IPC资源,否则打开操作。
IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建⽴立,否则就产⽣生错误。
如果两者同时使用的话就可以保证返回的标示符是新建的消息队列。(IPC_CREAT | IPC_EXCL)
消息队列的数据结构:
struct msqid_ds {
struct ipc_perm msg_perm; /* IPC对象结构体 */
struct msg *msg_first; /*消息队列头指针*/
struct msg *msg_last; /*消息队列尾指针*/
__kernel_time_t msg_stime; /*最后一次插入消息队列消息的时间*/
__kernel_time_t msg_rtime; /*最后一次接收消息即删除队列中一个消息的时间*/
__kernel_time_t msg_ctime; /* 最后修改队列的时间*/
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /*队列上所有消息总的字节数 */
unsigned short msg_qnum; /*在当前队列上消息的个数 */
unsigned short msg_qbytes; /* 队列最大的字节数 */
__kernel_ipc_pid_t msg_lspid;/* 发送最后一条消息的进程的pid */
__kernel_ipc_pid_t msg_lrpid;/* 接收最后一条消息的进程的pid */
};
三、往消息队列里读/写消息
msgrcv从队列中取用消息:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int
msgflg);
msgsnd将数据放到消息队列中:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
①.msqid:消息队列的标识码
②.msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是⼀个⽤用户可
定义的通⽤用结构体,
struct msgstru{
long mtype; //大于0
char mtext[];//用户自己定义大小
};
③.msgsz:消息的⼤大⼩小。
④.msgtyp:从消息队列内读取的消息形态。如果值为零,则表⽰示消息队列中的所有消息都
会被读取。
⑤.msgflg:⽤用来指明核⼼心程序在队列没有数据的情况下所应采取的⾏行动。如果msgflg和常
数IPC_NOWAIT合⽤用,则在msgsnd()执⾏行时若是消息队列已满,则msgsnd()将不会阻塞,⽽而
会⽴立即返回-1,如果执⾏行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定
错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取
阻塞等待的处理模式。
四、消息队列控制
int msgctl( int msgqid, int cmd, struct msqid_ds *buf );
参数:
①.msgqid :消息队列标识码
②.cmd:系统定义了 3 种 cmd 操作
: IPC_STAT , IPC_SET , IPC_RMID
IPC_STAT : 该命令.用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指
定的地址空间。
IPC_SET : 该命令.用来设置消息队列的属性,要设置的属性存储在buf中。
IPC_RMID : 从内核中删除 msqid 标识的消息队列。
五、key键
key是system V对IPC的名字,是int typedef来的。
ftok函数
函数ftok()把一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:
# include < sys/types.h>
# include < sys/ipc.h >
key_t ftok(const char *pathname, int proj_id);
下面模拟消息队列客户端和服务端消息的传递:
//comm.h
1 #ifndef _COMM_H_
2 #define _COMM_H_
3 #include<stdio.h>
4 #include<sys/types.h>
5 #include<error.h>
6 #include<sys/ipc.h>
7 #include<sys/msg.h>
8 #include<string.h>
9 #define PATHNAME "."
10 #define PROJ_ID 0x6666
11 #define SERVER_TYPE 1
12 #define CLIENT_TYPE 2
13 struct msgbuf
14 {
15 long mtype;
16 char mtext[1024];
17 };
18 int creat_msgqueue();//创建消息队列
19 int get_msgqueue();//接受消息队列
20 int destory_msgqueue(int msgid);//销毁消息队列
21 int senmsg(int msgid,int who,char*msg);//发送消息
22 int recvmsg(int msgid,int recvtype,char out[]);//接受消息
23 #endif
//comm.c
1 #include"comm.h"
2 static int commmsgqueue(int flags)
3 {
4 key_t _key=ftok(PATHNAME,PROJ_ID);
5 if(_key<0)
6 {
7 perror("ftok");
8 return -1;
9 }
10 int msgid=msgget(_key,flags);
11 if(msgid<0){
12 perror("msgget");
13 return -2;
14 }
15 return msgid;
16 }
17 int createmsgqueue()
18 {
19 return commmsgqueue(IPC_CREAT|IPC_EXCL|0666);
20 }
21 int getmsgqueue()
22 {
23 return commmsgqueue(IPC_CREAT);
24 }
25 int destorymsgqueue(int msgid)
26 {
27 if(msgctl(msgid,IPC_RMID,NULL)<0)
28 {
29 perror("msgctl");
30 return -1;
31 }
32 return 0;
33 }
34 int sendmsg(int msgid,int who ,char *msg)
35 {
36 struct msgbuf buf;
37 buf.mtype=who;
38 strcpy(buf.mtext,msg);
39 if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
40 {
41 perror("msgsnd");
42 return -1;
43 }
44 return 0;
45 }
46 int recvmsg(int msgid,int recvtype,char out[])
47 {
48 struct msgbuf buf;
49 if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvtype,0)<0)
50 {
51 perror("msgrcv");
52 return -1;
53 }
54 strcpy(out,buf.mtext);
55 return 0;
56 }
57
58
//client.c
1 #include"comm.h"
2 int main()
3 {
4 int msgid=createmsgqueue();
5 char buf[1024];
6 while(1)
7 {
8 buf[0]=0;
9 recvmsg(msgid,CLIENT_TYPE,buf);
10 printf("client# %s\n",buf);
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,SERVER_TYPE,buf);
18 printf("send done,wait recv...\n");
19 }
20 }
21 destorymsgqueue(msgid);
22 return 0;
23 }
//server.c
1 #include"comm.h"
2 int main()
3 {
4 int msgid=getmsgqueue();
5 char buf[1024];
6 while(1)
7 {
8 buf[0]=0;
9 printf("please enter#:");
10 fflush(stdout);
11 ssize_t s=read(0,buf,sizeof(buf));
12 if (s>0)
13 {
14 buf[s-1]=0;
15 sendmsg(msgid,CLIENT_TYPE,buf);
16 printf("send done,wait recv...\n");
17 }
18 recvmsg(msgid,SERVER_TYPE,buf);
19 printf("server# %s\n",buf);
20 }
21 return 0;
22 }
//Makefile
1 .PHONY:all
2 all:server client
3 client:client.c comm.c
4 gcc -o $@ $^
5 server:server.c comm.c
6 gcc -o $@ $^
7 .PHONY:clean
8 clean:
9 rm -f client server