消息队列

消息队列:消息队列是一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识。与管道不同的是消息队

列存放在内核中,只有在内核重启(即操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。

Linux内核中,每个消息队列都维护一个结构体msqid_ds,此结构体保存着消息队列当前的状态信息。该结构定义在

头文件linux/msg.h中,具体如下:

struct msqid_ds
{
struct_ipc_perm msg_perm; //是一个ipc_perm的结构,保存了消息队列的存取权限,以及队列的用户ID、组ID等信息
struct_msg *msg_first; //指向队列中的第一条消息
struct_msg *msg_last; //指向队列中的最后一条消息
__kernel_t time_t msg_stime; //向消息队列发送最后一条信息的时间
__kernel_t time_t msg_rtime; //从消息队列取最后一条信息的时间
__kernel_t time_t msg_ctime; //最后一次变更消息队列的时间
unsigned long msg_lcbytes;
unsigned long msg_lqbytes;
unsigned short msg_cbytes; //消息队列中所有消息占的字节数
unsigned short msg_qnum; //消息队列中消息的数目
unsigned short msg_qbytes; //消息队列的最大字节数
__kernel_ipc_pid_t msg_lspid; //向消息队列发送最后一条消息的进程ID
__kernel_ipc_pid_t msg_lrpid; //从消息队列读取最后一条信息的进程ID
};

消息队列是随着内核的存在而存在的,每个消息队列在系统范围内对应惟一的键值。要获得一个消息队列的描述符,

只需提供该消息队列的键值即可,该键值通常由函数ftok返回,该函数原形为:

#include <sys/ipc.h>

key_t ftok(const char *pathname,int proj_id);

创建一个新的消息队列或访问一个已存在的消息队列前需要使用ftok函数得到key值,下面是ftok函数的包裹函数:

key_t Ftok(const char *pathname,int proj_id)
{
key_t key= ftok(pathname,proj_id);
if(key== -)
{
perror("ftok.");
exit();
}
return key;
}

消息队列的创建或打开:

使用函数msgget进行消息队列的创建或打开。该函数定义在头文件<sys/msg.h>中,该函数的原形为:

#include <sys/ipc.h>
   #include <sys/msg.h>
   int semget(key_t key,int msgflg);

该函数如果调用成功则返回一个消息队列的描述符,否则返回-1。函数的第一个参数为ftok()函数得到的键值,第二

个参数msgflg是一个标志参数,可以取如下值:

  • IPC_CREAT:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回该

消息队列的描述符。

  • IPC_EXCL:和IPC_CREAT一起使用,如果对应键值的消息队列已经存在,则出错,返回-1。

消息队列的读写:

创建了一个消息队列后,就可以对消息队列进行读写了,函数msgsnd用于向消息队列发送写数据,该函数定义在头文

件<sys/msg.h>中,函数原形为:

int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);

函数中,参数msgqid为函数向msgid标识的消息队列发送一个消息(msqid是由msgget返回的标识符);参数msgp指向

发送的消息是一个结构指针,该结构如下所示;参数msgsz为要发送的消息的大小,不包含消息类型占用的4个字节;参数

msgflg为操作标志位,可以设置为0或者IPC_NOWAIT,如果msgflg为0,则当消息队列已满时,msgsnd将会阻塞,直到消息

可以写进消息队列,如果msgflg为IPC_NOWAIT,当消息队列已满的时候,msgsnd函数将不等待立即返回。msgsnd函数成功

返回0,失败返回-1。

struct msgbuf
{
long mtype; //代表消息类型,给消息指定类型,可以使得消息在一个队列中重复使用
char mtext[]; //消息内容
};

消息队列中放入数据后,其它进程就可以读取其中的消息了。读取消息的系统调用为msgrcv()函数,该函数定义在头

文件<sys/msg.h>中,函数原形为:

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

函数中,参数msqid为消息队列描述符(由msgget返回的标识符);参数msgp同上面的参数相同;参数msgsz为消息缓

冲区的大小;参数msgtyp为请求读取的消息类型;参数msgflg为操作标志位,msgflg可以为IPC_NOWAIT、IPC_EXCEPT、IP

