说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
《Linux系统编程》、《Linux网络编程》
消息队列是内核地址空间中的内部链表,通过Linux内核在各个进程之间传递内容。
消息顺序地发送到消息队列中,并以几种不同的方式从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识。内核中的消息队列是通过IPC的标识符来区别的,不同的消息队列之间是相对独立的。每个消息队列中的消息,又构成一个独立的链表。
常用结构msgbuf位于:linux/msg.h头文件中。
struct msgbuf {
__kernel_long_t mtype; // 消息类型,以正数来表示
char mtext[1]; // 详细数据
};
PS:
①mtype:用户可给某消息设定一个类型,可在消息队列中正确地发送和接收自己的消息。
②mtext用户构建自己消息结构时,并不一定为char或长度为1可依据实际需求构建。
③除以上变量外还可添加其他变量,但消息总大小不能超过8192字节,这是由Linux/msg.h中定义的MSGMAX决定。
#define MSGMAX 8192(二)msqid_ds 结构
位于:linux/msg.h头文件中。
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first;
struct msg *msg_last;
__kernel_time_t msg_stime;
__kernel_time_t msg_rtime;
__kernel_time_t msg_ctime;
unsigned long msg_lcbytes;
unsigned long msg_lqbytes;
unsigned short msg_cbytes;
unsigned short msg_qnum;
unsigned short msg_qbytes;
__kernel_ipc_pid_t msg_lspid;
__kernel_ipc_pid_t msg_lrpid;
};
(三)msqid_ds 结构
位于:linux/ipc.h头文件中。
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
二、内核中的消息队列关系
在消息的发送和接收的时候,内核通过一个比较巧妙的设置来实现消息插入队列的动作和从消息中查找消息的算法。
结构list_head形成一个链表,而结构msg_msg之中的m_list成员是一个struct list_head类型的变量,通过此变量,消息形成了一个链表,在查找和插入时,对m_list域进行偏移操作就可以找到对应的消息体位置。内核中的代码在头文件
1.函数说明:将路径名和项目的表示符转变为一个系统V的IPC键值
| 项目 | 说明 |
|---|---|
| 函数原型 | extern key_t ftok (const char *__pathname, int __proj_id) __THROW; |
| 头文件 | sys/types.h、sys/ipc.h |
| 参数说明 | __pathname:已存在的目录 |
| __proj_id:8位值 (通常用a,b等表示) | |
| 返回值 | 若成功则返回键值 若失败则返回-1 |
| 注意 |
1.函数说明:创建一个新的消息队列或访问一个现有的队列。
| 项目 | 说明 |
|---|---|
| 函数原型 | extern int msgget (key_t __key, int __msgflg) __THROW; |
| 头文件 | sys/types.h、sys/ipc.h、sys/msg.h |
| 参数说明 | __key:键值(可用flok函数生成) 拿来与内核其他消息队列关键字相比较,打开或访问操作依赖于msgflg参数内容 |
| __msgflg:参数说明如下: | |
| 返回值 | 若成功则返回消息队列ID 若失败则返回-1 |
| 注意 |
2.__msgflg参数:
①IPC_CREAT:如果在内核中不存在该队列,则创建它。
②IPC_EXCL:当与IPC_CREAT一起使用时,如果队列早已存在则将出错。
如果只使用了IPC_CREAT,msgget()函数或者返回新创建消息队列的消息队列标识符,或者会返回现有的具有同一个关键字值的队列的标识符。
如果同时使用了IPC_EXCL和IPC_CREAT,那么将可能会有两个结果:
或者创建一个新的队列;
或者如果该队列存在,则调用将出错,并返回一1。
IPC_EXCL本身是没有什么用处的,但在与IPC_CREAT组合使用时,它可以用于保证没有一个现存的队列为了访问而被打开。
1.函数说明:获取队列标识符后,用于向队列传递消息
| 项目 | 说明 |
|---|---|
| 函数原型 | extern int msgsnd (int __msqid, const void *__msgp, size_t __msgsz, int __msgflg); |
| 头文件 | sys/types.h、sys/ipc.h、sys/msg.h |
| 参数说明 | __msqid:队列标识符 (msgget()获取的返回值) |
| __msgp:指向消息缓冲区 | |
| __msgsz:消息的大小(字节单位) 其中不包括消息类型的长度(4字节) | |
| __msgflg:标记变量 0:忽略 IPC_NOWAIT:若队列满则不会写入 未指定IPC_NOWAIT则调用进程将被中断(阻塞),直到写消息为止 | |
| 返回值 | 若失败则返回-1 |
| 注意 |
1.函数说明:获取队列标识符后,用于向队列传递消息
| 项目 | 说明 |
|---|---|
| 函数原型 | extern ssize_t msgrcv (int __msqid, void *__msgp, size_t __msgsz, long int __msgtyp, int __msgflg); |
| 头文件 | sys/types.h、sys/ipc.h、sys/msg.h |
| 参数说明 | __msqid:队列标识符 (msgget()获取的返回值) |
| __msgp:指向消息缓冲区 存放获取的消息 | |
| __msgsz:消息缓冲区的大小(字节单位) 其中不包括mtype成员的长度(4字节) | |
| __msgtyp:指定队列中获取的消息类型 | |
| __msgflg:标记变量 0:忽略 IPC_NOWAIT:若队列满则不会写入 未指定IPC_NOWAIT则调用进程将被中断(阻塞),直到写消息为止 | |
| 返回值 | 若失败则返回-1 |
| 注意 |
1.函数说明:在一个消息队列上执行控制操作
| 项目 | 说明 |
|---|---|
| 函数原型 | extern int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf) __THROW; |
| 头文件 | sys/types.h、sys/ipc.h、sys/msg.h |
| 参数说明 | __msqid:队列标识符 (msgget()获取的返回值) |
| __cmd:命令 | |
| __buf:应用层和内核空间进行数据交换的指针 | |
| 返回值 | 若失败则返回-1 |
| 注意 |
2.__cmd值:
①IPC_STAT: 获取 队列的msqid_ds结构,并把它存放在buf变量所指定的地址中,通过这种方式,应用层可以获得当前消息队列的设置情况,例如是否有消息到来、消息队列的缓冲区设置等。
②IPC_SET: 设置 队列的msqid_ds结构的ipc _perm成员值,它是从 buf中取得该值的。通过IPC_SET 命令,应用层可以设置消息队列的状态,例如修改消息队列的权限,使其他用户可以访问或者不能访问当前的队列;甚至可以设置消息队列的某些当前值来伪装。
③IPC_RMID:内核 删除 队列。使用此命令执行后,内核会把此消息队列从系统中删除。
主函数先用函数ftok()使用路径“/ipc/msg/b”获得一个键值,之后进行相关的操作并打印消息的属性。
调用函数msgget()获得一个消息后,打印消息的属性;
调用函数msgsnd()发送一个消息后,打印消息的属性;
调用函数msgrcv()接收一个消息后,打印消息的属性;
调用函数msgctl()并发送命令IPC_SET设置一个消息后,打印消息的属性;
调用函数msgctl()并发送命令IPC_RMID销毁消息队列。
#include3.运行步骤:(未实践成功)#include #include #include #include #include #include #include void msg_show_attr(int msg_id, struct msqid_ds msg_info) { int ret =-1; sleep(1); ret = msgctl(msg_id, IPC_STAT, &msg_info); //获取消息 if(ret == -1) //获取消息失败 { printf("获得消息信息失败n"); return; } printf("n"); printf("现在队列中的字节数:%ldn", msg_info.msg_cbytes); //消息队列中的字节数 printf("队列中消息数:%ldn",msg_info.msg_qnum); //消息队列中的消息数 printf("队列中最大字节数:%ldn",msg_info.msg_qbytes); //消息队列中的最大字节数 printf("最后发送消息的进程pid:%dn",msg_info.msg_lspid); //最后发送消息的进程 printf("最后接收消息的进程pid:%dn",msg_info.msg_lrpid); //最后接收消息的进程 printf("最后发送消息的时间:%s",ctime(&(msg_info.msg_stime))); //最后发送消息的时间 printf("最后接收消息的时间:%s",ctime(&(msg_info.msg_rtime))); //最后接收消息的时间 printf("最后变化时间:%s",ctime(&(msg_info.msg_ctime))); //消息的最后变化时间 printf("消息UID是:%dn",msg_info.msg_perm.uid); //消息的UID printf("消息GID是:%dn",msg_info.msg_perm.gid); //消息的GID } int main (int argc,char *argv[]) { key_t key; //键值 int ret = -1; //返回状态 int msg_flags; //消息的标记 int msg_id; //消息ID识别号 int msg_sflags; //消息发送的标记 int msg_rflags; //消息接受的标记 char *msgpath = "/ipc/msg/"; //消息key产生所用的路径 struct msgmbuff{ long mtype; //消息类型 char mtext[10]; //消息数据 }; struct msqid_ds msg_info; struct msgmbuff msg_mbuf; //自定义构建的消息缓冲区 key = ftok(msgpath, 'a'); //产生key if(key != -1) //产生key成功 { printf("成功建立KEYn"); } else //产生key失败 { printf("建立KEY失败n"); } msg_flags = IPC_CREAT|IPC_EXCL; //消息队列的类型 msg_id = msgget(key, msg_flags|0x0666); //建立消息队列 if( msg_id == -1) { printf("消息队列建立失败n"); return 0; }else{ printf("成功建立消息队列n"); } msg_show_attr(msg_id, msg_info); //显示消息的属性 msg_sflags = IPC_NOWAIT; //消息的发送标记:直接读取消息,不等待 msg_mbuf.mtype = 10; //消息的大小为10字节 memcpy(msg_mbuf.mtext,"测试消息", sizeof("测试消息")); //复制字符串 ret = msgsnd(msg_id, &msg_mbuf,sizeof("测试消息"),msg_sflags);//发送消息 if( ret == -1) //发送失败 { printf("发送消息失败n"); } msg_show_attr(msg_id,msg_info); //显示消息属性 msg_rflags = IPC_NOWAIT|MSG_NOERROR; //消息的接收标记: ret = msgrcv(msg_id, &msg_mbuf, 10,10,msg_rflags); //接收消息 if( ret == -1) //接收失败 { printf("接收消息失败n"); } else //接收成功 { printf("接收消息成功,长度:%dn",ret); } msg_show_attr (msg_id, msg_info); //显示消息属性 msg_info.msg_perm.uid = 8; msg_info.msg_perm.gid = 8; msg_info.msg_qbytes = 12345; ret = msgctl(msg_id,IPC_SET, &msg_info); //设置消息属性 if( ret == -1 ) { printf("设置消息属性失败n"); return 0; } msg_show_attr(msg_id, msg_info); //显示消息属性 ret = msgctl(msg_id,IPC_RMID,NULL); //删除消息队列 if( ret == -1 ) { printf("删除消息失败n"); return 0; } return 0; }
①创建路径名:sudo mkdir -p /ipc/msg
构建IPC键值的路径必须是已存在的目录。
②编译:gcc mq.c -o mq
③运行:./mq



