ITKeyword,专注技术干货聚合推荐

注册 | 登录

IPC之消息队列

leex_brave 分享于 2016-07-13

推荐:linux 进程间通信(IPC)一消息队列

消息队列提供了一种在两个不相关的进程之间传递数据的相当简单且有效的方法。与命名管道相比,消息队列的优势在于,它独立于发送和接收进程而存在,这消除了在同

2020腾讯云7月秒杀活动,优惠非常大!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1040

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
地址https://www.aliyun.com/minisite/goods

   

实现linux进程通信的方式有5种:

--信号(Singal)

--管道(Pipe)

--消息队列(Message)

--信号量(Semaphore)

他们都是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),诸多的限制,也限制了消息队列的使用范围。


既然消息队列的特点对进程间通信有诸多的好处,那么我们需要知道消息队列的结构。此处直接上截图:


注意图中标注的红框

推荐:IPC之消息队列详解与使用

一、    概念  消息队列就是一个消息的链表。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程可以从消息队列中读出消息

         struct ipc_perm;是每个IPC方式都具有的一个标识,它的存在将消息队列纳入到进程通信这一大概念中来。

除了关注消息队列的结构,最重要的是要了解怎样去使用消息队列,所以我们就不得不对消息队列相关的函数进行研究。

1.创建新消息队列或取得已存在消息队列
原型: int msgget(key_t key, int msgflg);
参数:key:可以认为是一个端口号,也可以由函数ftok组成。

ftok函数:key_t ftok(const char *pathname, int proj_id);
函数ftok把一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:
头文件:# include <sys/types.h> 
        
# include <sys/ipc.h><pre name="code" class="cpp">ftok的典型实现调用stat函数,然后组合以下三个值:
1. pathname所在的文件系统的信息(stat结构的st_dev成员)
2.该问件在本文件系统内的索引节点号(stat结构的st_ino成员)
3. proj_id的低序8位(不能为0)

 
  

    msgflg:IPC_CREAT 如果IPC不存在,则创建一个IPC资源,否则打开操作。

         IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
  如果单独使用IPC_CREAT, msgget()函数要么返回一个已经存在的共享内存的操作符,要么返回⼀个新建的共享内存的标识符。
  如果将IPC_CREAT和IPC_EXCL标志一起使用, msgget()将返回一个新建的IPC标识符;如果该IPC资源已存在,或者返回-1。

  IPC_EXEL标志本来并没有太多的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。

2.msgrcv从队列中取出消息: 

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, intmsgflg);

 msgsnd将数据放到消息队列中: 

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:(一)msqid:消息队列的标识码

          (二)msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构
msgsz:消息的大小。
msgtyp:从消息队列内读取的消息形态。如果值为零,则表⽰消息队列中的所有消息都会被读取。
msgflg:用来指明核心程序在队列没有数据的情况下所应采取的⾏动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,并会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时, msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式

3.设置消息队列属性
原型: 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 标识的消息队列。


一个进程间通信的消息队列:

com.h

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <string.h>
#include <time.h>
#define __MSG_SIZE__ 1024
#define FILEPATH "/tmp/.msg"
#define ID 0
extern const int g_ser_send_type;//server
extern const int g_cli_send_type;//client
typedef struct _msginfo{
long mtype;
char mtext[__MSG_SIZE__];
}msginfo;
void print_log(char *);

com.c
const int g_ser_send_type=1;//server
const int g_cli_send_type=2;//client
void print_log(char *msg)
{
printf("%s[%d] : %s\n", __FUNCTION__,__LINE__,msg);
}
server.c
#include"comm.h"
int msg_server_start();
int msg_server_end(int);
int _msg_id = -1;
int msg_server_start()
{
key_t _key = ftok(FILEPATH,ID);//创建键值
if( _key < 0 ){
print_log("get key id error");
return 1;
}
msginfo _ser_info;
_msg_id = msgget(_key, IPC_CREAT); //获取信号队列ID
if( _msg_id < 0 ){<pre name="code" class="cpp">print_log("msg_server get key id failed\n");
return 1;
}
while(1){
// sleep(10);
if( msgrcv(_msg_id, &_ser_info, sizeof(_ser_info), g_cli_send_type, 0) == -1 ){
print_log("msg rcv error");
return 1;
}
printf("client :> %s\n",_ser_info.mtext);
printf("server :>");
memset(_ser_info.mtext, '\0', sizeof(_ser_info.mtext));
fgets(_ser_info.mtext, __MSG_SIZE__, stdin);
if(strncasecmp(_ser_info.mtext, "quit", 4) == 0){
printf("server bye!\n");
break;
}
_ser_info.mtype = g_ser_send_type;
if( msgsnd(_msg_id, &_ser_info, __MSG_SIZE__, 0) == -1 ){
printf("server send msg error\n");
exit(0);
} }
return 0;
}
int msg_server_end(int id)
{
if(msgctl(id, IPC_RMID, NULL) == -1){
printf("delete msg kernel info error\n");
return 1;
}
return 0;
}
static void delete_msg(void)
{
if( _msg_id != -1 ){
msg_server_end(_msg_id);
}
printf("delete msg queue end\n");
}
int main(int argc, char *argv[])
{
atexit(delete_msg);
if(msg_server_start() == 0){
print_log("msg_server start success\n");
}else{
print_log("msg_server start failed\n");<pre name="code" class="cpp">}
return 0;
}

 

 

client.c
#include"comm.h"
int msg_client_start();
int msg_client_end(int);<pre name="code" class="cpp">int _msg_id = -1;
int msg_client_start()
{
key_t _key = ftok(FILEPATH,ID);//创建键值
if( _key < 0 ){
print_log("client get key id error");
return 1;
}
msginfo _cli_info;
_msg_id = msgget(_key, 0); //获取信号队列ID
if( _msg_id < 0 ){
print_log("msg_server get key id failed\n");
return 1;
}
while(1){
printf("client :>");
fgets(_cli_info.mtext, sizeof(_cli_info.mtext),stdin);
if(strncasecmp(_cli_info.mtext, "quit", 4) == 0){
printf("client bye!\n");
break;
}
_cli_info.mtype = g_cli_send_type;
if( msgsnd(_msg_id, &_cli_info,sizeof(_cli_info), 0) == -1 ){
printf("client send msg error\n");
exit(0);
}
memset(_cli_info.mtext, '\0', sizeof(_cli_info.mtext));
if( msgrcv(_msg_id, &_cli_info, __MSG_SIZE__, g_ser_send_type, 0) == -1 ){
print_log("client recive msg error");
return 1;
}
printf("server :>%s\n",_cli_info.mtext); }
return 0;
}
int msg_client_end(int id)
{
if(msgctl(id, IPC_RMID, NULL) == -1){
return 1;
}
return 0;
}
static void delete_msg(void)
{
if( _msg_id != -1 ){
msg_client_end(_msg_id);
}
printf("delete msg queue end\n");
}
int main(int argc, char *argv[])
{
atexit(delete_msg);
if(msg_client_start() == 0){
print_log("msg_server start success\n");
}else{
print_log("msg_server start failed\n");
}
return 0;
}

 






推荐:【IPC】Posix消息队列

消息队列是一种IPC机制,有Posix消息队列和System V消息队列两种类型,它们有许多相似之处,但也有一些差别:对Posix消息队列的读总是返回优先级最高的最早的消

    实现linux进程通信的方式有5种: --信号(Singal) --管道(Pipe) --消息队列(Message) --信号量(Semaphore) 他们都是IPC对象的数据结构。 struct ipc_perm { key_t __key; /* Key sup

相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。