一、消息队列的概念
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法,每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的, 而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一 样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。
二、IPC对象数据结构
内核为每个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; /* Permissions */
unsigned short __seq; /* Sequence number */
};
消息队列,共享内存和信号量都有这样一个共同的数据结构。三、关于实现消息队列的函数
1.创建消息队列
2.销毁消息队列
关于结构体msgid_ds:
3.发送消息
4.接收消息
四、实现消息队列进行进程间通信
comm.c:
server.c:
client.c:
Makefile中定义变量:
运行结果:
由上图可知,在进程退出后,消息队列并没有随进程的退出而销毁,可以得出消息队列的生命周期是随内核,因此需要手动去删除它,采用 ipcs -q (消息队列mgsid)的方式去删除消息队列。
程序源码:
comm.c
#ifndef _COMM_H_
#define _COMM_H_
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
struct msgbuf
{
long mtype;
char mtext[128];
};
int comMsgQueue(int flags);
int createMsgQueue();
int getMsgQueue();
int sendMsg(int msgid, long type, const char* info);
int rcvMsg(int msgid, long type, char out[]);
int destroyMsgQueue(int msgid);
#endif
[admin@www msgqueue]$ vim comm.c
[admin@www msgqueue]$ vim comm.c
[admin@www msgqueue]$ vim server.c
[admin@www msgqueue]$ vim client.c
[admin@www msgqueue]$ vim Makefile
[admin@www msgqueue]$ cat comm.c
#include "comm.h"
int comMsgQueue(int flags)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0)
{
perror("ftok");
return -4;
}
int msgid = msgget(key, flags);
if(msgid < 0)
{
perror("msgget");
}
return msgid;
}
int createMsgQueue()
{
return comMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()
{
return comMsgQueue(IPC_CREAT);
}
int sendMsg(int msgid, long type, const char *info)
{
struct msgbuf msg;
msg.mtype = type;
strcpy(msg.mtext, info);
int ret = msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
if(ret < 0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int rcvMsg(int msgid, long type, char out[])
{
struct msgbuf msg;
int ret = msgrcv(msgid, &msg, sizeof(msg.mtext), type, 0);
if(ret < 0)
{
perror("msgrcv");
return -2;
}
strcpy(out, msg.mtext);
return 0;
}
int destroyMsgQueue(int msgid)
{
if(msgctl(msgid, IPC_RMID, NULL) < 0)
{
perror("msgctl");
return -3;
}
return 0;
}
server.c
#include "comm.h"
int main()
{
int msgid = createMsgQueue();//创建消息队列
printf("msgid is:%d\n", msgid);
char buf[128];
long type = SERVER_TYPE;
while(1) //server先收后发
{
rcvMsg(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); //发送消息
}
}
destroyMsgQuue(msgid); //销毁队列
return 0;
}
client.c
#include "comm.h"
int main()
{
int msgid = getMsgQueue(); //获取消息队列
printf("msgid is: %d\n", msgid);
char buf[128];
long type = CLIENT_TYPE;
while(1) //client先发后收
{
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); //发送消息
}
rcvMsg(msgid, SERVER_TYPE, buf); //接收消息
printf("server# %s\n", buf);
// destroyMsgQueue(msgid);
}
return 0;
}