消息队列(System V)
作用
从一个进程向另外一个进程发送一个带有类型的数据块
本质
是存储在内核中的一个消息的队列(链表)
特点
- 每个数据块都被认为有一个类型,接受者进程接收的数据块可以有不同的类型值
- 和管道一样,每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总字节数也是有上限的(MSGMNB),系统上的消息队列总数也是有上限的(MSGMNI)
- 是一个全双工通信,可读可写。
- 生命周期随内核
查看消息队列:ipcs -q
IPC对象数据结构:/usr/include/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;
};
消息队列结构:/usr/include/linux/msg.h
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
};
消息队列的系统限制:
#define MSGMNI 16 /* <= IPCMNI */ /* max # of msg queue identifiers */
#define MSGMAX 8192 /* <= INT_MAX */ /* max size of message (bytes) */
#define MSGMNB 16384 /* <= INT_MAX */ /* default max size of a message queue */
消息队列结构:
消息结构:
struct msgbuf {
long mtype; /* type of message */
char mtext[1]; /* message text */
};
使用
- 创建消息队列 msgget
- 发送数据/接收数据 msgsnd/msgrcv
- 释放消息队列 msgctl
msgget
创建或访问一个消息队列
key:内核中消息队列的标识
msgflg:
IPC_CREAT 不存在则创建,存在则打开
IPC_EXCL 与IPC_CREAT同时使用,存在则报错
mode 权限
msgrcv
从消息队列接收消息
成功后内核更新与该消息队列相关联的 msgid_ds 结构,刷新调用进程ID(msg_lrpid)、调用时间(msg_rtime)、并将消息数减1(msg_qnum)
msqid:由msgget返回的消息队列ID
msgp:指向准备接收的消息结构体msgbuf,需要自己定义
msgsz:msgp指向的要接收的数据长度,不包含msgtype
msgtype:指定接收的数据类型
- type == 0 返回队列中第一个消息
- type > 0 返回队列中消息类型为 type 的第一个消息
- type < 0 返回队列中消息类型值 <= type绝对值的信息,优先取值最小的
msgflg:标志选项
- 0 阻塞等待
- IPC_NOWAIT 使操作不阻塞,没有消息返回-1,error设置为ENOMSG
msgsnd
msgctl
类似于 ioctl 垃圾桶函数
cmd
- IPC_STAT 取队列的 msqid_ds 放入buf指向的结构体中
- IPC_SET 将 msg_perm.uid、msg_perm.mode、msg_qbytes信息从buf结构体复制到这个队列的 msqid_ds 结构中
- IPC_RMID 从系统中删除该消息队列的说有数据
ftok
通过文件的inode节点号和一个proj_id计算出一个key值
缺点:如果文件被删除或替换生,通过key值打开的不是同一个msgqueue
用消息队列实现一个简单的进程间 client server通信
client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#define IPC_KEY 0x12345678 //消息队列key
#define TYPE_SER 1 //数据块类型
#define TYPE_CLI 2 //数据块类型
struct msgbuf {
long mtype;
char mtext[1024];
};
int main()
{
int msgid = -1;
msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
if (msgid < 0) {
perror("msgget error");
return -1;
}
while (1) {
//接收数据
//struct msgbuf 自己定义
struct msgbuf buf;
//msgid :操作句柄
msgrcv(msgid, &buf, 1024, TYPE_CLI, 0);
printf("client say:[%s]\n", buf.mtext);
//发送数据
memset(&buf, 0x00, sizeof(struct msgbuf));
buf.mtype = TYPE_SER;
scanf("%s", buf.mtext);
msgsnd(msgid, &buf, 1024, 0);
}
//删除IPC
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#define IPC_KEY 0x12345678 //消息队列key
#define TYPE_SER 1 //数据块类型
#define TYPE_CLI 2 //数据块类型
struct msgbuf {
long mtype;
char mtext[1024];
};
int main()
{
int msgid = -1;
msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
if (msgid < 0) {
perror("msgget error");
return -1;
}
while (1) {
struct msgbuf buf;
//发送数据
memset(&buf, 0x00, sizeof(struct msgbuf));
buf.mtype = TYPE_CLI;
scanf("%s", buf.mtext);
msgsnd(msgid, &buf, 1024, 0);
//接收数据
msgrcv(msgid, &buf, 1024, TYPE_SER, 0);
printf("server say:[%s]\n", buf.mtext);
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}