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

基本介绍

  1. 支持不同进程之间以消息(messages)的形式进行数据交换,消息能够拥有自己的标识,且内核使用链表方式进行消息管理。
  2. 进程之间的通信角色为:发送者和接受者
    发送者:
    a. 获取消息队列的ID(key或者msgid)
    b. 将数据放入一个带有标识的消息结构体,发送到消息队列
    接受者:
    a. 获取消息队列的ID
    b. 指定标识的消息从消息队列中读出,然后进一步后续处理
  3. 支持不同的进程标记不同的消息类型(1,2,3…),并由内核态维护对应消息类型的链表。
  4. 内核态的0号消息类型维护了一个链表,用来保存按照时间顺序加入的消息

编程接口

  1. 生成ipc对象的唯一标识key的接口
    a. 头文件 <sys/ipc.h>
    b. 函数声明 key_t ftok(const char *path, int id);
    c. 参数描述
    path需指定一个已经存在的可访问的文件
    id为用户可自由指定的id

  2. 创建或者打开一个消息队列,并获取system V 消息队列中消息的身份标识
    a. 头文件 <sys/types.h> <sys/ipc.h> <sys/msg.h>
    b. 函数声明 int msgget (ket_t key, int msgflg)
    c. 参数描述
    key 为ipc对象的唯一标识,生成的消息身份标识与该参数相关
    msgflg当该函数没有搜索到系统中与key值对应的消息队列,则msgflg会指定IPC_CREAT,创建一个队列,并返回消息标识
    d. 返回值:成功返回消息身份标识,失败返回-1

  3. 发送消息到消息队列
    a. 头文件 <sys/types.h> <sys/ipc.h> <sys/msg.h>
    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. 头文件 <sys/types.h> <sys/ipc.h> <sys/msg.h>
    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. 头文件 <sys/types.h> <sys/ipc.h> <sys/msg.h>
    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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#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\n");
                _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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#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\n");
                _exit(-1);
        }
        printf("%s\n",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 in
                                                queue (nonstandard) */
               msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
               msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
           };

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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\n");
                _exit(-1);
        }
	
		//打印各个状态信息
        printf("uid :%d,gid:%d,cuid:%d,cgid:%d\n",\
                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\n",\
                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\n");
                _exit(-1);
        }

        if (-1 == msgctl(msg_id, IPC_STAT, &info)) {
                printf("ipc_stat failed\n");
                _exit(-1);
        }


        printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\
                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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值