进程通信之消息队列

1.什么是消息队列
  消息队列以队列形式(消息链表)的数据结构存放在系统内核中的,它可用于客户进程与服务进程之间的双向数据流。但是它跟信号量、共享内存一样,都只在系统范围内通信,且其在系统里没有名字,其内容不会自动删除,没有文件描述符。这些缺点导致现代进程通信基本不用它了。尽管如此,但是今天我还是需要了解它。

2.消息的数据类型

  首先,在头文件<sys/msg.h>中,定义了我们可用传给消息队列的数据类型:

struct msgbuf
{
long mtype;
char mtext[1];
}

mtype是指该消息的类型,区分出不同的消息,我们就可以辨别出消息的来源,比如我们把客户进程的mtype设置为1,服务进程的mtype设置为2,这样我们就可用实现客户进程和服务进程的双向通信了。注意,msgbuf只是一个模板,我们可用根据这个模板,自己定义其它的数据结构传给消息队列,如:

struct msgbuf_custom
{
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就是你指定的文件名(已经存在的文件名),一般使用当前目录,如:

key_t key = ftok(".", 1); //将fname设为当前目录,id是子序号

其返回值一般是取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 openMsg(key_t key)
{
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 writeMsg(int mid ,struct msgbuf *mbuf)
{
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 readMsg(int mid,long mtype,struct msgbuf *mbuf)
{
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 msg_exist(int mid,long 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:将该队列从系统删除。
    下面是一个封装用来删除消息队列的函数:

void delMsg(int mid)
{
msgctl(mid,IPC_RMID,
0);
}

下面是一个封装用来重新设置消息队列的权限的函数:

void modifyMsg(int mid,char*mode)
{
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            //删除该消息队列            

原文地址:https://www.cnblogs.com/codebean/p/2082791.html