首页 > linux进程间通信:system V消息队列

linux进程间通信:system V消息队列

文章目录

      • 基本介绍
      • 编程接口
      • 代码实例
        • 消息队列的发送和接收
        • 消息队列中的消息对象的属性控制

基本介绍

  1. 支持不同进程之间以消息(messages)的形式进行数据交换,消息能够拥有自己的标识,且内核使用链表方式进行消息管理。
  2. 进程之间的通信角色为:发送者和接受者

    发送者:

    a. 获取消息队列的ID(key或者msgid)

    b. 将数据放入一个带有标识的消息结构体,发送到消息队列

    接受者:

    a. 获取消息队列的ID

    b. 指定标识的消息从消息队列中读出,然后进一步后续处理
  3. 支持不同的进程标记不同的消息类型(1,2,3…),并由内核态维护对应消息类型的链表。
  4. 内核态的0号消息类型维护了一个链表,用来保存按照时间顺序加入的消息

编程接口

  1. 生成ipc对象的唯一标识key的接口

    a. 头文件

    b. 函数声明 key_t ftok(const char *path, int id);

    c. 参数描述

    path需指定一个已经存在的可访问的文件

    id为用户可自由指定的id

  2. 创建或者打开一个消息队列,并获取system V 消息队列中消息的身份标识

    a. 头文件

    b. 函数声明 int msgget (ket_t key, int msgflg)

    c. 参数描述

    key 为ipc对象的唯一标识,生成的消息身份标识与该参数相关

    msgflg当该函数没有搜索到系统中与key值对应的消息队列,则msgflg会指定IPC_CREAT,创建一个队列,并返回消息标识

    d. 返回值:成功返回消息身份标识,失败返回-1

  3. 发送消息到消息队列

    a. 头文件

    b. 函数声明 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

    c. 参数描述

    msqid 消息标识,类似于文件描述符fd

    msgp 消息内容指针

    msgsz 消息大小

    msgflg 当出现消息队列没有足够的可用空间时,可以通过设置msgflg为IPC_NOWAIT来让发送函数不产生阻塞,返回失败

    d. 返回值 失败返回-1,以及对应失败码;成功则返回0

  4. 从消息队列中接收消息

    a. 头文件

    b. 函数声明 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

    c. 参数描述

    msqid 消息标识,类似于文件描述符fd

    msgp 消息内容指针

    msgsz 消息大小,同时也是当前接收的消息最大能够接收的大小

    msgflg 当出现实际的消息内容大于设定的msgsz,可以通过MSG_NOERROR来将消息裁剪为msgsz大小进行获取,否则会返回失败。而消息仍然存在于消息队列。

    msgtyp 如果是0,则会读取处于消息队列的第一个消息;大于0,则会读取对应type处于消息队列中的第一个消息;如果小于0,则会读取type绝对值或者小于绝对值的消息队列的第一个消息。

    d. 返回值 失败返回-1,成功返回对应消息的大小

  5. 控制消息队列的各个操作

    a. 头文件

    b. 函数声明 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

    c. 参数描述

    msgqid 消息标识

    cmd 针对消息标识的操作,合法的操作如下:

    • IPC_STAT 获取msgqid的消息对象的信息,将各个属性从内核拷贝到一个临时的数据结构msgqid_ds类型的buf;调用者需要对消息队列有读权限
    • IPC_SET 自己可以通过临时的msgqid_ds来设置内核中消息的对应msgqid_ds的属性
    • IPC_RMID 立即移除消息队列;当前调用者需要拥有 消息队列的所有者权限,或者高于所有者的权限(root)
    • IPC_INFO 返回消息队列的参数限制

    其他标识可以通过man msgctl来查看

代码实例

消息队列的发送和接收

发送端msg_snd.c

#include 
#include 
#include 
#include 
#include 
#include 
#include #define MSG_TYPE1 1
#define MSG_TYPE2 2 struct msgbuf
{ long mtype;char mtext[100];
};int main()
{ //当多用户的时候通过指定文件以及设置id来获取唯一的key标识//key_t key = ftok(".",100);key_t key = 12345; //个人使用的时候可以直接指定key//创建msg_qid的对象int msg_qid = msgget(key, IPC_CREAT | 0666);struct msgbuf msg;memset(&msg, 0 , sizeof(msg));//初始化消息类型以及消息内容msg.mtype = MSG_TYPE2;strncpy(msg.mtext, "hello world" , 80);//发送消息到消息标识的msg_qid IPC 对象中if( -1 == msgsnd(msg_qid,(void *)&msg,strlen(msg.mtext),0)) { printf("send msg failed
");_exit(-1);}return 0;
}

编译运行:

gcc msg_snd.c -o msg_snd

运行前查看系统消息队列

ipcs -q

[root@node1 ~]# ipcs -q------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x000004d2 0          root       666        0            0

运行后,可以看到我们发送了消息队列的各个属性信息。关于key值,它为我们设置的12345的16进制数值

[root@node1 ~]# ./msg_snd 
[root@node1 ~]# ipcs -q------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x000004d2 0          root       666        0            0           
0x00003039 65538      root       666        11           1

接收端msg_rcv.c

