Linux 进程间的通信(三)—消息队列
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。消息队列与命名管道一样,每个数据块都有一个最大长度的限制
消息缓冲区结构
常用的结构是msgbuf结构。程序员可以以这个结构为模板定义自己的消息结构。在头文件<linux/msg.h>中,它的定义如下所示:
struct msgbuf {
long mtype;
char mtext[1];
};
获得消息函数msgget()
创建一个新的消息队列,或者访问一个现有的队列,可以使用函数msgget(),其原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
msgget()的第一个参数是键值,可以用ftok()函数生成,这个关键字的值将被拿来与内核中其他消息队列的现有关键字值相比较。
msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。
发送消息函数msgsnd()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
成功返回0,失败返回-1
函数msgsnd()的第一个参数是队列标识符,它是前面调用msgget()获得的返回值。第二个参数是msgp,它是一个void类型的指针,指向一个消息缓冲区。msgsz参数则包含着消息的大小,它是以字节为单位的,其中不包括消息类型的长度(四个字节长)。 msgflg参数可以设置为0(表示忽略),也可以设置为IPC_NOWAIT。如果消息队列已满,则消息将不会被写入到队列中。如果没有指定IPC_NOWAIT,则调用进程将被中断(阻塞),直到可以写消息为止
接收消息函数msgrcv()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long sgtyp, int msgflg);
函数msgrcv()的第一个参数msqid是用来指定在消息获取过程中所使用的队列(该值是由前面调用msgget()得到的返回值)。
第二个参数msgp代表消息缓冲区变量的地址,获取的消息将存放在这里。
第三个参数msgsz代表消息缓冲区结构的大小,不包括mtype成员的长度。
第四个参数mtype指定要从队列中获取的消息的类型。内核将查找队列中具有匹配类型的第一个到达的消息,并把它的一个复制返回到由msgp参数所指定的地址中。如果mtype参数传送一个为零的值,则将返回队列中最新的消息,不管该消息的类型是什么。
消息控制函数msgctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgclt()向内核发送一个cmd命令,内核根据此来判断进行何种操作,buf为应用层和内核空间进行数据交换的指针。其中的cmd可以为如下值:
IPC_STAT:获取队列的msqid_ds结构,并把它存放在buf变量所指定的地址中,通过这种方式,应用层可以获得当前消息队列的设置情况,例如是否有消息到来,消息队列的缓冲区设置等等。
IPC_SET:设置队列的msqid_ds结构的ipc_perm成员的值,它是从buf中取得该值的。通过IPC_SET命令,应用层可以设置消息队列的状态,例如修改消息队列的权限,使其他用户可以访问或者不能访问当前的队列;甚至可以设置消息队列的某些当前值来伪装。
IPC_RMID:内核删除队列。使用此命令执行后,内核会把此消息队列从系统中删除。
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; /*发送最后一个消息的进程的PID *
pid_t msg_lrpid; /*接收最后一个消息的进程的PID */
};
案例一、创建消息队列,一个程序往里面发送消息,另一个读出消息,这是值写入一种类型的消息。消息写入程序结束后,发送的消息任在消息队列中。
消息队列跟命名管道有不少的相同之处,通过与命名管道一样,消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用write,接收数据用read,则在消息队列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。
与命名管道相比,消息队列的优势在于,1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难,命名管道默认阻塞模式下需要读写方都打开才不阻塞。
2、同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。
3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。
msgsend.c
/* ************************************************************************
* Filename: msgsend.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
// 创建消息队列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
printf("Enter some string: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1;
strcpy(data.text, buffer);
// 向消息队列发送数据
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
if( strncmp( buffer, "end", 3) == 0 ){
running = 0;
}
sleep(1);
}
exit( EXIT_SUCCESS );
}
msgrcv.c
/* ************************************************************************
* Filename: msgrcv.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
int msgid = -1;
int msgtype = 1;
// 创建消息队列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
// 向消息队列接受数据
if( msgrcv( msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){
fprintf(stderr, "msgrcv failed with erron: %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Rcv Msgage: %s", data.text);
if( strncmp( data.text, "end", 3) == 0 ){
running = 0;
}
}
if( msgctl(msgid, IPC_RMID, 0) == -1 ){
fprintf( stderr, "msgctl(IPC_RMID) failed\n");
exit( EXIT_FAILURE );
}
exit( EXIT_SUCCESS );
}
BUFSIZ 在 /usr/include/stdio.h
里定义
编译运行结果
案例一、写入不同类型的消息,按要求类型读取消息
msgsend1.c
/* ************************************************************************
* Filename: msgsend1.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
// 创建消息队列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
printf("Enter some string1: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1;
strcpy(data.text, buffer);
// 向消息队列发送数据
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
if( strncmp( buffer, "end", 3) == 0 ){
data.msg_type = 2;
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
break;
}
sleep(1);
printf("Enter some string2: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 2;
strcpy(data.text, buffer);
// 向消息队列发送数据
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
if( strncmp( buffer, "end", 3) == 0 ){
data.msg_type = 1;
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
break;
}
sleep(1);
}
exit( EXIT_SUCCESS );
}
msgrcv.c
/* ************************************************************************
* Filename: msgrcv.c
.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
int msgid = -1;
int msgtype = 1;
// 创建消息队列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
// 向消息队列接受数据
if( msgrcv( msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){
fprintf(stderr, "msgrcv failed with erron: %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Rcv Msgage: %s", data.text);
if( strncmp( data.text, "end", 3) == 0 ){
running = 0;
}
}
if( strncmp( data.text, "end", 3) == 0 ){
if ( msgget( (key_t)1234, 0666) != -1 ){
if( ( msgctl(msgid, IPC_RMID, 0) == -1 ) ){
fprintf( stderr, "msgctl(IPC_RMID) failed\n");
exit( EXIT_FAILURE );
}
}
}
exit( EXIT_SUCCESS );
}
msgrcv1.c
/* ************************************************************************
* Filename: msgrcv1.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
int msgid = -1;
int msgtype = 2;
// 创建消息队列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
// 向消息队列接受数据
if( msgrcv( msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){
fprintf(stderr, "msgrcv failed with erron: %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Rcv2 Msgage: %s", data.text);
if( strncmp( data.text, "end", 3) == 0 ){
running = 0;
}
}
if( ( msgget( (key_t)1234, 0666) != -1 ) && ( msgctl(msgid, IPC_RMID, 0) == -1 ) ){
fprintf( stderr, "msgctl(IPC_RMID) failed\n");
exit( EXIT_FAILURE );
}
exit( EXIT_SUCCESS );
}