C_NOERROR三个常量,IPC_NOWAIT:如果没有满足条件的消息,调用立即返回,此时错误码为ENOMSG.IPC_EXCEPT:与msgt

yp配合使用,返回队列中第一个类型不为msgtyp的消息.IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的msgs

z字节,则把该消息截断,截断部分将被丢弃。msgrcv函数成功会返回读出消息的实际字节数,否则返回-1。

消息队列的控制:

消息队列的属性保存在系统维护的数据结构msqid_ds中,用户可以通过函数msgctl获取或设置消息队列的属性。msg

ctl定义在头文件<sys/msg.h>中,函数原形为:

int msgctl(int msqid,int cmd,struct msqid_ds *buf);

函数中,第一个参数为由msgget返回的标识符;第二个参数为执行的cmd操作;第三个参数为上面已经定义的msqid_d

s的结构体类型。系统中定义了3种cmd操作:IPC_STAT、IPC_SET、IPC_RMID,它们的含义如下:

  • IPC_STAT:该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指向的地址空间。
  • IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中,可设置的属性包括:msg_perm.uid、msg_pe

rm.gid、msg_perm.mode和msg_qbytes。

  • IPC_RMID:从内核中删除msqid标识的消息队列。

下面是使用消息队列进行发送消息的例子:

// myipc.h
#pragma once #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h> union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
}; key_t Ftok(const char *pathname,int proj_id)
{
key_t key= ftok(pathname,proj_id);
if(key== -)
{
perror("ftok.");
exit();
}
return key;
}
//utili.h
#pragma once #define MSG_BUFFER_LEN 256
#define SERVER_SEND_FLAG 100
#define SERVER_RECV_FLAG 200 #define CLIENT_SEND_FLAG 200
#define CLIENT_RECV_FLAG 100 typedef struct Msg
{
long msg_type;
char msg_text[MSG_BUFFER_LEN];
}Msg;
//server.c
#include "myipc.h"
#include "utili.h" int main(int argc,char *argv[])
{
key_t msg_key= Ftok(argv[],atoi(argv[]));
int msg_id= msgget(msg_key,IPC_CREAT|);
if(msg_id== -)
{
perror("msgget");
exit();
}
Msg msg;
while()
{
printf("Ser:");
scanf("%s",msg.msg_text);
msg.msg_type= SERVER_SEND_FLAG;
msgsnd(msg_id,&msg,strlen(msg.msg_text)+ ,); msgrcv(msg_id,&msg,MSG_BUFFER_LEN,SERVER_RECV_FLAG,);
printf("Cli:%s\n",msg.msg_text);
}
msgctl(msg_id,IPC_RMID,NULL);
return ;
}
//client.c
#include "myipc.h"
#include "utili.h" int main(int argc,char *argv[])
{
key_t msg_key= Ftok(argv[],atoi(argv[]));
int msg_id= msgget(msg_key,);
Msg msg;
while()
{
msgrcv(msg_id,&msg,MSG_BUFFER_LEN,CLIENT_RECV_FLAG,);
printf("Ser:%s\n",msg.msg_text);
printf("Cli:");
scanf("%s",msg.msg_text);
msg.msg_type= CLIENT_SEND_FLAG;
msgsnd(msg_id,&msg,strlen(msg.msg_text)+ ,);
}
return ;
}

