消息队列
消息队列是由内核维护的一种链式结构。链表中每一个记录又称作消息,消息具有特定的格式和优先级别。
(1)消息队列提供了一个从一个进程向另一个进程发送一块数据的方法。
(2)每个数据块都被认为是有⼀个类型,接收者进程接收的数据块可以有不同的类型值。
(3)消息队列也有管道⼀样的不足,就是每个消息的最⼤长度是有上限的(MSGMAX),每个消息队
列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。
IPC对象数据结构 可以在/usr/include/linux/ipc.h中打开;
内核为每个IPC对象维护一个数据结构。
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 */
};
消息队列可以在/usr/include/linux/msg.h中打开:
消息队列函数:
msgget函数
功能:⽤用来创建和访问⼀一个消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数:key: 某个消息队列的名字
msgflg:由九个权限标志构成,它们的⽤用法和创建⽂文件时使⽤用的mode模式标志是⼀样的。
参数msgflg是一些标志位,常用的有IPC_CREAT、IPC_EXCL、IPC_NOWAIT三个取值
返回值:成功返回⼀个非负整数,即该消息队列的标识码;失败返回-1
msgctl函数
功能:消息队列的控制函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//成功返回0,失败返回-11
参数:
msqid:由msgget函数返回的消息队列标识码
IPC_STAT:该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。
IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中。
IPC_RMID:从内核中删除 msqid 标识的消息队列。
cmd:是将要采取的动作(有三个可取值)。
返回值:成功返回0,失败返回-1。
msgsnd函数
功能:把⼀条消息添加到消息队列中
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//成功返回0,失败返回-1
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。
返回值:成功返回0;失败返回-1
说明:1.消息结构在两⽅方⾯面受到制约:
首先,它必须⼩于系统规定的上限值;
其次,它必须以⼀个long int⻓整数开始,接收者函数将利⽤用这个⻓整数确定消息的类型
2.消息结构参考形式如下:
struct msgbuf {
long mtype;
char mtext[1];
}
msgrcv函数
功能:是从一个消息队列接收消息
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码;
msgp:是一个指针,指针指向准备接收的消息;
msgsz:是msgp指向的消息⻓度,这个长度不含保存消息类型的那个long int长整型;
msgtype:它可以实现接收优先级的简单形式;
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事;
注意:
如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1。如果执行的是msgrcv(),则在消息队列为空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列为满或为空的情形时,采取阻塞等待的处理模式。
返回值:成功返回实际放到接收缓冲区⾥里去的字符个数,失败返回-1。
实例:应用这些函数来实现client/server两进程进行通信:
首先创建Makefile文件:
.PHONY:all
all:client server
client:client.c comm.c
gcc -o $@ $^
server:server.c comm.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f client server
comm.h
#ifndef _COMM_H_
#define _COMM_H_
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <string.h>
#define PATHNAME "."
#define PROJ_ID 0x666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct msgbuf
{
long mtype;
char mtext[1024];
};
int creatMsg();//创建消息队列
int getMsg();//获得消息队列
int destoryMsg(int msgid );//销毁消息队列
int sendMsg(int msgid,int who,char* msg);//发送消息
int recvMsg(int msgid,int recvType,char out[]);//接收消息
#endif
comm.c
#include "comm.h"
static int commMsg(int flags)
{
key_t _key = ftok(PATHNAME, PROJ_ID);
if (_key<0)
{
perror("ftok");
return -1;
}
int msgid = msgget(_key, flags);
if (msgid < 0)
{
perror("msgget");
}
return msgid;
}
int creatMsg()
{
return commMsg(IPC_CREAT|IPC_EXCL|0666);
}
int getMsg()
{
return commMsg(IPC_CREAT);
}
int destoryMsg(int msgid )
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl");
return -1;
}
return 0;
}
int sendMsg(int msgid,int who,char *msg)
{
struct msgbuf buf;//用户自己定义
buf.mtype=who;
strcpy(buf.mtext,msg);
if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msgid,int recvType,char out[])
{
struct msgbuf buf;
int p=msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0);
if(p<0)
{
perror("msgrcv");
return -1;
}
strncpy(out,buf.mtext,p);
return 0;
}
server.c
#include "comm.h"
int main()
{
int msgid=creatMsg();//server创建消息队列
char buf[1024];
while(1)
{
buf[0]=0;
recvMsg(msgid,CLIENT_TYPE,buf);//读取消息队列中的消息
printf("client say :%s\n",buf);
printf("Please Enter:");
fflush(stdout);
size_t s=read(0,buf,sizeof(buf));
if(s>0)
{
buf[s-1]=0;
sendMsg(msgid,SERVER_TYPE,buf);//发送消息
printf("waiting \n ");
}
}
destoryMsg(msgid);
return 0;
}
client.c
#include "comm.h"
int main()
{
int msgid=getMsg();
char buf[1024];
while(1)
{
buf[0]=0;
printf("Please Enter:");
fflush(stdout);
size_t s = read(0,buf,sizeof(buf));
if(s>0)
{
buf[s-1]=0;
sendMsg(msgid,CLIENT_TYPE,buf);
printf("waiting \n");
}
recvMsg(msgid,SERVER_TYPE,buf);
printf("server say:%s\n",buf);
}
return 0;
}
ipcs & ipcrm指令:
ipcs :显示IPC资源
ipcrm:手动删除IPC资源
当我们要查看当前系统中的消息队列就可以加上 -q 选项:
当我们需要手动 删除消息队列时候:
ipcrm -q +msqid
ipcrm -Q +key
通信结果如图所示: