定义:
消息队列是不同进程间通讯的一种方法。每个数据块都函数一个数据类型。接收进程可以独立接收不同类型的数据结构。
常用函数
int msgget(key_t key,int msgflg); | |||
功能 | 创建和访问一个消息队列 | ||
参数 | 输入参数 | key | 进程间通信键值 |
msgflg | 消息队列访问权限 跟文件权限一样。 可以与IPC_CREART做或操作。 | ||
返回值 | int | 返回一个消息队列标识符 成功:非零值 失败:-1 |
int msgrcv(int msgid, void *msg_ptr, size_t msg_size, long int msgtype, int msgflg) | |||
功能 | 该函数用来获取一个消息 | ||
参数 | 输入参数 | msgid | msgget()返回消息队列消息符 |
msg_ptr | 指向准备接收数据的指针,但是消息的数据结构有一定要求,msg_ptr所指向的消息结构一定要是一个长整型成员变量开始的结构体。消息队列定义结构体一定是这样的: strcut my_message{ long int message_type; /*The data you wish to transfer*/ }; | ||
msg_size | 接收数据长度,注意,这里是指消息长度,不是结构体msg_ptr长度,也就是说明不包含message_type。 | ||
msgtype | 消息接受的优先级。 如果msgtype为0,就获取消息队列中第一个消息。 如果msgtype>0.就获取具有相同消息类型的第一个消息。 如果msgtype<0,就获取当前值绝对值的第一个参数。 | ||
msgflg | 用于控制当队列中没有相应类型的消息。 | ||
返回值 | int | 成功:返回放到接收缓冲区中的字节数,被消息的消息复制到msg_ptr缓冲区中,然后删除相关消息队列 。 失败:返回-1。 |
int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz , int msgflg); | |||
功能: | |||
参数 | 输入参数 | msgid | msgget返回消息队列标识符 |
msg_ptr | 一个指向发送队列的指针,但是对数据结构有要求,msg_ptr指针指向一个结构体,该结构体一定要以一个长整型成员变量开始的结构体。接收函数将用该结构体中消息类型来确定消息的类型。 | ||
msg_sz | msg_ptr指向消息长度,注意不是msg_ptr指向结构体的长度,而是发送内容长度 | ||
msgflg | 用于控制当前消息队列满或者小修队列达到系统范围的限制将要发生的事。 | ||
返回值 | int | 成功:0。 失败:-1 |
int msgctl(int msgid , int command ,struct msgid_ds *buf) | |||
功能: | 该函数用来控制消息队列,用户可以通过函数msgctl获取或设置消息队列的属性。,他与共享函数shmctl相似 | ||
参数 | 输入参数 | msgid | msgget返回消息队列标识符 |
command | IPC_STAT:该命令用来获取消息队列对应的msgid_ds的数据结构,并将其保存到buf所指向的地址空间 IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中 IPC_RMID:从内核中删除msgid表示的消息队列 | ||
buf | 指向msgid_ds结构体的指针,它指向消息队列模式和访问权限的结构。msgid_ds结构至少包含以下成员: struct msgid_ds{ uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; } | ||
返回值 | int | 成功:0 失败:-1 |
Demo
下面请看demo。
首先我们先写发送端文件 msgsnd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
struct MSG_ST{
long int msg_type;
char buf[BUFSIZ];
};
void main(){
struct MSG_ST msg;
key_t key = ftok("./msg",23);
if(key < 0){
perror("ftok error");
exit(1);
}
int fd = msgget(key,0666|IPC_CREAT);
if(fd < 0){
perror("msgget error");
exit(1);
}
while(1){
char buf[BUFSIZ]={0};
// scanf("%s",buf);
fgets(buf, BUFSIZ, stdin);
printf("iput : %s\r\n",buf);
strcpy(msg.buf,buf);
msg.msg_type=1;
int ret = msgsnd(fd,(void *)&msg,BUFSIZ,0);
if(ret < 0){
perror("msgsnd error");
exit(1);
}else{
printf("send successfully %d\r\n",ret);
}
if(!strncmp(buf,"end",3)){
printf("end project\r\n");
break;
}
}
exit(0);
}
解这我们些接收端文件 msgrcv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
struct MSG_ST{
long int msg_type;
char buf[BUFSIZ];
};
int main(){
struct MSG_ST msg;
int msgtype=1;
key_t key = ftok("./msg",23);
if(key < 0){
perror("ftok error");
exit(1);
}
int msgid=msgget(key,0666|IPC_CREAT);
if(msgid < 0){
perror("msgget error");
exit(1);
}
printf("ready to recv data\r\n");
while(1){
int ret = msgrcv(msgid,(void *)&msg,BUFSIZ,msgtype,0);
if(ret < 0){
perror("msgrcv error");
exit(1);
}else{
printf("recv suceesfully %d",ret);
}
printf("recv: %s",msg.buf);
if(!strncmp(msg.buf,"end",3)){
printf("recv process exit");
break;
}
}
if(msgctl(msgid,IPC_RMID,0)<0){
perror("msgctl error");
// exit();
}
exit(0);
}
运行以上程序,先运行发送端函数,键盘输入“23424234234”,看到显示成功发送
yates@yates-virtual-machine:~/file/code$ ./msgsnd
23424234234
iput : 23424234234
send successfully 0
接着运行接收端函数,运行结果是
yates@yates-virtual-machine:~/file/code$ ./msgrcv
ready to recv data
recv suceesfully 8192recv: 23424234234
看见已经接收到接收端发送数据。
如果我们把接收端消息类型0改为1呢,运行结果会怎么样?
即把 “int msgtype=1;”改为“int msgtype=2”,
结果是无论发送端怎么发送,接收端都没收到,原因是此时接收端只接受消息类型为2的消息。
有人会问,那么消息队列跟命名管道区别是什么?
1.命名管道读写用 write和read,消息队列读写用msgsnd和msgrcv,并且数据有一个最大长度限制。
2.消息队列可以独立发送发送和接收函数而存在,命名管道可以能在打开和关闭时候产生困难。
2.发送消息队列可以比面管道的同步和阻塞问题,不需要进程自己提供同步方法。
3.接收程序可以通过消息类型有选择地接收数据,因为区分消息类型。不像命名管道那样,只能默认接收。