IPC进程间通信---消息队列的更多相关文章

  1. Linux 进程间通信(一)(经典IPC:消息队列、信号量、共享存储)

    有3种称作XSI IPC的IPC:消息队列.信号量.共享存储.这种类型的IPC有如下共同的特性. 每个内核中的IPC都用一个非负整数标志.标识符是IPC对象的内部名称,为了使多个合作进程能够在同一IP ...

  2. 详解linux进程间通信-消息队列

    前言:前面讨论了信号.管道的进程间通信方式,接下来将讨论消息队列. 一.系统V IPC 三种系统V IPC:消息队列.信号量以及共享内存(共享存储器)之间有很多相似之处. 每个内核中的 I P C结构 ...

  3. PHP 进程间通信——消息队列(msg_queue)

    PHP 进程间通信--消息队列 本文不涉及PHP基础库安装.详细安装说明,请参考官网,或期待后续博客分享. 1.消息队列函数准备 <?php//生成一个消息队列的key$msg_key = ft ...

  4. System V IPC(1)-消息队列

    一.概述                                                    System V三种IPC:消息队列,信号量,共享内存.这三种IPC最先出现在AT&am ...

  5. 进程间通信IPC:消息队列,信号量,共享内存

    2015.3.4星期三 阴天 进程间通信:IPC 文件对象:记录文件描述符,文件开关等 IPC标示符:系统全局的流水号两个进程要通信,打开的是唯一的对象进行通讯,通过key操作 XSI IPC:消息队 ...

  6. 进程间通信——XSI IPC之消息队列

    进程间通信XSI IPC有3种:消息队列.共享内存.信号量.它们之间有很多相似之处,但也有各自的特殊的地方.消息队列作为其中比较简单的一种,它会有些什么东西呢,来一起探讨探讨.. 消息队列结构 消息队 ...

  7. 四十九、进程间通信——System V IPC 之消息队列

    49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...

  8. Linux下进程间通信--消息队列

    消息队列的定义遍地都是,不想移驾,请看下文: 一.定义: 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法. 每个数据块都被认 为是有一个类型,接收者进程接收的数据块可以有不同的类型值.我 ...

  9. System V IPC 之消息队列

    消息队列和共享内存.信号量一样,同属 System V IPC 通信机制.消息队列是一系列连续排列的消息,保存在内核中,通过消息队列的引用标识符来访问.使用消息队列的好处是对每个消息指定了特定消息类型 ...

随机推荐

  1. 自己动手实现STL 01:内存配置器的实现(stl_alloc.h)

    一.前言 在STL中,容器是其中的重中之重,基本的STL中的算法,仿函数等都是围绕着容器实现的功能.而,内存配置器,是容器的实现的基础.所以,我第一次要去编写便是内存配置器的实现.在STL中,内存配置 ...

  2. PAT 1021 Deepest Root

    #include <cstdio> #include <cstdlib> #include <vector> using namespace std; class ...

  3. sql: 生日赠品中的相关算法

    ---2013年10月9日生日,就以2012年9月1日至2013年8月31日計算 (因為係生日月份前兩個月之最後一天為結算日) DECLARE @birthday datetime,@now date ...

  4. ZROJ#398. 【18提高7】随机游走(期望dp 树形dp)

    题意 [题目链接]版权原因就不发了.. 给出一棵树,求出任意两点之间期望距离的最大值 Sol 比较清真的一道题吧.. 设\(f[x]\)表示从\(x\)走到\(x\)的父亲的期望步数 \(g[x]\) ...

  5. C++异常安全的思考

    异常安全的代码是指,满足两个条件 1异常中立性 : 是指当你的代码(包括你调用的代码)引发异常时,这个异常 能保持原样传递到外层调用代码 2.异常安全性:  1,抛出异常后,资源不泄露, 2,抛出异常 ...

  6. WebLogic配置与部署

    一.创建域: 第一步,选择“开始菜单”-> “Oracle WebLogic”-> “WebLogic Server 10gR3” -> “Tools”-> “Configur ...

  7. 如何使用Kubernetes里的NetworkPolicy

    创建一个类型为NetworkPolicy的Kubernetes对象的yaml文件. 第九行的podSelector指定这个NetworkPolicy施加在哪些pod上,通过label来做pod的过滤. ...

  8. NO.006-2018.02.11《卜算子·我住长江头》宋代:李之仪

    卜算子·我住长江头_古诗文网(bǔ) 卜算子·我住长江头 宋代:李之仪 我住长江头,君住长江尾.日日思君不见君,共饮长江水. 我居住在长江上游,你居住在长江下游. 天天想念你却见不到你,共同喝着长江的 ...

  9. POJ 1330 Nearest Common Ancestors 【LCA模板题】

    任意门:http://poj.org/problem?id=1330 Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000 ...

  10. [19/03/12-星期二] 数组_遍历(for-each)&复制&java.util.Arrays类

    一.遍历 for-each即增强for循环,是JDK1.5新增加的功能,专门用于读取数组或集合中所有的元素,即对数组进行遍历. //数组遍历 for-each public class Test_03 ...