一、消息队列
消息队列是消息的链表,存放在内核中并由消息队列标识符(msqid)表示。
消息队列提供了一个从一个进程向另一个进程发送数据块的方法,每个数据块都可以被认为是有一个类型,接受者接受的数据块可以有不同的类型。
二、特点
- 消息队列独立于发送与接受进程。进程终止时,消息队列及其内容不会被删除,需要调用接口删除或使用命令删除
- 消息队列是面向记录的,其中的消息具有特定的格式及优先级,克服了管道只能承载无格式字节流的缺点
- 消息队列可以双向通信消息队列可实现消息的随机查询,也可按照消息类型读取
三、消息队列函数
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
1.msgget
功能:创建和访问一个消息队列
原型:
int msgget(key_t key, int msgflag);
参数:
key:某个消息队列的名字,键值,用ftok()产生
msgflag:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。
返回值:成功返回一个非负整数,即消息队列的标识码,失败返回-1
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
//eg:
key_t key; //可通过ls -i 查询
key = ftok(".",'z'); //"."当前路径 任意字母或数字
printf("key = %x\n",key);
pathname为已经存在的文件名,“.”表示当前目录;id为子序号,值范围只有8bit(0-255)
调用成功返回一个key值,用于创建消息队列,如果失败,返回-1(可通过ls -i 查询)
2.msgsnd
功能:把一条消息添加到消息队列中
原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msgid:由msgget函数返回的消息队列标识码
msgp:指针指向准备发送的消息(注意是指针)
msgze:msgp指向的消息的长度(不包括消息类型的long int长整型)
msgflg:默认为0
返回值:成功返回0,失败返回-1
消息结构一方面必须小于系统规定的上限,另一方面必须以一个long int长整型开始,接受者以此来确定消息的类型
struct msgbuf
{
long mtye; //用于确定消息类型
char mtext[1];
};
//eg:main中定义一个发送信号的赋值
struct msgbuf sendbuf = {888,"hello world"};
//将mtye 定义为888类型,接收消息msgrcv要填写相应的类型
3.msgrcv
功能:是从一个消息队列接受消息
原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:与msgsnd相同
long typ:填写与定义结构体相对于的类型,用于匹配接收相应的信息
函数msgrcv在读取消息队列时,type参数有下面几种情况
type == 0,返回队列中的第一个消息;
type > 0,返回队列中消息类型为 type 的第一个消息;
type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
4.msgctl
功能:消息队列的控制函数
原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
msqid:由msgget函数返回的消息队列标识码
cmd:有三个可选的值,在此我们使用IPC_RMID
- IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前关联值
- IPC_SET 在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
- IPC_RMID 删除消息队列
*buf:一般写为NULL
返回值:
成功返回0,失败返回-1
删除消息队列
消息队列可以通过msgctl(msgid,IPC_RMID,NULL)删除
同时还有两个指令
ipcs:显示IPC资源
ipcrm:手动删除IPC资源
四、demo消息队列的信息传输
进程A B的信息传递,B添加一个数据块(类型888)到消息队列,A从消息队列上获取B添加的数据块。A获取后也添加一个数据块(类型988)到消息队列,B再从消息队列上获取改数据块。完成AB间信息交互。
msg_A.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
// int msgget(key_t key, int msgflg);
//int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
//int msgsnd(int msqid, const void *ptr, size_t size, int flag);
struct msgbuf
{
long ntype;
char mtext[128];
};
int main()
{
key_t key;
key = ftok(".",'z'); //产生键值key,两进程保证键值相同以确定同一消息队列
printf("key = %x\n",key);
struct msgbuf readbuf; //定义一个用于获取(相当于接收)的readbuf数据块
int msgid = msgget(key,IPC_CREAT|0777); //创建或打开一个消息队列
if(msgid == -1) //如果创建失败
{
printf("creat faile");
}
//从消息队列上接收数据块到readbuf,888为相对应的类型
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);//readbuf此时内容为空,sizeof
printf("read from send:%s\n",readbuf.mtext);//打印获取的消息
//创建一个用于添加(相当于发送)的sendbuf数据块,并定义数据类型为988
struct msgbuf sendbuf = {988,"thank you over"};
//将数据块添加到消息队列 根据内容大小strlen
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
msgctl(msgid,IPC_RMID,NULL);//删除消息队列
return 0;
}
msg_B.c
//头文件省略
struct msgbuf
{
long ntype;
char mtext[128];
};
int main()
{
key_t key;
key = ftok(".",'z');
printf("key = %x\n",key);
//创建一个用于添加(相当于发送)的sendbuf数据块,并定义数据类型为888
struct msgbuf sendbuf = {888,"hello world"};
struct msgbuf readbuf; //定义一个用于获取(相当于接收)的readbuf数据块
int msgid = msgget(key,IPC_CREAT|0777); // //创建或打开一个消息队列
if(msgid == -1)
{
printf("creat faile");
}
//将数据块添加到消息队列 strlen
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);//strlen
//从消息队列上接收数据块到readbuf,888为相对应的类型
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),988,0); //sizeof
printf("read from get :%s\n",readbuf.mtext);
//打印获取的内容
return 0;
}
~
进程A 与 进程B 同时运行完成信息交互
A获取到B添加到消息队列的hello world
B获取到A添加到消息队列的thank you over