Linux 进程通信(System V) 第三节 ------> msg queue 消息队列

一.简介

 
    目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。考虑到程序的可移植性,新开发的应用程序应尽量使用POSIX消息队列。

      消息队列是内核创建的一个数据结构,是有标识的!
      对于有读写权限的进程来说哦,所以可以通过对共享的消息进程的读写实现不同进程之间的通信!
      关于消息队列的数据结构:
     
      #include<sys/msg.h>
     
      structmsqid_ds                                                //!> msg queue id dscription ( 消息队列状态描述符 )
      {
            structipc_perm            msg_perm;            //!> 读写perms
            structmsg                msg_first;            //!> 队列中的第一个msg, 对于内存的存储而言的,对用户使用没有意义
            structmsg                msg_last;        //!> 队列中的最后一个msg ,对于内存的存储而言的,对用户使用没有意义
            msglen_t                          msg_cbytes;        //!> 当前的队列中的bytes
            msgunum_t                  msg_qnum;            //!> 当前的队列中的message
            msglen_t                        msg_qbytes;            //!> 队列中最大允许的bytes
            pid_t                              msg_lspid;            //!> 最后一个发送msg的pid
            pid_t                              msg_lrpid;            //!> 最后一个接收msg的pid
            time_t                              msg_stime;            //!> 最后一个msg发送时间
            time_t                              msg_rtime;            //!> 最后一个msg收到时间
            time_t                              msg_ctime;            //!> 最后一个msg控制时间( time of last magctl())     
      };
     
     
      消息队列的创建:函数---> msgget():可以创建或者访问一个已经存在的queue
     
      #include<sys/types.h>
      #include<sys/ipc.h>
      #include<sys/msg.h>
     
      int msgget(key_t key, int flag);                  //!> 返回值就是操作句柄
                                                                              //!> key 可以是ftok()函数的返回值或者IPC_PRIVATE
                                                                              //!> flag也是读写权限(一般是:IPC_CREAT或者IPC_CREAT |IPC_EXCL)
     
注意创建的一个队列后的msqid_ds的初始化参数是:
                                                                                                msg_perm的uid和cuid是当前进程的有效用户id;gid和cgid为当前进程的有效组id
                                                                                                msg_ctime是当前时间
                                                                                                msg_qbytes:为系统限制值
                                                                                                其余的值都是0                                   
                                                                                                     
                                                                                                                       
关于队列的操作:
                        >>>>>
                                    int msgsnd(int msqid, const void * ptr, size_t mbytes, int flag );
                                    //!> 参数:msqid:msgget()的返回值;ptr:是一个结构体的指针structmsgbuf { long mtype; char mtext[1] };
                                    //!>                flag:可以指定为:IPC_NOWAIT,。。。

                        >>>>>           
                                    int msgrcv(int msqid, void * ptr, size_t nbytes, long type, int flag );
                                    //!> 参数:ptr;接收到的消息的储存的位置
                                    //!>  type:希望从队列获取什么样内容的消息=0:返回第一个消息;>0 : 返回类型为type的第一个消息;<0:返回类型值小于或等于type参数的绝对值的消息中类型值中最小的第一个消息
                                   
                                   
                        >>>>>
                                    int msgctl(int msqid, int cmd, struct msqid_ds * buff );
                                    可以完成的操作:
                                    IPC_RMID:
                                                              删除指定队列的所有数据,只能由两种进程执行,第一个:
                                                                                用户有效ID为  msg_perm.cuid或者msg_perm.uid
                                                              第二个:超级用户权限进程           
                                      IPC_SET:
                                                              按照buff值设置msg_perm.uid, msg_perm.gid, msg_perm.mode,
                                          msg_qbytes四个字段,执行进程同上面
                                      IPC_STAT:
                                                              取队列中msqid_ds结构值放到buff中     
                                                                                   
                                                 
二.
ftok()函数简介:

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * name, int id )
fname就时你指定的文件名,id是子序号。
在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

查询文件索引节点号的方法是: ls -i
当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。
如果要确保key_t值不变,要目确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值。

同一段程序,用于保证两个不同用户下的两组相同程序获得互不干扰的IPC键值。
由于etc/config.ini(假定)为应用系统的关键配置文件,因此不存在被轻易删除的问题——即使被删,也会很快被发现并重建(此时应用系统也将被重起)。
ftok()的设计目的也在于此.

三.




#include <stdlib.h>
#include<string.h>   
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>

#define  MSG_FILE              "server.c"                                                                              //!> 仅仅是创建的path而已
#define  BUFFER              255
#define  PERM                      S_IRUSR | S_IWUSR

typedef struct dataType
{
      long      mID;
      char        buffer[BUFFER + 1];
}dataType;

struct msgtype
{
      long              mtype;
      dataType        mdata;
};

int main()
{
      structmsgtype      msg;
      key_t                              key;                                                                                              //!> 每个queue都有一个自己的KEY作为标志
      int                                      msgid;                                                                                              //!>
   
      if( ( key =ftok( MSG_FILE, 'a' ) ) == -1)                                                      //!> when fail
                                                                                                                                                  //!>注意'a'相当于是一个标志码而已   
              fprintf( stderr, "Create Key error...: %s \n", strerror( errno ));
              exit( EXIT_FAILURE );
      }
   
      if( ( msgid= msgget( key, PERM | IPC_CREAT | IPC_EXCL ) ) == -1)//!> 创建 msg  queue
      {
              fprintf( stderr, "Create Msg error...: %s \n", strerror( errno ));
              exit( EXIT_FAILURE );
      }
   
      printf("\nMsg id = %d \n", msgid);

                                                                                                                                                                      //!>注意此处的flag都是简单处理为0   
      while( 1)                                                                                                                                  //!> 接收
      {
              msgrcv( msgid, &msg, sizeof( struct msgtype ), 1, 0);                           
              //!> 我们可以知道type=1,所以可以知道在client中的msg标志就是1
              //!> ( 必须的,我们等一下可以使用进程ID的处理 )
              fprintf( stderr, "Server receive: %s \n", msg.mdata.buffer);                      //!>
              msg.mtype = msg.mdata.mID;
              //!> 注意此处的ID必须要换成data中客户端进程的ID( 不然客户端无法识别!!!!!!!)
              sprintf( msg.mdata.buffer, " %d  客户端接收的回馈!",(int)msg.mtype );   
                                                                                                                                                                                                                //!> 注意server的回馈msg已经改变
              msgsnd( msgid, &msg, sizeof( struct msgtype ), 0);                              //!> 回发送给client
      }
   
      exit( 0);   
}



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <stdio.h>

#define  MSG_FILE              "server.c"
#define  BUFFER              255
#define  PERM                      S_IRUSR | S_IWUSR

typedef struct dataType
{
      long      mID;
      char        buffer[BUFFER + 1];
}dataType;

struct msgtype
{
      long              mtype;
      dataType        mdata;
};

int main( int argc, char ** argv)                                                           
{
      structmsgtype        msg;
      key_t                              key;
      int                                        msgid;
   
      if( argc !=2)                                                                                                                              //!> 需要输入一个字符串作为参数
      {
              fprintf( stderr, "Usage: %s string .\n", argv[0] );
              exit( EXIT_FAILURE );
      }
   
      if( ( key =ftok( MSG_FILE, 'a' ) ) == -1)                                                              //!> 获得这个Key
      {
              fprintf( stderr, "Create Key error...: %s \n", strerror( errno ));
              exit( EXIT_FAILURE );
      }
   
      if( ( msgid= msgget( key, PERM ) ) == -1)                                                      //!>获得queue的ID   
      {
              fprintf( stderr, "Create Msg error...:%s \n", strerror( errno ));
              exit( EXIT_FAILURE );
      }
   
      msg.mtype =1;                                                                                                                                              //!>注意此处的type需要与server中匹配   
      msg.mdata.mID =getpid();                                                                                                          //!> my ID
      strncpy(msg.mdata.buffer, argv[1], BUFFER);                                              //!> 获得命令行参数而已
   
      msgsnd(msgid, &msg, sizeof( struct msgtype ), 0);                              //!> 发送给server
      memset(&msg, '\0', sizeof( struct msgtype ));                                           
      msgrcv(msgid, &msg, sizeof( struct msgtype ), getpid(), 0);           
    //!>获得server的反馈( 注意type是自己的ID,所以就是server的发送msg中带的 )
      fprintf(stderr, "Client receive: %s \n", msg.mdata.buffer);            //!> 注意只接受自己ID的msg!!!
   
      exit(EXIT_SUCCESS );
}

2.运行
./s&                                              //!> server 后台
./cILOVEYOU              //!> 参数是字符串

if是多客户操作:for i in 1 2 3 4 5; do ./c sss &done                              //!> 注意 sss 是参数而已

3.注意:
<1>:
                client对于server的发送的标志是一样的才是可以的!
                因为server不可以识别多个未知的client,但是对于server的反馈而言可以根据client的ID
                也就是client的接受可以根据自己的进程ID来进行不同的接受( 在网络环境下可以根据Socket套接字接受) 

<2>:
          注意对于msg queue 而言:必须有一个这样的数据结构:
struct msgtype
{
      long              mtype;
      dataType        mdata;                                      //!> 注意此处的不一定只是char*,可以是自己定义的
};                                                                              //!> 但是整体的格式必须的,主要是long这个标志是必需的!!!

typedef struct dataType
{
      long      mID;
      char        buffer[BUFFER + 1];
}dataType;

注意:对于cs模式而言,server和client必须是打开同一个queue才是OK的,所以需要有相同的操作就是:
              if( ( key = ftok( MSG_FILE, 'a' ) ) == -1)      //!> 获得相同的Key
              ...
              获得了相同的KEY后,对于msgget的操作(也就是打开文件,自己随便...)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值