消息队列:
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先⼊入先出。消息队列与命名管道有一样的不足,就是每个消息的最⼤大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有⼀一个上限(MSGMNI)
ipc对象数据结构:
内核为每个IPC对象维护一个数据结构(/user/include/linux/ipc.h)
- struct kern_ipc_perm{ //内核中记录消息队列的全局数据结构msg_ids能够访问到该结构;
- key_t key; //该键值则唯一对应一个消息队列
- uid_t uid;
- gid_t gid;
- uid_t cuid;
- gid_t cgid;
- mode_t mode;
- unsigned long seq;
- }
消息队列结构:
可以看到第一个条目就是IPC的结构体,即是共有的,后面的都是消息队列所私有的成员。
消息队列使用链表实现的。
所使用函数:
函数: key_t ftok(const char *filename, int proj_id);
通过文件名和项目号获得System V IPC键值(用于创建消息队列、共享内存所用)proj_id:项目号,不为0即可
返回:成功则返回键值,失败则返回-1
函数: int msgget(key_t key, int msgflg);
key:键值,当为IPC_PRIVATE时新建一块共享内存;
shmflg:标志。
IPC_CREAT:内存不存在则新建,否则打开;
IPC_EXCL:只有在内存不存在时才创建,否则出错。
返回:成功则返回标识符,出错返回-1
函数: int msgsnd(int msgid, const void *msgp, size_t sz, int flg);
向消息队列发送消息
msgid:通过msgget获取
msgp:指向消息内容的指针
sz:消息内容的大小
flg:处理方式;如为IPC_NOWAIT时表示空间不足时不会阻塞
返回:成功则返回0,失败返回-1
函数: int msgrcv(int msgid, void *msgp, size_t sz, long type, int flg);
从消息队列读取消息
msgid:通过msgget获取
msgp:指向消息内容的指针
sz:消息内容的大小
type:指定接收的消息类型;若为0则队列第一条消息将被读取,而不管类型;若大于0则队列中同类型的消息将被读取,如在flg中设了MSG_RXCEPT位将读取指定类型的其他消息;若小于0读取绝对值小于type的消息。
flg:处理方式;
返回:成功返回收到消息长度,错误返回-1
函数: int msgctl(int msgid, int cmd, struct msgid_ds *buf);
msgid:通过msgget获取
cmd:控制命令,如下:
IPC_STAT:获取消息队列状态
IPC_SET:改变消息队列状态
IPC_RMID:删除消息队列
buf:结构体指针,用于存放消息队列状态
返回:成功返回与cmd相关的正数,错误返回-1
具体的实现过程如下:
comm.h
#ifndef _COMM_H_
#define _COMM_H_
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<string.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#define SIZE 128
struct msgbuf{
long mtype;
char mtext[SIZE];
};
int creatMsgQueue();
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 creatMsgQueue()
{
return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()
{
return commMsgQueue(IPC_CREAT);
}
int destoryMsgQueue(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("fail\n");
return -1;
}
}
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("msgrecv");
return -1;
}
strcpy(out,msg.mtext);
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);
}
return 0;
}
server.c
#include"comm.h"
int main()
{
int msgid=creatMsgQueue();
printf("msgid: %d\n",msgid);
char buf[SIZE];
while(1)
{
recvMsg(msgid,CLIENT_TYPE,buf);
printf("client# %s\n",buf);
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1]='\0';
sendMsg(msgid,SERVER_TYPE,buf);
}
}
deseoryMsgQueue(msgid);
return 0;
}
运行结果:
注意:消息队列存在后不可再次被创建
ipcs -q可查看存在的消息队列
ipcrm -q +msgid 可删除该消息队列