对于进程我们知道一个进程拥有独立的用户地址空间,在一个进程中的全局变量在另一个进程中是看不到的,那仫进程间是如何通信的呢?
在进程与进程间的通信必须要经过内核,在内核中开辟一块缓冲区,其中的一个进程把数据从用户空间拷贝到内核缓冲区,另一个进程则从该内核缓冲区中把数据读走,内核提供的这种机制叫进程间通信(IPC)。这里主要讲的的是进程间通信的第一种方式- 消息队列。
什么是消息队列?
消息队列就是消息的链表,存放在内核当中,由消息队列标识符标识,消息队列提供一种进程之间数据块传送的方法,每个数据块都被认为是一种类型。每个进程都有一个与之相关联的消息队列。
注意:
还存在一种进程间通信的机制就是 匿名管道和 命名管道,管道的生命周期是随进程的,只要该进程消亡了该管道就会随之消失。但是消息队列却是随内核的,就算进程退出,不去手动的释放消息队列,消息队列依然是存在的。
我们可以使用ipcs -q进行查看操作系统的消息队列,使用ipcrm -q msgid进行销毁消息队列。
消息队列的创建
使用msgget函数,我们可以创建或者访问一个消息队列,
创建消息队列之前用ftok得到一个key标识符。
key_t ftok(const char *pathname, int proj_id);
key:可以认为是端口号,也可以由函数ftok生成。
然后创建队列。
int msgget(key_t key, int msgflg);
msgflg:它一般由这两个宏实现:IPC_CREAT and IPC_EXCL 。
IPC_CREAT:如果IPC不存在则创建一个新的IPC,否则执行打开操作。
IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
注意:
如果单独使用IPC_CREAT,XXXget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符;如果将IPC_CREAT和IPC_EXCL标志一起使用,XXXget()将返回一个新建的IPC标识符;如果该IPC资源已存在,或者返回-1。IPC_CREAT和IPC_EXCL一起使用时可以保证该对象是新建的而不是打开一个已经存在的消息队列。
发送消息和接受消息
使用消息队列当然涉及到发送消息和接受消息,msgsnd函数和msgrcv函数提供了这两个功能。
msgsnd函数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
在这里我们需要关注第二个参数,第二个参数是指向一个结构体的一个指针,这个结构体叫做 struct msgbuf
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[用户指定大小]; /* message data */
};
这个结构体第一个变量是消息的类型,第二个是存放消息的数组。
msqid:就是该消息队列的标识码;
magp:是指向一个结构体的指针,这个结构体是msgbuf;msgp指向消息队列的缓冲区的指针,用来暂时存储和发送的消息;
msgsz:指的是当前发送消息的大小,而不是整个结构体的大小。msgbuf结构在下列给出,就是说msgsz的值不包括mtype消息类型的大小,而这个参数只是用来指明消息的长度的。
msgflg:这个参数用来指明在没有数据的情况下所需要做的操作,这个操作我们经常使用IPC_NOWAIT,这样当消息队列满了的时候,再向消息队列中发送消息,不会发生阻塞现象。
当调用成功,也就是说此时已经有消息写入消息队列中,接收消息的一方可以调用函数msgrcv:
msgrcv函数
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
将消息从消息队列中取出函数msgrcv,参数类似msgsnd函数,在这里主要提到的就是msgtyp,它的作用是:从消息队列中读取的消息形态。如果值为0则表示所有消息队列中的所有消息都会被读取。
消息队列的通信实例
使用服务器进程创建消息队列,然后使用客户进程进行send数据,服务进程接收数据。
comm.h
#ifndef __COMM_H__
#define __COMM_H__
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<string.h>
#include<time.h>
#define _MSG_SIZE_ 1024
#define FILEPATH "."
#define PROJID 0
#define PERM 0666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#define MYSIZE 128
struct msgbuf
{
long mtype;
char mtext[MYSIZE];
};
static int commmsg(int msgflg);
int createmsg();
int sendmsg(int msgid,long type,const char * msg);
int getmsg();
int recvmsg(int msgid,int type,char out[]);
int destorymsg(int msgid);
#endif //!__COMM_H__
comm.c
#include"comm.h"
static int commmsg(int msgflg)
{
key_t key=ftok(FILEPATH,PROJID);
if(key<0)
{
perror("ftok");
return -1;
}
int msqid=msgget(key,msgflg);
if(msqid<0)
{
perror("msgget");
return -2;
}
return msqid;
}
int createmsg()
{
return commmsg(IPC_CREAT|IPC_EXCL|PERM);
}
int getmsg()
{
return commmsg(0);
}
int sendmsg(int msgid,long type,const char *msg)
{
struct msgbuf buf;
buf.mtype=type;
strcpy(buf.mtext,msg);
int id=msgsnd(msgid,&buf,sizeof(buf.mtext),0);
if(id<0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvmsg(int msgid,int type,char out[])
{
struct msgbuf buf;
int size=msgrcv(msgid,&buf,sizeof(buf.mtext),type,0);
if(size>0)
{
//buf.mtext[size]='\0';
strncpy(out,buf.mtext,size);
return 0;
}
perror("msgrcv");
return -1;
}
int destorymsg(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl");
return -1;
}
return 0;
}
client.c
[cpp] view plain copy print?
#include"comm.h"
int main()
{
int msg_id=GetMsgQueue();
char buf[MYSIZE];
char out[2*MYSIZE];
while(1)
{
printf("please enter#");
fflush(stdout);
ssize_t size=read(0,buf,sizeof(buf)-1);
if(size > 0)
{
buf[size-1]='\0';
SendMsgQueue(msg_id,CLIENT_TYPE,buf);
}
if(ReceiveMsgQueue(msg_id,SERVER_TYPE,out) < 0)
{
break;
}
printf("server echo#%s\n",out);
}
return 0;
}
client.c
#include"comm.h"
int main()
{
int msqid=getmsg();
char buf[MYSIZE];
char out[2*MYSIZE];
while(1){
printf("please input:");
fflush(stdout);
ssize_t _s=read(0,buf,sizeof(buf)-1);
if(_s>0)
{
buf[_s]='\0';
sendmsg(msqid,CLIENT_TYPE,buf);
}
if(recvmsg(msqid,SERVER_TYPE,out)<0)
{
break;
}
printf("server echo :%s\n",out);
}
return 0;
}
server.c
#include"comm.h"
int main()
{
int msqid=createmsg();
char buf[2*MYSIZE];
while(1){
if(recvmsg(msqid,CLIENT_TYPE,buf)<0)
{
break;
}
printf("client# %s\n",buf);
if(sendmsg(msqid,SERVER_TYPE,buf)<0)
{
break;
}
}
destorymsg(msqid);
return 0;
}
测试图如下: