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

Linux-进程间通信:消息队列

2018-03-31 13:14 232 查看

消息队列

之前学到了进程间通信主要是为了让两个毫不相关的进程之间看到一份公共的资源。

管道提供了进程之间数据流的传输。

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法。

每个数据块被认为是有类型的,接收者进程接受数据块是可以有不同的类型的。

消息队列的不足:每个消息的最大长度是有上限的,每个消息队列的总的字节数是有上限的,系统上消息队列的总数也是有上限的。

ipc对象数据结构

操作系统内核为每个ipc对象都维护了一个数据结构

在/usr/include/linux/ipc.h中可以查看到:



也可以在/usr/include/linux/msg.h中查看到消息队列的数据结构:



如何创建

首先介绍相关接口函数;

msgget函数:用来创建和获得一个消息队列。

返回值:成功返回一个非负整数,即该消息队列的标识码。失败返回-1.



注意:

这个函数的功能是用来区创建和获得,那么如何区分这两个功能?

参数的不同,决定了函数的功能。

1. IPC_CREAT + IPC_EXCL:

若消息队列已存在,出错返回

若消息队列不存在,则创建一个消息队列并且返回。

2. IPC_CREAT

若消息队列不存在,则创建消息队列然后返回

若消息队列已存在,则返回已存在的消息队列

可以看到该函数中参数有一个key_t key,是某个消息队列的名字,那么如何获得它?用以下函数:用来获得一个key值(是一个有符号整数),失败返回-1.

#include  <sys/types.h>
#include  <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);


msgctl函数:消息队列的控制函数



参数:msgid:由msgget函数返回的消息队列标识码

cmd:是将要采取的动作:

IPC_STAT:把msgid_ds中的数据设置为消息队列的当前关联值

IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msgid_ds数据结构中给出的值

IPC_RMID:删除消息队列(这里只使用删除消息队列该参数)

有了创建获得和销毁,下面就要有接收信息和发送消息:



msgsnd函数:把一条消息添加到消息队列中。

参数:

msgid:由msggst函数返回的消息队列标识码

msgp:是一个指针,指针指向准备发送的消息

msgsz:是msgp指向的消息长度

msgflg:控制着当前消息队列满或达到系统上限时将要发生的事情。

返回值:

成功返回0;失败返回-1;

msgrcv函数:从一个消息队列接收消息

参数:msgid:由msgget函数返回的消息队列标识码

msgp:是一个指针,指针指向准备接受的消息

msgsz:是msgp指向的消息长度

msgtype:可以实现接受优先级的简单形式

msgflg:控制着当前消息队列中没有相应类型的消息可供接受时将要发生的事情

用消息队列实现两个进程通信

使用消息队列实现client与server聊天的效果:

comm.c
#include "comm.h"
int CommMsgQueue(int flags)
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0)
{
perror("ftok");
return -1;
}
int msgid=msgget(key,flags);
if(msgid<0)
{
perror("msgget");
}
return msgid;
}
int CreatMsgQueue()
{
return CommMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}

int GetMsgQueue()
{
return CommMsgQueue(IPC_CREAT);
}

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

int SendMsg(int msgid,int who,char* msg)
{
struct msgb buf;
buf.mtype=who;
strcpy(buf.mtext,msg);

if(msgsnd(msgid,(void *)&buf,sizeof(buf.mtext),0)<0)
{
perror("msgsnd");
return -1;
}
return 0;

}
int RecvMsg(int msgid,int recvtype,char out[])
{
struct msgb buf;
if(msgrcv(msgid,(void* )&buf,sizeof(buf.mtext),recvtype,0)<0)
{
perror("msgrcv");
return -1;
}
strcpy(out,buf.mtext);
return 0;
}


在comm.c中封装以上函数,实现创建消息队列,获得消息队列,接收消息与传送消息以及销毁该消息队列的功能。

server.c
#include "comm.h"

int main()
{
int msgid=CreatMsgQueue();

char buf[1024];
while(1)
{
buf[0]=0;
RecvMsg(msgid,CLIENT_TYPE,buf);
if(strcmp("quit",buf)==0)
{
printf("client quit! server quit!\n");
break;
}

printf("client# %s\n",buf);
printf("please input#");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf));
if(s>0)
{

buf[s-1]=0;
SendMsg(msgid,SERVER_TYPE,buf);
printf("send done,wait recv...\n");
}
}
DestroyMsgQueue(msgid);
return 0;
}


client.c
#include "comm.h"
int main()
{
int msgid=GetMsgQueue();
char buf[1024];
while(1)
{
printf("please input#");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf));
if
b9ef
(s>0)
{

buf[s-1]=0;
SendMsg(msgid,CLIENT_TYPE,buf);
printf("send done,wait recv...\n");
}
if(strcmp("quit",buf)==0)
{
printf("client quit\n");
break;
}
RecvMsg(msgid,SERVER_TYPE,buf);
printf("server# %s\n",buf);
}
return 0;

}


效果如下:





但是将server与client异常终止之后,会发现再次运行server出错了。



原因是因为,该消息队列已经存在了,需要将它删除掉,如何查看与删除已存在的消息队列。

ipcs -q


查看已存在的消息队列



ipcsrm -q (msgid)




看到此时被删除了。再此运行server就没有问题了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: