IPC主题一:消息队列

来源:互联网 发布:ps4网络 编辑:程序博客网 时间:2024/06/03 17:33


1、什么是消息队列 

      消息队列(也叫报文队列)是Unix系统V版本中3中进程间通信机制之一。另外两个是共享内存和信号量。这些IPC

机制使用共同的授权方法。只有通过系统调用将标识符传递给核心之后,进程才能存取这些资源。这种系统IPC对象

使用的控制方法和文件系统非常相似。使用对象的引用标识符作为资源表中的索引。消息队列就是一个西欧阿西的链

表。就是把消息看做一个记录,并且这个记录具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以按

照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。                        

     消息队列提供了一种从一个进程向另一个进程发送一个有类型数据块的方法。  每个数据块都被认为含有一个类

型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。

消息队列与命名管道一样,每个数据块都有一个最大长度的限制(MSGMAX),每个消息队列的总的字节数是有上

限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。消息队列是由一串消息构成的链表,每个

消息队列包含两个部分,即消息队列头、消息队列体。

     消息队列是随内核持续的并和进程相关,只有内核重启或者显示删除一个消息队列时,该消息队列才会真正被删

除。因此系统中记录消息队列的数据结构位于内核中,系统中的所有消息队列都可以在结构msg_ids中找到访问入

口。 linux采用消息队列的方式来实现消息传递。这种消息的发送方式是:发送不必等待接收方检查它所收到的消息

就可以继续工作下去,而接收方如果没有接收到消息也就不需要等待。这种通信机制相对简单,但是应用程序使用起

来就需要使用相对复杂的方式应付了。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间

来接收。

     IPC标识符:每一个C目标都有一个唯一的C标识符。这里所指的C目标是指一个单独的消息队列、一个

信号量集或者一个共享的内存段。系统内核使用此标识符在系统内核中指明 C目标。

     IPC 关键字:想要获得唯一的标识符,则必须使用一个 C关键字。客户端进程和服务器端进程必须双方都同意

此关键字。这是建立一个客户机/服务器框架的第一步。在System IPC机制中,建立两端联系的路由方法是和C

关键字直接相关的。通过在应用程序中设置关键字值,每一次使用的关键字都可以是相同的。一般情况下,可以使

ftok )函数为客户端和服务器端产生关键字值。

2、相关函数

(1) 获得key值

  key_t ftok(char *pathname, int projid)
  #include <sys/types.h>
  #include <sys/ipc.h>
  参数:
  pathname:文件名(含路径),通常设置为当前目录“.”
  projid:项目ID,必须为非0整数(0-255).
(2) 创建消息队列
  原型:int msgget(key_t key, int msgflag);
    头文件: #include <sys/types.h>
                  #include <sys/ipc.h>
                  #include <sys/msg.h>
  功能:用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。

      参数:
  key:可以认为是一个端口号,也可以有函数ftok生成。
  msgflag:
  IPC_CREAT:如果IPC不存在,则创建一个新的IPC资源,否则代开操作。
  IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。如果单独使用IPC_CREAT,       XXXget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符;如果与         IPC_CREAT一同使用,返回一个新建的IPC标识符,如果要创建的消息队列已经存在,则返回错误。
  IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
  返回值:
  调用成功返回队列标识符,否则返回-1.
  在以下两种情况下,将创建一个新的消息队列:
  1)如果没有与键值key相对应的消息队列,并且msgflag中包含了IPC_CREAT标志位。
  2)key参数为IPC_PRIVATE。
(3)消息队列属性控制
  原型:int msgctl(int msqid,  int cmd,  struct msqid_ds *buf)

  功能:对消息队列进行各种控制操作,操作的动作由cmd控制。

  参数:
  msqid:消息队列ID,消息队列标识符,该值为msgget创建消息队列的返回值。
  3种
cmd操作:

  IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中.
  IPC_SET:该命令用来设置消息队列的属性,将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应          值.
  IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构.
  buf:消息队列缓冲区

(4)发送、获取信息到消息队列

       msgrcv 原型:ssize_t msgrcv(int msqid,  struct msgbuf *msgp,  size_t msgsz,  long msgtype,  int                               msgflag)

  功能:从队列中接收消息
  msgsnd原型:int msgsnd(int msqid,  struct msgbuf *msgp,  size_t msgsz,  int msgflag)

       功能:将新消息添加到队列尾端,即向消息队列中发送一条消息。
  两个函数的头文件:

       #include <sys/types.h>

     #include <sys/ipc.h>
   #include <sys/msg.h>
  
  参数:
  msqid:消息队列的识别码(已打开的消息队列id)
  msgp:指向消息缓冲区的指针,此位置用来暂时存放发送和接收的消息,是一个用户可定义的通用结构。形态如       下:
  struct msgstru
  {
  long mtype;//消息类型,大于0
  char mtext[,用户指定大小];//消息正文,消息数据的首地址
  }
  msgsz:消息的大小。
    msgflag:函数的控制属性。用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflag和常数           IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而立即返回-1,如果执       行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,             msgsnd()及msgrcv()在队列呈空的情形时,采取阻塞等待的处理模式。

(5)ftok函数

     原型:  key_t ftok( const char * pathname, int proj_id );

     功能:函数ftok把一个已存在的路径名和一个整数标识的转换成一个key_t值,称为IPC键;该函数把从pathname

     导出的信息与id的低8位组合成一个整数IPC键。
     
     头文件:#include<sys/types.h>
                  #include<sys/ipc.h>

      ftok函数是根据pathname和id来创建一个关键字(类型为 key_t),此关键字在创建信号量,创建消息队列的时候

      都需要使用。其中pathname必须是一个存在的可访问的路径或文件,id必须不得为0。失败返回值为-1



3、
ipcs 命令

    命令ipcs用于读取System IPC目标的状态。

        ipcs -q: 只显示消息队列。

         ipcrm -q  megid:删除消息队列。

         ipcs -s: 只显示信号量。

         ipcs -m: 只显示共享内存。

         ipcs –help: 其他的参数。


4、实现双向通信的代码:

 server.c


client.c



测试之前:



测试之后:



5、总结:消息队列的特点
1)消息队列随内核,消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
2)消息队列允许一个或多个进程向它写入与读取消息.
3)管道和命名管道都是通信数据都是先进先出的原则。
4)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有      优势。
   目前主要有两种类型的消息队列:POSIX消息队列以及system V消息队列,系统V消息队列目前被大量使用。系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。

6、消息队列与命名管道的比较
 
    消息队列跟命名管道有不少的相同之处,通过与命名管道一样,消息队列进行通信的进程可以是不相关的进程,同

时它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用write,接收数据用read,则在消息队

列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。

     与命名管道相比,消息队列的优势在于:

1)消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。

2)同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。

3)接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地

接收。



原创粉丝点击