进程通信之消息队列
1.什么是消息队列
消息队列以队列形式(消息链表)的数据结构存放在系统内核中的,它可用于客户进程与服务进程之间的双向数据流。但是它跟信号量、共享内存一样,都只在系统范围内通信,且其在系统里没有名字,其内容不会自动删除,没有文件描述符。这些缺点导致现代进程通信基本不用它了。尽管如此,但是今天我还是需要了解它。
2.消息的数据类型
首先,在头文件<sys/msg.h>中,定义了我们可用传给消息队列的数据类型:
{
long mtype;
char mtext[1];
}
mtype是指该消息的类型,区分出不同的消息,我们就可以辨别出消息的来源,比如我们把客户进程的mtype设置为1,服务进程的mtype设置为2,这样我们就可用实现客户进程和服务进程的双向通信了。注意,msgbuf只是一个模板,我们可用根据这个模板,自己定义其它的数据结构传给消息队列,如:
{
long mtype;
char name[256];
int age;
}
我们自己定义了msgbuf_custom,它可用向消息队列传送字符串的name,整形的age。
3.相关函数
(1). key_t ftok(char * fname, int id)
ftok()的作用是产生一个独一无二的ID,把这个ID提供给IPC对象来作为的key,以便其与其它IPC对象通信。fname就是你指定的文件名(已经存在的文件名),一般使用当前目录,如:
其返回值一般是取fname的索引节点号,然后加上id。
(2). int msgget(key_t key,int msgflag)
msgget用于打开或者产生一个消息队列,key即上面函数产生的key,msgget具体行为跟msgflag的取值有关:
IPC_CREAT:不存在则创建之,存在则打开之.
IPC_EXCL:该值要与IPC_CREAT(通过|)连用才有意义。与IPC_CREAT连用表示,不存在则创建之,存在则报错返回-1,错误码是EEXIST。下面是一个封装的打开消息队列的函数:
{
int mid;
if( ( mid = msgget( key , IPC_CREAT|0660) )==-1 )
{
perror("open error:");
exit(1);
}
return mid;
}
注意我们给其加上了权限0660.
(3). int msgsnd(int mid,struct msgbuf *buf,int msgsize,int msgflag);
msgsnd()用于向消息队列发送一条消息,mid即上面函数msgget()返回的结果,buf即我们要发送的消息,msgsize即消息的大小,msgflag跟具体取值有关:
0,则忽略。
IPC_NOWAIT,如果消息队列没有满,则buf就不写入了,返回,不造成阻塞。
下面我们封装一个发送消息队列的函数:
{
int rs;
int len =sizeof(struct msgbuf)-sizeof(long);
if( rs =msgsnd(mid,mbuf,len,0)==-1 )
{
perror("write error:");
exit(1);
}
return rs;
}
(4). int msgrcv(int mid,struct msgbuf *buf,int msgsize,long mtype,int msgflag)
msgrcv()用于取出mtype类型的消息到buf中,其中msgflag的取值可以是:
0,忽略。
IPC_NOWAIT:如果消息队列为空,则返回ENOMSG,不造成进程阻塞。
MSG_NOERROR:如果取得的消息大于msgsize,则只返回msgsize大小的消息。如果设置该值,将会导致消息留在队列中。
下面我们封装一个发送消息队列的函数:
{
int rs;
int len =sizeof(struct msgbuf)-sizeof(long);
if((rs=msgrcv(mid,mbuf,len,mtype,0))==-1)
{
perror("read is error");
exit(1);
}
return rs;
}
利用msgflag的特性,我们还可用检测指定类型(mtype)的的消息是否存在:
{
int rs;
if( (rs=msgrcv(mid,NULL,0,mtype,IPC_NOWAIT))==-1 )
{
if(errno==E2BIG)
{
return1;
}else{
perror("exist is error");
exit(0);
}
}
return0;
}
(5). int msgctl(int mid,int cmd ,struct msqid_ds *buf)
cmd可用取以下值:
IPC_STAT:取出系统保存的消息队列的msqid_ds数据到buf中.
IPC_SET :根据buf重新设置系统保存的消息队列的msqid_ds数据.
IPC_EMID:将该队列从系统删除。
下面是一个封装用来删除消息队列的函数:
{
msgctl(mid,IPC_RMID,0);
}
下面是一个封装用来重新设置消息队列的权限的函数:
{
struct msqid_ds mds;
msgctl(mid,IPC_STAT,&mds);
sscanf(mode,"%ho",&mds.msg_perm.mode);
msgctl(mid,IPC_SET,&mds);
}
3.一个实例
附件提供一个实例,下载后请使用:gcc -o msgtool msgtool.c func.c编译。
用法:./msgtool w 1 "codebean" //将数据“codebean”写作为1的类型入消息队列
./msgtool r 1 //取出消息队列类型为1的消息
./msgtool d //删除该消息队列