(一)进程间通信的系统IPC方法(消息队列)编程
(1) 消息队列基础知识
1.1 消息队列的定义
消息队列:在消息的传输过程中保存消息的容器(本质上是位于内核空间的链表,链表的每个节点都是一条消息。消息类型为 0 的链表记录了所有消息加入队列的顺序,除此之外每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0)。
限制:
①每个消息的最大长度是有上限的(MSGMAX)。
②每个消息队列的总的字节数是有上限的(MSGMNB)。
③系统上消息队列的总数也有一个上限(MSGMNI)。
拓展:
①什么是消息?
消息:是在两台计算机间传送的数据单位,也可以理解为有边界信息的字节流。下至包含文本字符串,上至包含嵌入对象(不同于字节流,字符流)。
1.2 消息队列涉及函数
系统IPC方法的步骤基本相同。消息队列创建步骤也分为了:创建和访问MQ,发送消息,接收消息,删除MQ
1.2.1 ftok()函数
ftok()函数:系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
#include <sys/types.h> //头文件包含
#include <sys/ipc.h>
key_t ftok( const char * pathname, int id)
参数 | 功能 |
---|---|
pathname | pathname:指定的文件名或路径(一般:当前目录)) |
int id | id:子序号(int型,但只有8bits:1~255) |
返回值:在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到。
1.2.2 msgget()函数
msgget()函数:用来创建消息队列ID。
#include <sys/types.h> //头文件包含
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数 | 功能 |
---|---|
key | ftok()返回的key_t类型键值 |
msgflg | 创建标志 |
参数msgflg两种设置:
①IPC_CREAT,不存在则创建,存在则返回已有的mqid。②IPC_CREAT|IPC_EXCL,不存在则创建,存在则返回出错。
返回值:
成功:返回一个非负整数的共享内存段的标识码
失败:返回-1,并设置相应errno值。
1.2.3 msgsnd()函数
msgsnd()函数:用来发送一个消息,必须要有写消息队列的权限。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数 | 功能 |
---|---|
msgid | 由msgget函数返回的消息队列 ID |
msgp | 指针,指向要发送的消息结构体类型的变量。限制:①小于系统上限值②以一个long int长整数开始 |
msgsz | 发送消息的长度 |
msgflg | 控制着当前消息队列满或到达系统上限时将要发生的事情,设置IPC_NOWAIT表示队列满不等待,返回EAGAIN错误(设为0:线程将被阻塞至消息被写入) |
返回值:
成功:返回0。
失败:返回-1,并设置相应errno值。
msgp指针指向的结构体示例:
typedef struct s_msgbuf
{
long mtype;
char mtext[512];
} t_msgbuf;
1.2.4 msgrcv()函数
msgrcv()函数:用来从一个消息队列中接收消息。
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数 | 功能 |
---|---|
msgid | 由msgget函数返回的消息队列 ID |
msgp | 一个指针,它指向准备接收的消息 |
msgsz | msgp指向的消息长度(这个长度不含保存消息类型的那个long int长整型) |
msgtype | 消息的类型,它可以实现接收优先级的简单形式 |
msgflg | 控制着队列中没有相应类型的消息可供接收时将要发生的事 |
参数msgtype的值设置:
①等于0:返回队列第一条信息。
②大于0:返回队列第一条类型等于msgtype的消息。
③小于0:返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息
参数msgflg的值设置:
①IPC_NOWAIT:队列没有可读消息不等待,返回ENOMSG错误。
②MSG_NOERROR:消息大小超过msgsz时被截断。
③msgtype>0且msgflg=MSG_EXCEPT:接收类型不等于msgtype的第一条消息。
④0:一直阻塞,直到有信息进入。
返回值:
成功:返回实际接收到缓冲区的字符个数。
失败:返回-1,并设置相应errno值。
1.2.5 msgctl()函数
msgctl()函数:用于控制消息队列。
参数 | 功能 |
---|---|
msgid | msgget函数返回的消息队列 ID |
cmd | 采取的操作 |
buf | 一个结构指针,它指向存储消息队列的相关信息的 buf |
参数cmd的值设置:
①IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值。
②IPC_SET: 如果进程有足够的权限,就把消息队列的当前关联值设置为msqid_ds结构中给出的值。
③IPC_RMID:删除消息队列。
(2) 不同进程收发消息编程示例
2.1 不同进程收发消息编程的代码示例
2.1.1 消息发送方的代码示例
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define FTOK_PATH "/dev/zero"
#define FTOK_PROJID 0x66
typedef struct s_msgbuf
{
long mtype;
char mtext[512];
}t_msgbuf;
int main(int argc,char**argv)
{
key_t key;
int msgid;
int i;
t_msgbuf msgbuf;
int msgtype;
if((key=ftok(FTOK_PATH,FTOK_PROJID))<0)
{
printf("ftok() get IPC token failure:%s\n",strerror(errno));
return -1;
}
//创建消息队列ID
msgid=msgget(key,IPC_CREAT|0666);
if(msgid<0)
{
printf("msgget() create msgqueue failure:%s\n",strerror(errno));
return -2;
}
//转化后,打印各值
msgtype=(int)key;
printf("key[%d] msgid[%d] msgypte[%d]\n",(int)key,msgid,msgtype);
for(i=0;i<4;i++)
{
//将获得的队列ID放入msgbuf结构体中
msgbuf.mtype=msgtype;
strcpy(msgbuf.mtext,"Ping");
//发送信息
if(msgsnd(msgid,&msgbuf,sizeof(msgbuf.mtext),IPC_NOWAIT)<0)
{
printf("msgsnd() send message failure :%s\n",strerror(errno));
break;
}
printf("send message:%s\n",msgbuf.mtext);
sleep(1);
}
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
2.1.2 消息接收方的代码示例
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define FTOK_PATH "/dev/zero"
#define FTOK_PROJID 0x66
typedef struct s_msgbuf
{
long mtype;
char mtext[512];
}t_msgbuf;
int main(int argc,char**argv)
{
key_t key;
int msgid;
int i;
t_msgbuf msgbuf;
int msgtype;
if((key=ftok(FTOK_PATH,FTOK_PROJID))<0)
{
printf("ftok() get IPC token failure:%s\n",strerror(errno));
return -1;
}
//创建消息队列ID
msgid=msgget(key,IPC_CREAT|0666);
if(msgid<0)
{
printf("msgget() create msgqueue failure:%s\n",strerror(errno));
return -2;
}
//转化后,打印各值
msgtype=(int)key;
printf("key[%d] msgid[%d] msgypte[%d]\n",(int)key,msgid,msgtype);
for(i=0;i<4;i++)
{
memset(&msgbuf,0,sizeof(msgbuf));
//接收信息
if(msgrcv(msgid,&msgbuf,sizeof(msgbuf.mtext),msgtype,0)<0)
{
printf("msgrcv() receive message failure :%s\n",strerror(errno));
break;
}
printf("receive message:%s\n",msgbuf.mtext);
sleep(1);
}
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
2.1.3 遇到的问题
发送没有问题:
接收出现问题:No message of desired type
这里提示:没有符合需求类型的消息。
那么,我在想key值是相同的,但是接收方却接收不到信息,可能是接收上出现问题了。
解决一:将msgrcv()函数中的msgflg设为0,让其一直阻塞看看情况怎么样。
先运行接收程序,让其阻塞等待消息:
再运行发送程序:
接收程序的结果:
我们发现,这样就解决了之前的问题了。