目录
1、消息队列介绍
消息队列与管道类似,但不需要像管道一样要提前创建管道文件,不使用文件方式打开、关闭,而转用函数接口在使用时再创建。多个进程可以通过相同的键值获取到同一消息队列的描述符,从而访问消息队列,进行通信。
1.1、缺点
与管道一样,若发送方持续发送消息,而接收方一直未接收消息,待消息队列满时,发送方会发送失败。
Linux系统有两个宏定义MSGMAX和MSGMNB,以字节为单位分别定义了一条消息的最大长度和一个队列的最大长度。
1.2、优点
1、提供了从一个进程向另一进程发送数据块的方法,数据块可以被标记为不同类型的消息。接收消息进程可根据消息类型独立接收每条消息,可实现消息分类与简单的优先级控制。
2、可通过设置非等待标志,使发送消息与接收消息失败时不阻塞。
2、函数接口介绍
2.1、msgget
用于创建一个消息队列,或获取一个已有的消息队列的描述符。成功返回队列描述符,失败返回-1。其他函数接口通过消息队列描述符,对指定消息队列进行操作。
函数原型:
int msgget(key_t key, int msgflg);
1、key:用来命名某个特定的消息队列。多个进程可以通过相同的键值获取到同一队列的描述符。
2、msgflg:包括由9bit的权限标志,设置本进程所属用户的进程、用户同属组的进程与其他进程对该消息队列的访问权限。与一个特殊位IPC_CREAT按位或,来创建一个新的消息队列。若用key命名的消息队列已存在,操作不出错,IPC_CREAT被忽略。
2.2、msgsnd
用于把消息添加到消息队列中。成功返回0,失败返回-1。消息以结构体形式保存。
函数原型:
int msgsnd(int msqid, const void *msg_ptr,
size_t msg_sz, int msgflg);
1、msqid:msgget()返回的消息队列标识符。
2、msg_ptr:指向待发送消息的指针。消息结构体形式:
struct my_massage{
long int message_type;
......
}
消息结构体必须以 长整型message_type 开始,其指定了消息的类型。后续为实际消息内容,可以以数组等形式保存。
3、msg_sz:消息内容的字节长度,不包括长整型消息类型。
4、msgflg:控制当消息队列满或一条消息过长时函数的动作。
若msgflg设置为IPC_NOWAIT,当消息队列已满时,函数不发送消息,立即返回,返回-1;若IPC_WAIT标志被清除,则函数阻塞,等待到直到消息队列有空间后,将消息发送出去。
消息长度超出最大消息长度时的可能结果:消息被截断、消息被分片 依次发送、消息发送失败。
2.3、msgrcv
用于从消息队列获取一条消息。成功返回消息内容长度字节数,并将信息拷贝到msg_ptr指向的结构体中,删除消息队列中对应的消息;失败返回-1。
函数原型:
int msgrcv(int msqid, void *msg_ptr,
size_t msg_sz, long int msgtype,
int msgflg);
1、msqid:消息队列描述符。
2、msg_ptr:接收方拥有的消息结构体,用于接收来自消息队列的消息的拷贝。
3、msg_sz:一次能接收的消息内容的最大长度。
4、msgtype:指定要接收的消息类型,可实现简单的优先级控制。
(1)0:接收消息队列的第一条消息。
(2)n:只接收类型为n的消息。
(3)-n:接收消息类型 <= n 的所有消息。
5、msgflg:设置阻塞或非阻塞(IPC_NOWAIT)。
2.4、msgctl
用于设置或获取进程对消息队列的权限信息,或删除消息队列。成功返回0,失败返回-1。若消息队列被删除,则正对该消息队列进行的读、写会失败。
函数原型:
int msgctl(int msqid, int command,
struct msqid_ds *buf);
1、struct msqid_ds *buf:指向权限信息结构体的指针。
struct msqid_ds{
uid_t msg_perm.uid;//用户id
uid_t msg_perm.gid;//用户所在组id
mode_t msg_perm.mode;//权限值
}
2、command:函数采取的动作:
命令 | 说明 |
IPC_STAT | 获取消息队列对本进程的权限设置 |
IPC_SET | 将结构体中的值设置给消息队列 |
IPC_RMID | 删除消息队列 |
3、实操
接收消息进程Reader.c:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<sys/msg.h>
#define BUFFSIZE 1024
struct my_msg{ //消息结构体
long int massage_type;
char text[BUFFSIZE];
};
int main(){
//以键值1234创建消息队列
int msgid = msgget((key_t)1234,0666 | IPC_CREAT);
if(msgid==-1){
fprintf(stderr,"msgget failed with error: %d\n",errno);
exit(1);
}
struct my_msg recv_data;//消息结构体用于接收消息
while(1){
//接收消息
if(msgrcv(msgid, (void*)&recv_data,
BUFFSIZE,0,0)==-1){//0:按顺序接收消息,无消息时阻塞
fprintf(stderr,"msgrcv failed with error: %d\n",errno);
exit(1);
}
printf("read: %s",recv_data.text);
if(strncmp(recv_data.text,"end",3)==0){//以end为通信结束标志
break;
}
}
//删除消息队列
if(msgctl(msgid,IPC_RMID,NULL)==-1){
fprintf(stderr,"msgctl(IPC_RMID) failed\n");
exit(1);
}
exit(0);
}
发送消息进程Writer.c:
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<sys/msg.h>
#define BUFFSIZE 1024
struct my_msg{
long int massage_type;
char text[BUFFSIZE];
};
int main(){
//以相同key值获取消息队列的描述符
int msgid = msgget((key_t)1234,0666 | IPC_CREAT);
if(msgid==-1){
fprintf(stderr,"msgget failed with error: %d\n",errno);
exit(1);
}
struct my_msg send_data;
char buff[BUFFSIZE]={0};
while(1){
printf("Enter some text: ");
fgets(buff,BUFFSIZE,stdin);
send_data.massage_type=1;//消息类型为1
strncpy(send_data.text,buff,BUFFSIZE);
//发送消息
if(msgsnd(msgid,(void*)&send_data,
BUFFSIZE,0)==-1){//消息队列满时阻塞
fprintf(stderr,"msgsnd failed\n");
exit(1);
}
if(strncmp(buff,"end",3)==0){
break;
}
}
exit(0);
}
分别执行两进程:写进程等待数据写入,还未向消息队列写入消息,因而读进程阻塞。
写端写入消息后,消息队列中有消息,读端从阻塞转入运行状态,接收消息后打印。再次等待写端向消息队列写入消息。
写端写入end,通信终止。