进程间通讯(ipc)之消息队列:
1.在任意进程间有选择性地进行通讯的方式
2.发送数据时需要携带一定的消息标志
3.消息队列创建后由于是存在于内核中由内核管理,使用过后需要人工删除以释放资源
4.当一个发送方往消息队列中发送数据时,接收方一直还没接收,数据会一直在消息队列里等待接收方的出现
我们换个通俗点的说法:小红和小绿是同班同学,小绿喜欢小红,但是小红不喜欢小绿,很明显哪怕他们在一起了也是没有结果的,爱情是要双方互相喜欢的情况下才会有好的结果,于是在我们没有遇到生命中的那个人时我们就会等他/她的出现;消息队列就如爱情❤,如果他们的消息标识符不一致是不能进行通讯的,它会一直等待接收(或发送)它消息的进程出现
相关函数原型:
//创建键值函数
//函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
//函数原型
key_t ftok(const char *pathname, int proj_id);
//函数介绍:
/********************************************************
函数作用:用于创建一个键值,
键值作用 :用于进程间通讯(ipc)时的一个标识符
形参:pathname:路径名-->通过哪一个路径来获得键值
proj_id( project identifier):项目id:即我们指定给它一个8位(一个字节,最大256)的整数,
注:其实可以看成路径下的消息队名为给它的数
返回值:
成功:新生成且未被使用的键值
失败:-1
********************************************************/
//获取消息队列的id号,也可使用参数在不存在的情况下创建
//函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//函数原型
int msgget(key_t key, int msgflg);
//函数介绍:
/********************************************************
函数作用:用于获取一个消息队列的id号或者创建一个消息队列
形参:key:键值,通过使用ftok函数创建返回的一个键值
msgflg:所对应的操作:
IPC_CREAT :键值key所对应的消息队列不存在则创建
(创建时需要给它一个权限使用方法类似于open函数,在后面使用位或或上操作权限)
O_EXCL:键值key对应的消息队列存在则报错
注:当键值key被指定为IPC_PRIVATE时,系统自动分配一个键值来对应消息队列
返回值:
成功:返回消息队列的标识符
失败:-1
********************************************************/
测试代码:
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
int main(int argc,char **argv)
{
//合成键值,在当前目录下合成键值
key_t key = ftok(".",1);
if(key == -1)
{
perror("ftok failed");
return -1;
}
//创建消息队列,返回一个msgid
int msgid = msgget(key,IPC_CREAT | 0666);
if(msgid == -1)
{
perror("msgget failed");
return -1;
}
return 0;
}
(1)当路径与pro_t值都一样时,返回的是同一个值(即合成的键值是同一个数)
(2)合成键值其实有点类似于创建一个文件的(但本质不一样),我们指定一个路径加上我们自定义的一个数字(类似我们指定一个路径下创建一个名字为什么的文件)
关于消息队列用于通讯的函数
//消息队列用于发送和接收的函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
/********************************************************
函数作用:
形参:msqid:通过msgget函数获取到的消息队列id号
msgp:一个自己定义的消息队列结构体的地址
这个结构体一般形式有:
struct msgbuf {
long mtype; //消息的标识符,且必须大于0
char mtext[1]; //消息正文,数组大小由自己定义
};
注:结构体第一个元素必须为long类型的标识符!!!
msgsz:发送数据的正文的大小,取决于消息队列结构体中mtext数组的大小
msgtyp:消息队列接收函数中用于选择接收的数据的消息标识符
msgflg:参数有
0 :阻塞写入或读取数据
IPC_NOWAIT:往消息队列或写入或读取数据时不阻塞等待
MSG_NOERROR:消息长度超出 msgsz时截断信息且不报错
返回值:
msgsnd:
成功: 0
失败:-1
msgrcv:
成功:读取到的字节数
失败:-1
********************************************************/
//获取或设置删除消息队列函数
//函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//函数原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
/********************************************************
函数作用:获取消息队列属性,设置消息队列属性或删除消息队列
形参:msqid:通过msgget函数获取到的消息队列id号
cmd:IPC_STAT:获取消息队列的属性,储存到struct msqid_ds *buf中
IPC_SET:把消息队列的属性设置为struct msqid_ds *buf中的属性
IPC_RMID:删除消息队列
IPC_INFO:获取当前系统消息队列的限制值信息
MSG_INFO:获取当前系统消息队列的相关资源消耗情况
buf:一个struct msqid_ds类型的变量的地址,这个结构体的成员有:
struct msqid_ds {
struct ipc_perm msg_perm; //消息队列的所有权和权限
time_t msg_stime; //消息队列最后一次发送信息的时间
time_t msg_rtime; // 最后一次一次接收信息的时间
time_t msg_ctime; // 最后一次更改消息队列状态时间
unsigned long __msg_cbytes; //当前消息队列的数据大小
msgqnum_t msg_qnum; //当前消息队列的消息个数
msglen_t msg_qbytes; //消息队列最大的数据大小
pid_t msg_lspid; //最后一次发送信息的进程id号
pid_t msg_lrpid; //最后一次接收信息的进程id号
};
返回值:
成功:
IPC_STAT:0
IPC_SET:0
IPC_RMID:0
IPC_INFO:内核中记录所有消息队列信息的数组的下标最大值
MSG_INFO:内核中记录所有消息队列信息的数组的下标最大值
失败:-1
********************************************************/
测试代码:
发送端:
/*
消息队列发送
*/
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
//设计消息队列结构体,数组大小为30
struct msgdata
{
long type;
char data[30];
};
int main()
{
//申请一个消息队列结构体变量
struct msgdata msg_sed;
//清空一下结构体变量
bzero(&msg_sed,sizeof(msg_sed));
//产生键值,第二个值可以为随机值,只要前面创建其他队列没有使用过
key_t key = ftok(".",1);
if(key == -1)
{
//创建失败直接退出
perror("ftok failed");
return -1;
}
//创建消息队列给0666权限,返回一个msgid
int msgid = msgget(key,IPC_CREAT | 0666);
if(msgid == -1)
{
//创建失败直接退出
perror("msgget failed");
return -1;
}
//不断发送数据
while(1)
{
//每次发送前清空一下结构体里的内容
bzero(&msg_sed,sizeof(msg_sed));
printf("请输入需要发送的数据:");
//发送类型为1
msg_sed.type = 1;
fgets(msg_sed.data,30,stdin);
//打印一下字符串长度和内容
//printf("%ld\n",strlen(msg_sed.data) );
//printf("%s\n",msg_sed.data );
//往消息队列发送数据
msgsnd(msgid,&msg_sed,strlen(msg_sed.data),0);
}
//删除消息队列
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
接收端:
/*
消息队列接收
*/
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>
//设计消息队列结构体
struct msgdata
{
long type;
char data[30];
};
int main()
{
//申请一个消息队列结构体变量
struct msgdata msg_rcv;
//
key_t key = ftok("./",1);
if(key == -1)
{
perror("ftok failed");
return -1;
}
//获取消息队列id号,如果不存在则创建
int msgid = msgget(key,IPC_CREAT | 0666);
if(msgid == -1)
{
perror("msgget failed");
return -1;
}
//接收数据
while(1)
{
//每次清空一下结构体变量里的内容
bzero(&msg_rcv,sizeof(msg_rcv));
//msgrcv(msgid,&msg_rcv,30,1,0);
//从消息队列读取数据,并判断是否读取成功
int ret = msgrcv(msgid, &msg_rcv, 30 ,1, 0);
if(ret == -1)
{
perror("error");
}
printf("接收到的数据%s", msg_rcv.data);
}
//删除消息队列
msgctl(msgid,IPC_RMID,NULL);
return 0;
}