#include 
#include 
#include 
#include 
#include 
#include 
#include #define MSG_TYPE1 1
#define MSG_TYPE2 2 struct msgbuf
{ long mtype;char mtext[100];
};int main()
{ key_t key = 12345;int msg_qid = msgget(key, IPC_CREAT | 0666);struct msgbuf msg;memset(&msg, 0 , sizeof(msg));if (-1 == msgrcv(msg_qid,(void *)&msg,sizeof(msg.mtext),MSG_TYPE2,0)) { printf("receive msg failed
");_exit(-1);}printf("%s
",msg.mtext);//当完成接收之后从消息队列中删除当前消息//msgctl(msg_id,IPC_RMID,NULL);return 0;
}

编译运行,可以看到已经接手到了我们之前发送的内容:

[root@node1 ~]# gcc msg_rcv.c -o msg_rcv
[root@node1 ~]# ./msg_rcv
hello world

查看消息队列情况,消息队列中的数据已经被接收,所以在used-bytes和messages中看不到消息内容了,但是没有删除该消息队列,所以消息标识仍然存在。我们可以在上述代码中加入msgctl

[root@node1 ~]# ipcs -q------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x000004d2 0          root       666        0            0                    
0x00003039 65538      root       666        0            0  

消息队列中的消息对象的属性控制

控制代码msg_ctl.c

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

其中主要控制的是消息队列一个数据结构,可以通过man msgctl查看 msqid_ds结构体

           struct msqid_ds { struct ipc_perm msg_perm;     /* Ownership and permissions */time_t          msg_stime;    /* Time of last msgsnd(2) */time_t          msg_rtime;    /* Time of last msgrcv(2) */time_t          msg_ctime;    /* Time of last change */unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t       msg_qnum;     /* Current number of messagesin queue */msglen_t        msg_qbytes;   /* Maximum number of bytesallowed in queue */pid_t           msg_lspid;    /* PID of last msgsnd(2) */pid_t           msg_lrpid;    /* PID of last msgrcv(2) */};

以下代码为主要控制参数的代码,通过msgctl的cmd参数进程控制

#include 
#include 
#include 
#include 
#include 
#include 
#include int main()
{ key_t key = 12345;int msg_id = msgget(key,IPC_CREAT|0666);struct msqid_ds info;//第一次先从已存在的12345的消息中获取队列状态if (-1 == msgctl(msg_id,IPC_STAT,&info)) { printf("control msg failed
");_exit(-1);}//打印各个状态信息printf("uid :%d,gid:%d,cuid:%d,cgid:%d
",info.msg_perm.uid,info.msg_perm.gid,info.msg_perm.cuid,info.msg_perm.cgid);printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu
",info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);//尝试设置消息队列允许的最大字节内容info.msg_qbytes = 16000;//通过cmd为IPS_SET的标记进行设置if (-1 == msgctl(msg_id, IPC_SET, &info)) { printf("ipc_set failed
");_exit(-1);}if (-1 == msgctl(msg_id, IPC_STAT, &info)) { printf("ipc_stat failed
");_exit(-1);}printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu
",info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);return 0;
}

输出如下,可以看到我们控制的队列允许的最大字节内容msg_qbytes已经设置进去:

[root@node1 ~]# ./msg_ctl 
uid :0,gid:0,cuid:0,cgid:0
mode:6663o,cbytes:11,qnum:1,qbytes:16000
mode:6663o,cbytes:11,qnum:1,qbytes:16000

更多相关:

  • 首先对微擎的工作原理做简单描述, 微擎使用规则和模块的机制来处理公众平台的请求数据并返回响应的结果.执行流程描述为: 粉丝用户与公众号码进行对话或交互, 而后公众平台将粉丝用户的请求消息(当前包括: 文本, 图片, 位置, 链接, 事件. 请参阅消息类型)传递给微擎系统, 微擎系统按照消息类型和对应的公众号所设定的规则列表匹配到合适的...

  • 消息队列的使用场景以下介绍消息队列在实际应用常用的使用场景。异步处理、应用解耦、流量削锋和消息通讯四个场景。1】异步处理:场景说明:用户注册后,需要发注册邮件和注册短信。引入消息队列后架构如下:用户的响应时间=注册信息写入数据库的时间,例如50毫秒。发注册邮箱、发注册短信写入消息队列后,直接返回客户端,因写入消息队列的速度很快,基...

  • 下面是我凭记忆想到的几个题目,有需要的同学就拿去吧,我也算做了点善事. 中体骏彩C++笔试题 2013-11-18 1.指针的含义是:B A.名字 B.地址 C.名称 D.符号 2.给出下面的程序输出: #include #include #include ...

  • 双端通信描述 利用消息队列针对发送接受消息的类型唯一性 进行多个客户端之间消息传递,而不需要server端进行消息转发。 同时消息队列的读阻塞和写阻塞特性(消息队列中已经写入数据,如果再不读出来,则无法再次写入)让消息队列的实现过程只能如下: 客户端1的父进程用来处理类型1的消息写,子进程处理类型2的消息读客户端2的父进程处理类型...

  • ---- 创建任务 DECLAREp_project_id NUMBER := 155233;p_task_number VARCHAR2(240) := 'CXYTEST0001';p_task_name VARCHAR2(240) := '接口测试CXYTEST0...

  • 使用队列实现栈的下列操作: push(x) – 元素 x 入栈pop() – 移除栈顶元素top() – 获取栈顶元素empty() – 返回栈是否为空 队列的特点:先入先出 栈的特点:后入先出 即我们每次添加元素到队列时,想要达到栈的效果,则需要调整当前元素到队列头部 方法一:双队列 一个临时队列保存push进去的元素,将...

  • Queue除了前面介绍的实现外,还有一种双向的Queue实现Deque。这种队列允许在队列头和尾部进行入队出队操作,因此在功能上比Queue显然要更复杂。下图描述的是Deque的完整体系图。需要说明的是LinkedList也已经加入了Deque的一部分(LinkedList是从jdk1.2 开始就存在数据结构)。   Deque在Q...

  • 网络流量队列优先级相关知识点 Qdisc(quick disconnect)快速分离,断开;是一种排队规则,实现对流量的优先级管理.   涉及随机公平队列,令牌桶过滤器,分层令牌桶,FIFO, /*  *CopyRight (c) 2014-02-05 by Ruiy use to CopyLift!!!!  * */   ...