内容
介绍
消息队列也能实现不同进程之间的通信。和管道不同的是每条消息都有类型有格式的,他可以实现消息的随机查询,可以不按照先入先出的原则,只有内核重启或者人工删除的时候消息队列才回被删除,否则一直存在于内存之中,所以我们指定IPC键值的时候要保证唯一。消息队列的个数最多16个,总容量最多为16384个字节,每个消息的内容最多8192字节。和原道一样,数据读取了以后就会被删除。
获取键值
可以通过ftok()来获取到一个唯一的IPC键值
key_t ftok(const char *pathname, int proj_id);
依赖的头文件:
#include <sys/types.h>
#include <sys/ipc.h>
参数:
pathname:路径名
proj_id:项目id
返回值:成功返回key,失败返回-1
创建消息队列
可以通过msgget()来获取到一个唯一的IPC键值
int msgget(key_t key, int msgflg);
依赖的头文件:
#include <sys/msg.h>
参数:
key:ICP键值,一般要保证唯一性
msgflg:函数的行为IPC_CREAT(创建)或IPC_EXCL(如果已经存在则返回失败)
返回值:成功返回队列标识符,失败返回-1
消息的格式
首先要指定消息的类型,必须为长整型,并且是结构体的第一个成员。说明:此时的类型是指的这个消息的类型,这一个消息队列可以传输好多消息,可以按照消息的类型进行区分,相当于消息的id。
typedef struct _msg
{
long mtype;
/*消息类型*/
char mtext[100];
/*消息正文*/
... /*消息的正文可以有多个成员*/
}MSG;
发送消息
可以通过msgsnd()来发送消息
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
依赖的头文件:#include <sys/msg.h>
#include <sys/msg.h>
参数:
msqid:消息队列的id
msgp:要发送的消息的结构体
msgsz:消息正文的长度
msgflg:控制条件,0:msgsnd调用阻塞直到条件满足为止。IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。
返回值:成功返回0,失败返回-1
接收消息
可以通过msgrcv()来接收消息
ssize_t msgrcv(int msqid, void *msgp, size_tmsgsz, long msgtyp, int msgflg);
依赖的头文件:#include <sys/msg.h>
#include <sys/msg.h>
参数:
msqid:消息队列的id
msgp:存放消息的结构体,要与发送方的一致。
msgsz:消息正文的长度
msgtyp:msgtyp = 0:返回队列中的第一个消息;msgtyp > 0:返回队列中消息类型为msgtyp的消息;msgtyp < 0:返回队列中消息类型值小于或等于msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息
msgflg:控制条件,0:msgrcv调用阻塞直到接收消息成功为止。MSG_NOERROR:若返回的消息字节数比nbytes字节数
多,则消息就会截短到nbytes字节,且不通知消息发送进程。IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1
返回值:成功返回实际读取到的消息的长度,失败返回-1
测试
正常发送和接收
我们在写端调用msgsnd()函数将消息传送过去,从读端用msgrcv()将数据读出来,这里需要注意的是:写端的key、消息类型要和读端的一致,具体的代码如下:
写端:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct _msg{
long mtype;
char mtext[50];
}MSG;
void main(){
key_t key;
int msgqid;
MSG msg;
key = ftok(".",666);
printf("key:%d\n",key);
msgqid = msgget(key,IPC_CREAT|0666);
if(msgqid == -1)
{
perror("msdget出错\n");
exit(-1);
}
msg.mtype = 10;
strcpy(msg.mtext,"helloworld");
msgsnd(msgqid,&msg,sizeof(msg.mtext),0);
}
读端:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct _msg{
long mtype;
char mtext[50];
}MSG;
void main(){
key_t key;
int msgqid;
MSG msg;
key = ftok(".",666);
msgqid = msgget(key,IPC_CREAT|0666);
if(msgqid == -1)
{
perror("msgget错误\n");
exit(-1);
}
msgrcv(msgqid,&msg,sizeof(msg.mtext),10,0);
printf("获取到的数据为:【%s】\n",msg.mtext);
}
运行结果:
修改接收端的key,使之与发送端的key不一致
修改后,两端的key值不同,生成的消息队列自然也不会相同,所以二者之间不能进行通信。读端会进入阻塞状态。
运行结果:
收发两条消息类型不同的消息
我们分别发送两条消息类型为10和11的两条消息,然后再从读端将两条消息读取出来。
写端代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct _msg{
long mtype;
char mtext[50];
}MSG;
void main(){
key_t key;
int msgqid;
MSG msg;
key = ftok(".",666);
printf("key:%d\n",key);
msgqid = msgget(key,IPC_CREAT|0666);
printf("msgqid:%d \n",msgqid);
if(msgqid == -1)
{
perror("msdget出错\n");
exit(-1);
}
msg.mtype = 10;
strcpy(msg.mtext,"helloworld");
msgsnd(msgqid,&msg,sizeof(msg.mtext),0);
printf("10号消息发送成功\n");
msg.mtype = 11;
strcpy(msg.mtext,"hi everyone");
msgsnd(msgqid,&msg,sizeof(msg.mtext),0);
printf("11号消息发送成功\n");
}
读端代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct _msg{
long mtype;
char mtext[50];
}MSG;
void main(){
key_t key;
int msgqid;
MSG msg;
key = ftok(".",666);
printf("key:%d \n",key);
msgqid = msgget(key,IPC_CREAT|0666);
printf("msgqid:%d \n",msgqid);
if(msgqid == -1)
{
perror("msgget错误\n");
exit(-1);
}
msgrcv(msgqid,&msg,sizeof(msg.mtext),10,0);
printf("10号获取到的数据为:【%s】\n",msg.mtext);
msgrcv(msgqid,&msg,sizeof(msg.mtext),11,0);
printf("11号获取到的数据为:【%s】\n",msg.mtext);
}
运行结果表名两条消息都可以正常收到,结果如下图所示:
作业
通过消息队列,实现进程间收发字符串"hello world" 的功能。
答案:上述【正常发送和接收】部分。