(进程间通信一)——消息队列

##一、消息队列 ##

——想知道如何通过消息队列实现进程间通信,我们必须得了解三个概念。

1. 消息队列
2. 消息缓冲区
3. 通道
这里写图片描述

我们来参照这个图分析一下:

  • 什么是消息对列?
    ——就是一个数据结构,是一个队列。主要是用来实现消息传递。(可以理解为一个链式结构)

  • 什么是消息缓存区?
    ——如上图,那些绿块块分别是一个一个的消息缓存区。用来存放通道号,和你写入通道中的数据。

struct msgbuf{
	long channel;  //通道号,必须大于0(系统内部其实将它定义为 mtype ——把它视为一个类型)
	char mtext[100];//消息内容(100是自定义的值,你可以任意更改)
	}:
  • 什么是通道?
    ——通道号就相当于一个分类,并不真实存在。channel 值相同的属于同一通道,系统可以根据通道号来选择对应的消息队列。

联系实例分析:就好比看电视,不同的频道对应不同的通道,想看哪个频道,就从哪个通道进入

在写数据时,对通道号,也是有一定的要求的:

channel=0,——非法(报错)
channel<0,——非法(报错)
channel>0,——可以,继续写入消息内容

在读数据时,对通道号,也是有一定的要求的:

channel=0,——返回队列的第一个消息
channel>0,——返回队列中该通道号的第一个消息(相同的通道号归为一类,一个通道里可以放很多相同的通道号)
channel<0,——返回队列中你输入通道号的绝对值,所对应的通道号(若有多个相同的通道号,默认存放最小消息内容的通道号)

##二、 对消息队列的各种操作 ##

1.查看消息队列:ipcs -q

此时消息队列已经创建:
这里写图片描述

##2. 删除消息队列:ipcrm -Q key ##
这里写图片描述

3.创建/打开消息队列:

#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>

//用于创建一个新的或者打开一个已存在的消息队列
int msgget(key_t key, int flag);

//返回值:-1(失败)  成功(你创建/打开的消息队列的标识符id)

代码实现:

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

int main( void )
{
        int id = msgget(1234, IPC_CREAT|0644);
        if ( id == -1 ) perror("msgget"),exit(1);
        printf("create ok\n");
}

这里写图片描述

解释msgget参数 :##

(1)key(关键字)

  • 每个消息队列都有一个唯一的标识符id,可以就是消息队列的关键字,根据key的信息,找到对应的消息队列
  • 判断一个消息队列是否存在,直接用key与已有的消息队列关键字比较,若key值相同,说明已存在,key值不同,说明没有这个消息队列,可新创建

(2)flag(权限)

  • IPC_CREAT——创建一个新的消息队列

  • IPC_EXCL——表示创建的消息队列存在,返回错误(与IPC_CREATE一同使用)

  • 0 ——是已存在的消息队列,直接打开

  • IPC_NOWAIT——读写消息队列无法满足时,不阻塞。(是msgsnd 和msgrcv 的权限)

(3)key的生成

生成key,需要一个函数——ftok()

key_t ftok(const char*filename,proj_id)
第一个参数:
//filename是一个文件名(路径),要求这个文件名必须存在——(一般用const修饰)
//一般默认为当前文件
例:key_t key;
key=(".",1)——将当前目录设为你的文件名

第二个参数:是子序号,虽然是int型,但是只使用8 bits,(1~255)

返回值:-1(失败)   (成功)key_t 的结果

代码实现:

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

int main()
{
    key_t key=ftok(".",'a');
    if(key == -1)
        perror("ftok"),exit(1);
    int id=msgget(key,IPC_CREAT|0644);
    if(id == -1)
        perror("msgget"),exit(1);
    printf("create success\n");
    return 0;
}


4.往消息队列里写数据

int msgsnd(int misqid,const void*msgp,size_t msgsz,int msgflg)
//misgid——msgget函数的返回值,想写入内容的消息队列的标识符id
//msgp——临时创建一个消息队列结构体对象的指针

两者之间的关系:
    1.先将想写入的消息内容存放在 消息队列结构体的对象中,这个对象相当于一个中转站;
    2.再将中转站里的内容放入到 miqid对应的消息队列中;
    3.从而间接的实现 往消息队列中写数据。

//mgsz——写入消息的大小,char metex[100]的大小,不包括通道号
//msgflg——权限(0——阻塞方式,当输入的通道号不合法,会一直阻塞,不会结束,除非用Ctrl C
              IPC_NOWAIT——非阻塞方式,当输入的通道号不合法,不会阻塞,直接结束。


代码实现:

#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h> 
          
struct msgbuf {
        long channel;
        char mtext[100];
};      

int main( void )
{     
        int id = msgget(0x000004d2,0); 
      //打开消息队列,0x000004d2是刚才创建的消息队列的关键字
	  //第二个参数为0,说明该消息队列已经存在,这里进行的是打开操作而不是创建

        if ( id == -1 ) perror("msgget"),exit(1);
    
        struct msgbuf mb;
        memset(&mb,0,sizeof(mb));//一定要记得初始化,否则因为缓冲区的问题,会出现错误
        printf("channel:");
        scanf("%ld", &mb.channel);
        printf("text:");
        scanf("%s", mb.mtext);
        
        int r = msgsnd(id, &mb, strlen(mb.mtext), 0);
        if ( r == -1 ) perror("msgsnd"),exit(1);

}

这里写图片描述



5.从消息队列中读数据:

int msgrcv(int msggid,void*msgp,size_t msgsz,int chnnel,int msgflg)
//misgid——msgget函数的返回值,想写入内容的消息队列的标识符id
//msgp——临时创建一个消息队列结构体对象的指针

两者之间的关系:
    1.先将msgqid对应的消息队列的内容放在这个中转站(临时创建的结构体对象)中;
    2.再对中转站里的内容进行读取;
    3.从而间接的实现 从消息队列中读数据。

//mgsz——写入消息的大小,char metex[100]的大小,不包括通道号
//msgflg——权限(0——阻塞方式,当输入的通道号不合法,会一直阻塞,不会结束,除非用Ctrl C
              IPC_NOWAIT——非阻塞方式,当输入的通道号不合法,不会阻塞,直接结束。

代码实现:

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

struct msgbuf{

        long channel;
        char mtext[100];
};

int main(void)
{
        int id=msgget(0x000004d2,0);
        //打开消息队列,0x000004d2是刚才创建的消息队列的关键字
        //第二个can顺为0,说明该消息队列已经存在,这里进行的是打开操作而不是创建


        if(id==-1)
                perror("msgget"),exit(1);

        struct msgbuf mb;
        memset(&mb,0,sizeof(mb));//一定要记得初始化,否则因为缓冲区的问题,会出现错误
        printf("channel:");
        int channel;
        scanf("%d",&channel);

        ssize_t r= msgrcv(id,&mb,100,channel,0);

        if(r==-1)
                perror("msgrcv"),exit(1);
        printf("%s\n",mb.mtext);
}

这里写图片描述



6.删除消息队列:

#include <sys/ipc.h>
#include <sys/msg.h>
#include<sys/types.h>

 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 //返回值:成功返回0,失败返回-1
 
//msqid 消息队列标识符id,mspid是msgget的返回值
//cmd:包括以下三种 
  1. IPC_STAT
	读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。
  2.IPC_SET
	设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
  3.IPC_RMID
	从系统内核中移走消息队列,即删除消息队列
	(buf 配合IPC_RMID使用,设为0,即buf为NULL)

代码实现:

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

int main()
{
    int id=msgget(111,IPC_CREAT|0644);
    if(id == -1)
        perror("msgget"),exit(1);
    printf("create success\n");
    if(msgctl(id,IPC_RMID,0) == -1)
        perror("msgtcl"),exit(1);
    printf("msgctl success\n");
    return 0;
}

这里写图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值