学习环境 Centos6.5 Linux 内核 2.6
消息队列是SystemV版本中三种进程通信机制之一,另外两种是信号量和共享存储段。消息队列提供了进程间发送数据块的方法,而且每个数据块都有一个类型标识。消息队列是基于消息的,而管道是基于字节流。创建的消息队列,生命周期随内核,只有内核重启或用户主动去删除,才可以真正关闭消息队列。
// 内核为每个IPC对象维护一个数据结构(/usr/include/linux/ipc.h)
struct ipc_perm
{
key_t __key; /* key supplied to xxxget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permission */
unsigned short __seq; /* Sequeence number*/
}
//消息队列的结构 ( /usr/include/linux/msg.h)
// message queue id
// defined in
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 butes on queue */
unsigned short msg_qnum; /* number of messages in queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
}
#include
#include
key_t ftok(const char* path, int id);
#include
#include
int msgget(key_t key, int msgflag);
#include
#include
#include
int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflag);
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int msgflag);
struct msgubf
{
long mtype; // 消息类型, 必须大于零
char mtext[SIZE]; // 消息文本
}
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
client_=client
server_=server
cc=gcc
clientSrc=client.c common.c
serverSrc=server.c common.c
.PHONY:all
all:$(client_) $(server_)
$(client_):$(clientSrc)
$(cc) -o $@ $^
$(server_):$(serverSrc)
$(cc) -o $@ $^
.PHONY:clean
clean:
rm -f $(client_) $(server_)
#ifndef _COMMON_H_
#define _COMMON_H_
#include // strcpy
#include
#include
#include // read
#include
#include
#define PATHNAME "./"
#define PROJ_ID 0x666
#define MSGSIZE 1024
#define SERVER_TYPE 1 // 服务端发送消息类型
#define CLIENT_TYPE 2 // 客户端发送消息类型
struct msgbuf // 消息结构
{
long mtype; // 消息类型
char mtext[MSGSIZE]; // 消息buf
};
int createMsgQueue(); // 创建消息队列
int destroyMsgQueue( int msqid); // 销毁消息队列
int getMsgQueue(); // 获取消息队列
int sendMsg( int msqid, long type, const char *_sendInfo); // 发送消息
int recvMsg(int msqid, long type, char buf[]); // 接收消息
#endif /* _COMMON_H*/
#include "common.h"
int commMsg(int msgflag)
{
// 生成IPC 关键字
key_t _k = ftok(PATHNAME, PROJ_ID);
int msqid = msgget(_k, msgflag); // 获取消息队列ID
if(msqid < 0)
{
perror("msgget");
return -2;
}
return msqid;
}
int createMsgQueue() // 创建消息队列
{
return commMsg(IPC_CREAT|IPC_EXCL|0666);
}
int destroyMsgQueue( int msqid) // 销毁消息队列
{
int _ret = msgctl(msqid, IPC_RMID, 0);
if(_ret < 0)
{
perror("msgctl");
return -1;
}
return 0;
}
int getMsgQueue() // 获取消息队列
{
return commMsg(IPC_CREAT);
}
int sendMsg( int msqid, long type, const char *_sendInfo) // 发送消息
{
struct msgbuf msg;
msg.mtype = type;
strcpy(msg.mtext, _sendInfo);
int _snd = msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
if( _snd < 0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msqid, long type, char buf[]) // 接收消息
{
struct msgbuf msg;
int _rcv = msgrcv(msqid, &msg, sizeof(msg.mtext), type, 0);
if( _rcv < 0)
{
perror("msgrcv");
return -1;
}
strcpy(buf, msg.mtext);
return 0;
}
#include "common.h"
void client()
{
int msqid = getMsgQueue();
char buf[MSGSIZE];
while(1)
{
printf("Please enter :");
fflush(stdout);
ssize_t _s = read(0, buf, sizeof(buf)-1);
if(_s > 0)
{
buf[_s -1] = '\0';
sendMsg(msqid, CLIENT_TYPE, buf);
}
recvMsg(msqid, SERVER_TYPE, buf);
if(strcmp("exit",buf) == 0)
{
printf("服务端退出,客户端自动退出\n");
break;
}
printf("服务端说:%s\n", buf);
}
}
int main()
{
client();
return 0;
}
#include "common.h"
void server()
{
int msqid = createMsgQueue();
char buf[MSGSIZE];
while(1)
{
// 服务端先接收
recvMsg(msqid, CLIENT_TYPE, buf); printf("客户端说:%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(msqid, SERVER_TYPE, buf);
if(strcmp(buf, "exit") == 0)
break;
}
}
destroyMsgQueue(msqid);
}
int main()
{
server();
return 0;
}
通信截图示例:
注意:如果在启动server 后 强制结束掉(ctrl+c)程序,则消息队列会一直存在,这时再次执行server会执行失败,需要使用命令 ipcrm -q xxx xx表示要关闭的msqid。
总结:消息队列发送的是数据块, 生命周期随内核,依赖于系统接口实现。适用于无血缘关系多个进程之间通信。