想要有顺序地学习Linux,入口在这里哦: Linux:目录索引
消息队列的结构
0.消息队列、消息缓存块和通道
在学习“进程之间是如何使用消息队列进行通信”之前,先要知道:
什么消息队列
什么是消息缓存块
什么是通道
①
消息队列
:一种数据结构,即队列,其详细内容暂不讨论
②消息缓冲块
:一种数据结构,用于存储信息,进程之间通信的方法之一,代码结构如下:
struct msgbuf{
long channel;//通道号
char mtext[100];//消息内容,这里的100是自定义值,可更改
}
③通道
:通道并不真实存在,更应该说它是分类
,即由消息队列
结构中的channel通道号分类,channel值相同的消息队列属于同一通道
(或说属于同一类),进程可以通过消息队列
的通道
(即channel值)来选择对应类型的消息队列
消息队列的各种操作
1.创建/打开消息队列
#include <sys/ipc.h>
#include <sys/msg.h>
#include<sys/types.h>
//用于创建一个新的或打开一个已经存在的消息队列
int msgget(key_t key,int flag);
//返回值:-1表示失败,成功返回创建/打开的消息队列标识符id
(1)msgget参数:消息队列对象的关键字key
每个消息队列有一个唯一的标识符id,key就是消息队列的关键字
函数将key与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建
(2)msgget参数:操作消息队列的权限flag
IPC_CREAT:创建新的消息队列。
IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞
0:如果是打开文件,即文件已存在,写0
(3)★生成key
系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值(关键字key)。通常情况下,该ID值(关键字key)通过ftok函数得到
#include <sys/ipc.h>
#include <sys/types.h>
key_t ftok(const char *pathname, int proj_id);
//pathname就是你指定的文件名(路径),要求文件必须存在,一般使用当前目录,如:
key_t key;
key = ftok(".", 1);//这样就是将pathname设为当前目录
//id是子序号,虽然是int类型,但是只使用8bits(1-255)
//返回值:失败返回-1,成功返回key_t值
ftok工作原理
在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i
简单来说:ftok的作用就是保证生成的消息队列关键字key唯一
代码如下:
#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;
}
2.查看消息队列
命令:ipcs -q
实例如下:
3.如何手工删除消息队列
命令:ipcrm -Q [key]
实例如下:
4.往消息队列里写数据
#include <sys/ipc.h>
#include <sys/msg.h>
#include<sys/types.h>
//调用成功返回0,不成功返回-1
int msgsnd(int msqid,const void* msgp,ssize_t msgsz,int msgflg)
//msqid 想要写入内容的消息队列标识符id,mspid是msgget的返回值,也用来标识消息队列
//msgp 临时创建的消息队列结构体对象的指针
//★上面两个参数的关系:
//①先将想要写入的内容写入“msgp指向的消息队列结构体对象”里
//②再将“msgp指向的消息队列结构体对象”里的内容放入“msqid对应的消息队列”中
//③从而实现对“msqid对应的消息队列”的写入
//msgsz 要写入的消息的大小,不包括通道号,即char mtext[100]的大小
//msgflg 为0表示阻塞方式,设置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(0x61021685, 0);
//打开消息队列,0x61021685是刚才创建的消息队列关键字
//第二个参数为0,表示这是打开消息队列而不是创建,即该消息队列存在
if ( id == -1 )
perror("msgget"),exit(1);
struct msgbuf 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.从消息队列里读数据
#include <sys/ipc.h>
#include <sys/msg.h>
#include<sys/types.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//msqid 想要读取内容的消息队列标识符id,mspid是msgget的返回值,也用来标识消息队列
//msgp 临时创建的消息队列结构体对象的指针
//★上面两个参数的关系:
//①先将“msqid对应的消息队列”里的内容放入“msgp指向的消息队列结构体对象”中
//②再从“msgp指向的消息队列结构体对象”中读取想要读取的内容
//③从而实现对“msqid对应的消息队列”的读取
//msgsz 消息大小,不包括通道号,只指消息内容大小,即char mtext[100]的大小
//msgtyp 想接收的消息所在的通道号,通道号如果写0,表示按照顺序接收消息队列
//msgflg 为0表示阻塞方式,设置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(0x61021685, 0);
//打开消息队列,0x61021685是刚才创建的消息队列关键字
//第二个参数为0,表示这是打开消息队列而不是创建,即该消息队列存在
if ( id == -1 )
perror("msgget"),exit(1);
struct msgbuf 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>
//msgctl系统调用对msqid标识的消息队列执行cmd操作列
//返回值:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//msqid 消息队列标识符,mspid是msgget的返回值,也用来标识消息队列
//cmd:包括以下三种
//IPC_STAT
//读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。
//IPC_SET
//设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
//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;
}