linux消息队列

消息队列是linux中进程间通信的方法之一。消息队列是一个先进先出的队列,对于同一个类型的队列,消息是遵循先进先出的原则的。
消息队列的实现很简单,只有四个函数就解决了,下面介绍下如何实现消息队列。
1 消息队列的创建
  函数原型如下:
  int msgget (key_t __key, int __msgflg);
  第一个参数是一个key,实际上就是一个长整型,通过这个Key唯一标识一个消息队列。这个值可以是我们自己定义一个唯一的整数。也可以通过 ftok来获取一个key。
  第二个参数为标识符,IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。  
                      IPC_EXCL值,与IPC_CREAT一起使用。(用”|”连接),如果消息队列不存在则创建之,否则产生一个错误并返回。
  另外还要注意队列的读写权限,例如要支持读写需要这样写IPC_CREAT|0666.
  该函数成功返回消息队列的id,失败返回-1。
  该函数返回的id唯一标识一个消息队列。msgsnd 、msgrcv、msgctl都通过这个id来操作消息队列。
  
  ftok的函数原型为
  key_t ftok( const char * fname, int id );
  fname 指定的已经存在的文件名或者目录名(必须确保已经存在的文件名或者目录。且不会被删除)。该文件或者目录不存在将返回-1。
  id 可以是自己指定的一个整数。
  该函数成功返回产生的key_t的值,失败但会-1;




  该函数,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。
  如果文件被删除再重新创建,那么索引节点号就变了。用ftok创建的key就改变了,那么就会造成进程不能访问同一个队列了。所以要确保该文件不会被删除。
  下面是示例代码
  int msgid; 
  key_t key =  ftok(".", 20 );
  msgid=msgget(key ,IPC_CREAT|0666);  
  if(msgid<0)
  {
          printf("create msg error");
  }




2 发送消息:
  函数原型如下:
  int msgsnd (int __msqid, __const void *__msgp, size_t __msgsz, int __msgflg);
  __msqid 通过msgget 返回的消息id
  __msgp  指向消息缓冲区的指针。linux给出了一个消息的参考结构:
  struct msgbuf {
         long mtype; /* 消息类型,必须 > 0 */
         char mtext[1]; /* 消息文本 */
  };
  对于消息的结构,我们可以不用上面的结构自己定义一个结构。但是第一个参数必须是消息的类型,必须要和参考的结构一样,为一个long型。而且值必须大于等于1 。
  mtype后面的成员我们可以任意指定。例如
  struct mymsg {
         long mtype; /* 消息类型,必须 > 0 */
         char mtext[100]; /* 消息文本 */
  };
  struct mymsg {
         long mtype; /* 消息类型,必须 > 0 */
         int item1:
         char item2;
         char itme3[10];
         ....
  };
  __msgsz: 发送消息的尺寸,也就是__msgp 指向的数据的尺寸。
  __msgflg:为IPC_NOWAIT表示不阻塞,也就是发送的时候如果消息队列已满,将直接返回-1。为0,如果消息队列满将一直阻塞,直到函数可以向队列写数据为止。
  该函数成功返回0 失败返回-1。




3 接收消息
  函数原型如下:
  int msgrcv (int __msqid, void *__msgp, size_t __msgsz, long int __msgtyp, int __msgflg);
  __msqid :通过msgget 返回的消息id
  __msgp  :指向接收消息缓冲区的指针。
  __msgsz:消息缓冲区的尺寸,也就是__msgp 指向的缓冲区的大小。
  __msgtyp :当__msgtyp 为0 时返回整个消息队列中的第一个消息。
             当__msgtyp 大于0时返回消息队列中第一个消息类型等于__msgtyp 的消息。消息类型就是前面介绍的消息参考结构中的mtype 。
             当__msgtyp 小于0时,返回消息队列中第一个消息类型小于或者等于__msgtyp的绝对值 的第一个消息。
  __msgflg:为IPC_NOWAIT表示不阻塞,也就是如果消息队列为空,将直接返回-1并且将错误码设置为ENOMSG。为0,如果消息队列为空,将一直阻塞直到消息队列有数据为止。
  该函数成功返回实际读取的数据长度。 失败返回-1。
  注意这个函数调用一次只会返回一条消息数据,即使指定__msgsz可以保存多条消息。




4 消息队列的其他操作:
  函数原型如下:
  int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf);
  __msqid 通过msgget 返回的消息id。
  __cmd :IPC_STAT 读取消息队列的数据结构msqid_ds,并将其存储在__buf指定的地址中。
          IPC_SET  设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自__buf参数。
          IPC_RMID 从系统内核中移走消息队列。不论消息队列中是否还有消息,都将溢出消息队列。
  该函数返回0 ,执行成功。
        返回- 1,执行失败:返回失败是错误码如下:
        errno = EACCES (没有读的权限同时cmd 是IPC_STAT )
                EFAULT (buf 指向的地址无效)
                EIDRM (在读取中队列被删除)
                EINVAL (msgqid无效, 或者msgsz 小于0 )
                EPERM (IPC_SET或者IPC_RMID 命令被使用,但调用程序没有写的权限)
注意:因为消息队列是多进程共享的,所以进程结束后系统并不会主动释放消息队列,所以我们在确保消息队列不用的时候要调用msgctl 释放消息队列,以减少不必要的资源占用。


下面我们看看示例代码:


typedef struct
{
        long mtype;
        char mtext[20];
}my_msg;
int main(int argc, char** argv)
{
        int isSend;
        int index;
        int msgid; 
        my_msg msg;
        if(argc!=3)
        {
                printf("error para,exit\n");
                return 0;
        }
        isSend = argv[1][0]-0x30;//第一个参数为0表示进程接收消息,非0为表示进程发送消息
        index = argv[2][0]-0x30;//第二个参数表示要接收的消息的类型
        if(isSend==0)
        {
                if(index>3||index<0)
                {
                        printf("index error,exit\n");
                        return 0;
                }
        }
        
        key_t key =  ftok(".", 2 );
        printf("key_t = %d\n",key);
        msgid=msgget(key ,IPC_CREAT|0666);  
        if(msgid<0)
        {
                printf("create msg queue error\n");
                return 0;
        }
        if(isSend==1)
        {
         
                struct timespec ts;
        
                while(1)
                {
                        clock_gettime(CLOCK_REALTIME, &ts);
                        msg.mtype=ts.tv_nsec%2+1;
                        sprintf(msg.mtext,"msg= %d",msg.mtype);
                        msgsnd(msgid, &msg, sizeof(my_msg), 0);
                        printf("send msg=%s\n",msg.mtext);
                        sleep(3);
                }
        }
        else
        {
                int len ;
                while(1)
                {
                        len = msgrcv(msgid, (void *)&msg, sizeof(my_msg), index, 0);
                        if(len < 0)
                        {
                                printf("get msg error\n" );
                        }
                        else
                        {
                                printf("process %d get msg =[%s] type=%d\n" ,index ,msg.mtext,msg.mtype);
                        }
                }
        }
        return 0; 
}
例如我们编译出来的可执行文件msgtest
我们分别在终端0运行  msgtest 1 0
        在终端1运行  msgtest 0 1 
        在终端2运行  msgtest 0 2 
        在终端3运行  msgtest 0 3  


我们可以看到在终端0打印 send msg n的时候  (n为1-3)


对应的终端n   就会打印 process n get msg =[msg= n]  type=n    (n为1-3)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值