一、什么是消息队列
消息队列是进程之间通讯的一种机制,每个数据块都被认为含有一个类型,接收进程可以根据类型来选择性的接受数据,而不像管道那样必须以先进先出的方式接受数据
Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。
二、在Linux中操作消息队列的函数
1.msgget函数
int msgget(key_t, key, int msgflg);
作用:创建消息队列,或者返回已存在的消息队列的id
key:程序必须提供一个键来命名某个特定的消息队列。
msgflg:权限标志,表示消息队列的访问权限,它与文件的访问权限一样。
如果使用IPC_CREAT表示如果不存在此消息队列则创建,如果存在则返回消息队
列的msgqid值
如果使用IPC_CREAT|IPC_EXCL 表示如果不存在此消息队列则创建,如果存在则
返回错误-1
msgflag一般的使用方法:IPC_CREAT |(文件权限)
例如:IPC_CREAT | 0666
2
、msgsnd函数
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
作用:
该函数用来把消息添加到消息队列中
msgid:msgget函数返回的消息队列标识符。
msg_ptr:是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针
msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,该
结构体比如有类似如下的定义
struct msgbuf
{
long mytype; /*消息类型*/
char buf[] ; /*消息内容*/
};
msg_sz :消息的数据部分长度
msgflg :通常只支持IPC_NOWAIT 标志,即以非阻塞的方式发送消息,默认情况下,发送
消息时消息队列已经满的话,msgsnd就会阻塞,如果IPC_NOWAIT被指定的话
msgsnd就会立马返回并设置errno为EAGAIN
另外:处于阻塞状态下的msgsnd调用可能被如下两种异常情况所中断
1.消息队列被移除 此时msgsnd立马返回并设置errno为EIDRM
2,程序接收到信号 此时msgsnd立马返回并设置errno为EINTR
返回值:成功返回0 失败返回-1并设置errno
3、msgrcv函数
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
作用 :从指定消息队列获取指定类型的消息
参数 :
msgid:msgget函数返回的消息队列标识符。
msg_ptr:获取到的消息的消息类型以及消息内容将存入该指针指向的结构体内存中
msgtype:要获取的消息的消息类型
msgflg:同msgsnd中msgflg
返回值:调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.
4、msgctl函数
int
msgctl(
int
msgid,
int
command,
struct
msgid_ds *buf);
struct msqid_ds { struct ipc_perm msg_perm; /* 消息队列的操作权限 */ time_t msg_stime; /* 最后一次调用msgsnd的时间 */ time_t msg_rtime; /* 最后一次调用msgrcv的时间 */ time_t msg_ctime; /* 最后一次被修改的时间 */ unsigned long __msg_cbytes; /* 消息队列中已有的字节数 */ msgqnum_t msg_qnum; /* 消息队列中已有的消息数 */ msglen_t msg_qbytes; /* 消息队列允许的最大字节数 */ pid_t msg_lspid; /* 最后执行msgsnd的进程的PID */ pid_t msg_lrpid; /* 最后执行msgrcv的进程的PID */ };
作用:控制消息队列
参数:
command是将要采取的动作,它可以取下列值,
IPC_STAT:将消息队列关联的内核数据结构复制到buf中,成功返回0
IPC_SET:将buf中的部分成员复制到消息队列关联的内核数据结构中,同时
内核数据中的msqid_ds.msg_ctime被更新,成功返回0
IPC_RMID:立即移除消息队列,唤醒所有等待读消息和写消息的进程,成功返回0
(这些进程的函数调用会立即返回并设置errno为EIDRM)
IPC_INFO:获取系统消息队列资源配置信息,将结果存储在buf中,应用程序
需要将buf转换成msginfo结构体类型来读取这些系统信息,成功时
返回内核消息队列信息数组中已经被使用的项的最大索引值
MSG_INFO:与IPC_INFO类似,不过返回的是已经分配的消息队列占用的资源信息
成功时返回内核消息队列信息数组中已经被使用的项的最大索引值
MSG_STAT:与IPC_STAT类似,不过此时msqid参数是内核消息队列信息数组的索引
成功时返回内核消息队列信息数组中索引值为msqid的消息队列的
标识符
返回值:成功时返回值取决于command参数,失败时返回-1并设置errno
三、示例代码
#include <fcntl.h> #include<assert.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <sys/msg.h> /* 创建消息队列并向消息队列写入消息的程序 */ int main() { /* 创建一个消息队列 用户层唯一标识key值为20 */ int msqid = msgget(20,IPC_CREAT|0666); if(msqid == -1) { perror("msgget error"); } else printf("create mesg queue :%d\n",msqid); /* 写入消息队列的消息类型为1 */ struct mymesg { long mtype; char mtext[512]; }mymesg={1,"hello,this is mesg queue\n"}; /* 将消息写入消息队列 */ int n = msgsnd(msqid,(void *)&mymesg,512,0); if(n == -1) { perror("msgsnd error"); return 0; } else printf("write success\n"); /* 使用完后删除消息队列 * 否则即使程序退出,消息队列也不会被清除 * */ #if 0 n = msgctl(msqid,IPC_RMID,NULL); if(n == -1) perror("mesg queue remove failed"); else printf("mesg queue has been removed\n"); #endif return 0; }
运行结果:
#include <fcntl.h> #include<assert.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <sys/msg.h> /* 读取指定消息队列中的指定消息的程序 */ int main() { /* 利用用户层的消息队列唯一key获得内核中的消息队列id */ int msqid = msgget(20,IPC_CREAT|0666); if(msqid == -1) { perror("msget error"); } struct mymesg { long mtype; char mtext[1024]; }mymesg; int n = msgrcv(msqid,(void *)&mymesg,1024,1,0); if(n == -1) { perror("msgrcv error"); return 0; } else { printf("recv %d bytes ->%s",n,mymesg.mtext); } return 0; }
运行结果
四、Linux下IPC的shell命令
ipcs -a :查看系统中所有ipc(共享内存、消息队列、信号量)
ipcs -q :只查看消息队列
ipcs -m :只查看共享内存
ipcs -s :只查看信号量
ipcrm :移除指定ipc
ipcrm msg <msqid> :移除ID为msqid的消息队列
ipcrm shm <shmid> :移除ID为shmid的共享内存
ipcrm sem <semid> :移除ID为semid的信号量