消息队列
消息队列 提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。
- 消息队列与命名管道一样,每个消息的最大长度MSGMAX,每个消息队列的总的字节数上限MSGMNB,消息队列的总数上限MSGMNI
- 消息队列是基于消息的
- 管道是基于字节流的
- 消息队列的读取不一定是先入先出
- 消息队列是用链表实现的
- 消息的生命周期—-随内核
IPC对象数据结构
内核为每个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 */
__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 */
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 */
};
查看和删除消息队列
- ipcs -q :查看消息队列
- ipcrm -q (msqid) :删除消息队列
创建和获取消息队列
函数原型: int msgget(key_t key, int msgflg);
int commMsgQueue(int flags)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(_k < 0){
perror("ftok");
return -1;
}
int msgqid = msgget(key, flags);
if(msgqid < 0)
{
perror("msg");
return -2;
}
return msg_id;
}
int createMsgQueue() //创建消息队列
{
return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()//获取消息队列
{
return commMsgQueue(IPC_CREAT);
}
删除消息队列
函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int destoryMsgQueue(int msgid) //删除消息队列
{
if(msgctl(msgid, IPC_RMID, NULL) < 0)
{
perror("msgctl");
return -1;
}
return 0;
}
向队列中读/写消息
函数原型:(写) int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数原型:(读)ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
int sendMsg(int msgid, long type, const char *_info) //发送
{
struct msgbuf msg;
msg.mtype = type;
strcpy(msg.mtext, _info);
if(msgsnd(msgid, &msg, sizeof(msg.mtext), 0) < 0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msgid, long type, char out[]) //接收
{
struct msgbuf msg;
if(msgrcv(msgid, &msg, sizeof(msg.mtext), type, 0) <0)
{
perror("msgrcv");
return -1;
}
strcpy(out, msg.mtext);
return 0;
}
msggp:指向消息缓存区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构:
struct msgbuf{
long mtype;//大于0
char mtext[用户指定大小];
};
运用消息队列实现进程间通信
<comm.h>
#ifndef _COMM_H_
#define _COMM_H_
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SIZE 1024
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct msgbuf{
long mtype;
char mtext[SIZE];
};
int createMsgQueue(); //创建消息队列
int getMsgQueue();//获取消息队列
int sendMsg(int msgid, long type, const char *_info); //发送
int recvMsg(int msgid, long type, char out[]); //接收
int destoryMsgQueue(int);//摧毁消息队列
#endif
<comm.c>
#include"comm.h"
int commMsgQueue(int flags)
{
key_t _k = ftok(PATHNAME, PROJ_ID);
if(_k < 0){
perror("ftok");
return -1;
}
int msg_id = msgget(_k, flags);
if(msg_id < 0)
{
perror("msg");
return -2;
}
return msg_id;
}
int createMsgQueue() //创建消息队列
{
return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()//获取消息队列
{
return commMsgQueue(IPC_CREAT);
}
int sendMsg(int msgid, long type, const char *_info) //发送
{
struct msgbuf msg;
msg.mtype = type;
strcpy(msg.mtext, _info);
if(msgsnd(msgid, &msg, sizeof(msg.mtext), 0) < 0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msgid, long type, char out[]) //接收
{
struct msgbuf msg;
if(msgrcv(msgid, &msg, sizeof(msg.mtext), type, 0) <0)
{
perror("msgrcv");
return -1;
}
strcpy(out, msg.mtext);
return 0;
}
int destoryMsgQueue(int msgid) //摧毁消息队列
{
if(msgctl(msgid, IPC_RMID, NULL) < 0)
{
perror("msgctl");
return -1;
}
return 0;
}
<server.c>
//先发送后接收
#include"comm.h"
int main()
{
int msgid = createMsgQueue(); //创建消息队列
printf("msgid : %d\n",msgid);
char buf[SIZE];
while(1)
{
recvMsg(msgid, CLIENT_TYPE, buf);
printf("client# %s\n",buf);
printf("Please Enter$ ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s>0)
{
buf[s-1] = '\0';
sendMsg(msgid,SERVER_TYPE, buf);
}
}
destoryMsgQueue(msgid); //摧毁消息队列
return 0;
}
<client.c>
//先接收后发送
#include"comm.h"
int main()
{
int msgid = getMsgQueue(); //创建消息队列
printf("msgid : %d\n",msgid);
char buf[SIZE];
while(1) //send --> recv
{
printf("Please Enter$ ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s>0)
{
buf[s-1] = '\0';
sendMsg(msgid, CLIENT_TYPE, buf);
}
recvMsg(msgid, SERVER_TYPE, buf);
printf("server# %s\n",buf);
}
destoryMsgQueue(msgid); //摧毁消息队列
return 0;
}
<makefile>
client_=client
server_=server
cc=gcc
cliSrc=client.c comm.c
serSrc=server.c comm.c
.PHONY:all
all:$(client_) $(server_)
$(client_):$(cliSrc)
$(cc) -o $@ $^
$(server_):$(serSrc)
$(cc) -o $@ $^
.PHONY:clean
clean:
rm -f $(client_) $(server_)