##一、消息队列 ##
——想知道如何通过消息队列实现进程间通信,我们必须得了解三个概念。
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;
}