【Linux】进程间通信之消息队列

       进程间通信(IPC)主要包括了管道、系统IPC(包括了消息队列、信号及共享存储)和套接字(SOCKET)。

       IPC方法包括管道(PIPE)、消息队列(Message_Queue)、旗语、共用内存(ShareMemory)以及套接字(Socket)。

       下面主要说明一下消息队列。

1、初步认识      

       根据管道的特性,知道管道的生命周期是随进程的,进程退出,管道就结束了,并且管道传送数据时以无格式字节流的形式传送,这有时会给程序的开发带来不便。然而消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。并且消息队列是随内核的,也就是说进程已经退出了,如果不自主释放资源,消息队列是会悄无声息的存在着。所以较管道来说,消息队列的生命周期更加持久,需要手动释放资源。

       我们可以通过发送信息来避免命名管道的同步和阻塞问题。管道的读取是先进先出的,而消息队列的读取不一定是先进先出。消息队列与命名管道有一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

      

        消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。                                                 

 2、消息队列结构、函数

       目前有POSIX消息队列和系统V消息队列,系统V消息队列目前被大量使用。考虑到程序的可移植性,新开发的应用程序应尽量使用POSIX消息队列。

       系统V消息队列是随内核持续的只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构(struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中找到访问入口。消息队列是一个消息的链表,每一个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头包含了该消息队列的大量信息,包括消息队列键值、用户ID、组ID、消息队列中消息数目等等,设置记录了最近对消息队列读写进程的ID。读者可以访问这些信息,也可以设置其中的某些信息。

1)IPC对象数据结构内核为每个IPC对象维护一个数据结构(vim /usr/include/linux/ipc.h)


消息队列,共享内存和信号量都有这样一个共同的数据结构。

       在上图中,可以看到许多id信息,uid为拥有者的id;gid为组用户的id;cuid和cgid中的c为创建者;mode为模式,即权限;seq为顺序值。其中key为目标获取(get)时的标志,类似于端口号。

2)消息队列结构(vim /usr/include/linux/msg.h)

其中结构体第一个就是IPC对象数据结构内核为每个IPC对象维护的那个数据结构

3)消息队列函数

头文件:#include<sys/types.h>; #include<sys/ipc.h>; #include<sys/msg.h>

3.1)创建新消息队列或取得已存在消息队列(msgget)

原型:int msgget(key_t key, int msgflg);    

参数:

       key:可以认为是一个端口号,也可以由函数ftok生成。

       msgflg:

           IPC_CREAT:如果IPC不存在,则创建一个IPC资源,否则打开操作。

           IPC_EXCL:只有在共享内存不存在的时候,新的共享内存建立,存在就产生错误。

a.  如果单独使用IPC_CREAT,XXXget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标志符。

b.  如果将IPC_CREAT和IPC_EXCL标志一起使用,XXXget()将返回一个新建的IPC标识符;如果该IPC资源已经存在,就返回-1。     

c.  IPC_EXCL标志本身并没有太大意义,但是和IPC_CREAT标志一起使用可用来保证所得的对象是新建的,而不是打开已有的对象。

     System V IPC使用key_t值作为它们的名字,在Redhat linux(后续验证默认都在该平台下)下key_t被定义为int类型

ftok函数
 函数ftok把一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:
      # include <sys/types.h> 
      # include <sys/ipc.h>
      key_t ftok(const char *pathname, int id);

       pathname就是你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。该函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键。当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。

       在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。如指定文件的索引节点号为65538,换算成16进制为 0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。查询文件索引节点号的方法是: ls -i

3.2)向队列读,写消息(msgrcv、msgsnd)

原型如下:msgrcv从队列中取出消息,msgsnd将数据放到消息队列中


参数:

     msqid:消息队列的标识码

     msgp:指向消息队列的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

                  struct msgstru{ 

                            long mtype;//大于0

                            char mtext[用户指定大小];

                   }

     msgsz:消息的大小

     msgtype:从消息队列中读取到的消息形态。如果值为0,则表示消息队列中的所有消息都会被读取。

     msgflg:用于指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时,若是消息队列已满,则msgflg()将不会阻塞,而会立即返回-1;如果执行的是msgrcv(),则消息队列成空时,不做等待马上返回-1,并设错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。

3.3)设置消息队列属性(msgctl)

原型:int msgctl(int msgqid, int cmd, struct msqid_ds* buf);

参数:msgctl系统调用对msgqid标识的消息队列执行的cmd操作,系统定义了3种cmd操作:IPC_STAT,IPC_SET,IPC_RMID

           IPC_STAT:该命令用于获取消息队列对应的msqid_ds数据结构,并将保存到buf指定的地址空间。

           IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中。

           IPC_RMID:从内核中删除msqid标识的消息队列。

下面用消息队列模拟客户端和服务端间的通信。

comm.h


comm.c


server.c


client.c


makefile文件如下:


其中 $@:表示目标文件       $^:所有依赖关系

运行结果如下:


      这样就完成了client端和server端的通信。

      需要注意的是,运行程序时如果先打开了client端的可执行程序,此时的第二个窗口运行server端时,可能会弹出17,FileExists的提示信息致使程序无法运行,因为此时打开client端时已创立了一个消息队列,并申请了IPC资源。所以此时需要清除它(注意切换至root权限),并重新按照先server后client的顺序执行即可。


       最后make clean删除生成的可执行程序时,千万别忘了用ipcrm指令删除产生的消息队列,因为它不具有管道那样进程退出就释放所有资源的特性,不自主手动释放,它也是会偷偷的一直存在的。

命令:

   ipcs -q ----------------------显示消息队列相关信息;

   ipcrm -q [参数]--------------参数为输入显示的key值或者msqid的值




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值