您的位置:首页 > 运维架构 > Linux

Linux--进程通信之消息队列的双向通信

2017-06-13 21:37 591 查看

IPC是UNIX系统中进程间通信的统称,有三种称作XSI IPC:消息队列,信号量,共享内存。每创建这三种之间的任何一种,都相当于建立了一个XSI IPC对象

一、消息队列

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认 为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息 来避免命名管道的同步和阻塞问题。

二、消息队列的特点

1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.     

2.消息队列允许一个或多个进程向它写入与读取消息.      

3.消息队列的生命周期随内核。

4.消息队列可以实现双向通信。

三、操作函数

1.创建消息队列

#include<sys/msg.h>
int msgget(key_t key,int msgflag)


返回值:成功返回一个消息队列的msgid,作为进程对他的唯一标识;失败返回-1;

参数:

(1)key是系统对IPC资源的唯一标识符,在申请IPC资源前,先要获取此标识符。我们可以通过ftok函数过得它。

#include<sys.ipc.h>
key_t ftok(const char* path,int id)


第一个参数为给定的路径名,一般我们设置为当前路径“.”。第二个参数id为项目id,我们可以指定一个0~255之前的数来最为项目id。ftok函数会将路径名与项目id相结合,通过特定的方法得出标识符key。

(2)msgflag有两种创建方式:

a.IPC_CREAT单独使用,表示申请一个IPC资源,若申请的资源存在,则直接获取;若不存在,则创建新的IPC资源。

b.IPC_EXCL只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。如果将IPC_CREAT和IPC_EXCL一起使用,将返回一个新建的IPC标识符;如果该IPC资源已存在,返回-1.

注意:IPC_EXCL单独使用没有太大的意义,但是和IPC_CREAT一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。

2.销毁消息队列

#include<sys/msg.h>
int msgctl(int msqid,int cmd,struct msgid_ds *buf)


返回值:成功返回0,失败返回-1。

参数:
(1) msqid是msgget 的返回值。

(2)cmd指定了对该消息队列指定执行的命令。因此msgctl函数不只有删除消息队列的功能。

a. IPC_STAT,获取此消息队列的msqid_ds结构,并将其存放在buf指向的结构中。

b. IPC_SET,将参数buf中的某些字段复制到消息队列msgqid_ds结构中对应的字段。此命令只有以下两种用户可以执行,一种是其有效用户ID等于msg_perrm,msg_perm.uid。另一种是超级用户。

c. IPC_RMID,从系统中删除该消息队列以及该队列的所有数据。删除立即生效。

所以我们在删除消息队列时要将cmd设置成IPC_RMID.

3.发送消息

#include<sys/msg.h>
int msgsnd(int msqid,const void* ptr,size_t nbytes,int msgflag)

返回值:成功返回0,失败返回-1.

参数:

(1)msqid:消息队列的标识符。

(2)ptr一般指向一个结构体

struct msgbuf
{
long mtype;
char mtext[MYSIZE];
};

mtype设置为要发送数据类型。mtext存放发生的数据。

(3)nbytes位传输数据的大小。

(4)msgfalg:

msgflag可设置称IPC_NOWAIT,也可以不设置(设为0)。IPC_NOWAIT类似于文件I/O的非阻塞I/O标志。当消息队列已满,指定IPC_NOWAIT后,msgsnd立即出错返回EAGAIN,如果没有指定,该进程将会一直阻塞。知道直到消息队列有空间容纳发送的消息或者系统删除了此消息队列或者收到哦了一个信号,从信号处理程序中返回。

4.接受消息

#include<sys/msg.h>
ssize_t msgrcv(int msqid,void* msgp,size_t msgsz,long msgtyp,int msgflag)

返回值:成功返回接收到消息的bytes,失败返回-1。

参数:

(1)msqid:消息队列的标识符。

(2)msgp是指向消息缓冲区的指针,此位置是暂时存储发送和接收的消息,是一个用户可定义的通用结构。
(3)msgtyp从消息队列内读取的消息的形态。如果值为0,表示消息队列中的所有信息都会被读取。

四、消息队列的控制指令

当我们运行代码时,有时候可能遇到下面的情况:



说明资源文件已经存在了,不能再申请IPC资源了。我们可以利用指令查看当前的消息队列资源。

ipcs -q



我们可以从图中可以发现产生报错的资源文件,我们也可以利用指令杀死他,确保代码成功运行。

ipcrm -q [msgid]



五、代码实现

comm.h:

#ifndef _COMM_
#define _COMM_

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
c3d4
;
#include<string.h>
#include<unistd.h>
#define MYSIZE 128

#define PATHNAME "."

#define PROJID 0x1234

#define CLIENT_TYPE 1
#define SERVER_TYPE 2

struct msgbuf { long mtype; char mtext[MYSIZE]; };

int commMsgQueue(int flag);
int creatMsgQueue();
int getMsgQueue();
int sendMessage(int msgid,long type,const char *buf);
int destroyMsgQueue(int msgid);

#endif
comm.c
#include"comm.h"

int commMsgQueue(int flag)
{
key_t _k=ftok(PATHNAME,PROJID);
if(_k<0)
{
perror("ftok");
return -1;
}
int msg_id=msgget(_k,flag);
if(msg_id<0)
{
perror("msgget");
return -2;
}
return msg_id;
}

int creatMsgQueue()
{
return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}

int getMsgQueue()
{
return commMsgQueue(IPC_CREAT);
}

int sendMessage(int msg_id,long type,const char* msg)
{
struct msgbuf buf;
buf.mtype=type;
strcpy(buf.mtext,msg);

int ret=msgsnd(msg_id,&buf,sizeof(buf.mtext),0);
if(ret<0)
{
perror("msgsnd");
return -1;
}
return 0;
}

int recvMessage(int msg_id,long type,char out[])
{
struct msgbuf buf;
int size=msgrcv(msg_id,&buf,sizeof(buf.mtext),type,0);
if(size>0);
{
//	printf("%d",size);
buf.mtext[size-1]='\0';
//	printf("Debug 0:%s\n",buf.mtext);
strncpy(out,buf.mtext,sizeof(buf.mtext));
return size;
}
perror("msgrcv");
return -1;
}

int destroyMsgQueue(int msg_id)
{

if(msgctl(msg_id,IPC_RMID,NULL) < 0)
{
perror("msgctl");
return -1;
}
return 0;
}


server.c

#include"comm.h"

int main()
{
int msgid = creatMsgQueue();
char buf[MYSIZE*2];
while(1)
{
buf[0]=0;
recvMessage(msgid,CLIENT_TYPE,buf);
printf("client say: %s\n",buf);
printf("Please Enter# ");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1]=0;
sendMessage(msgid,SERVER_TYPE,buf);
}
}
destroyMsgQueue(msgid);
return 0;
}
client.c

#include"comm.h"

int main()
{
int msgid=getMsgQueue();
char buf[MYSIZE];

while(1)
{
buf[0]=0;
printf("Please Enter#");
fflush(stdout);
ssize_t  ret = read(0,buf,sizeof(buf)-1);
if(ret>0)
{
buf[ret-1]=0;
sendMessage(msgid,CLIENT_TYPE,buf);
}
recvMessage(msgid,SERVER_TYPE,buf);

printf("server say # %s\n",buf);
}
return 0;
}




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 消息队列