IPC机制之消息队列

   消息队列是消息在传输过程中保存消息的容器,它提供了一种从一个进程向另一个进程发送数据块的方法。而且,每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型值的数据块。

     消息队列的优点是可以通过发送消息来完全避免命名管道的同步和阻塞问题,甚至可以用一些方法来提前查看紧急消息。其缺点是:与管道一样每个数据块都有一个最大长度限制,系统中所有队列所包含的全部数据块的总长度也有一个上限。

      这里我们还是按照惯例来首先介绍和消息队列有关的函数,具体的例子在后面。

消息队列相关函数

msgget() 用来创建和访问一个消息队列

#include<sys/msg.h>

int msgget(key_t  key,  int  msgflg);

    第一个参数key,与其他IPC机制一样,程序必须提供一个键值来命名某个特定的消息队列。特殊键值IPC_PRIVATE用于创建私有队列,从理论上来说它应该只能被当前进程访问,但同信号量和共享内存的情况是一样的,消息队列在某些LINUX系统中事实上并非私有。由于私有队列并没有什么用处,所以这并不算一个很严重的问题。

    第二个参数msgflg由9个权限标志组成。由IPC_CREAT定义的一个特殊位必须和权限标志按位或才能创建一个新的消息队列。在设置IPC_CREAT标志时,如果给出的是一个已有消息队列的键也不会产生错误。如果消息队列已有,则IPC_CREAT会被悄悄地忽略掉。

     成功时,IPC_CREAT返回一个正整数,即队列标识符,失败时返回-1。

msgsnd()用来把消息添加到消息队列中

#include<sys/msg.h>

int msgsnd(int  msgid,  const  void*  msg_ptr,  size_t  msg_sz,  int  msgflg)

  消息的结构受到两方面的约束。首先,它的长度必须小于系统规定的上限;其次,它必须以一个长整型成员变量开始,接收函数将用这个成员变量来确定消息的类型。当使用消息时,最好把消息结构定义为下面这样。

struct my_messege

{

     long int messege_type;

     /*剩下的成员是你想要发送的消息*/

}

     由于在消息的接收中要用到messge_type,所以你不能忽略它。你必须在声明自己的数据结构时包含它,并且最好将他初始化为一个已知值,但这个值必须大于等于 1 。

     第一个参数 msgid 是由msgget函数返回的消息队列标识符

     第二个参数msg_ptr是一个指向准备发送消息的指针,消息必须像刚才说的那样以以一个长整型成员变量开始。

     第三个参数msg_sz是msg_ptr指向的消息的长度。这个长度不能包含长征型消息类型的成员变量的长度。

     第四个参数msgflg控制在当前消息队列满或队列消息达到系统范围的限制时将要发生的事情。这里有个常用的标志:

IPC_NOWAIT,函数立即返回,不发送消息并且返回值为-1。若不存在该标志,则发送进程将挂起以等待队列中腾出可用空间。

       成功时函数返回0,失败时返回-1。如果调用成功,消息数据的一份副本将被放到消息队列中。

msgrcv()函数从一个消息队列中获取消息。

#include<sys/msg.h>

int msgrcv(int msgid, void*  msg_ptr,  size_t  msg_sz, long int  msg_type, int  msgflg)

        第一个参数msgid是由msgget函数返回的消息队列标识符

  第二个参数msg_ptr是一个指向准备接受消息的指针,消息必须像前面msgsnd函数中介绍的那样,以一个长整型成员变量开始。

         第三个参数msg_sz是msg_ptr指向的消息的长度,他不包括长整型消息类型成员变量的长度。

       第四个参数msgtype是一个长整数,它可以实现一种简单形式的接收优先级。如果msgtype的值为0,就获取队列中第一个可用消息。如果他的值大于0,将获取具有相同消息类型的第一个消息。如果它的值小于0,将获取消息类型等于或小于msgtype的绝对值的第一个消息。所以,如果只想要按照消息的发送顺序来接受他们,就把msgtype设置为0,如果只想获取某一特定类型的消息,就把msgtype设置为相应的类型值。如果想接收类型等于或小于n的消息,就把msgtype设置为-n。

      第五个参数msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情。若msgflg被设置为IPC_NOWAIT标志,函数会立刻返回,返回值为-1。如果未设置该标志,那么进程将会挂起以等待一条相应类型的消息到达。

     成功时msgrcv返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时,返回-1

msgctl()函数消息队列控制函数

#include<sys/msg.h>

int msgctl(int  msqid,   int command  ,  struct  msqid_ds  *buf);

msqid_ds结构至少包含以下成员

struct msqid_ds

{

     uid_t  msg_perm.uid;

     uid_t  msg_perm.gid;

     mode_t  msg_perm.mode;

}

    第一个参数msqid是由msgget返回的消息队列标识符。

    第二个参数command是将要采取的动作,它可以取三个值

IPC_STAT  把msqid_ds结构中的数据设置为消息队列的当前关联值

IPC_SET    如果进程有足够的权限,就把消息队列当前的关联值设置为msqid_ds结构中给出的值

IPC_RMID 删除消息队列

     第三个参数buf就是刚才所说的指向msqid_ds结构的指针

    成功时返回0,失败时返回-1。如果删除消息队列时,某个进程正在msgsnd和msgrcv函数中等待,这两个函数将失败。

具体举例

      我们编写一个名为 msg_send.c 的程序,用来创建消息队列并往该队列中添加消息,我们这次添加的消息时“hello world 2”。另一个用来接收的程序我们叫做 msg_recv.c ,用来获取队列中的消息并打印到屏幕。

      另外,我们可以使用 ipcs  -q 命令来查看消息队列中的内容。

      参考之前的函数使用方法,我们把该消息的类型定义为 2,接收的也是类型为 2 的函数,具体代码为:

/*
	msg_send.c 创建消息队列并往该队列中添加消息
*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>
#include<string.h>
#include<sys/msg.h>

struct mess
{
    long int type;  //数据类型   
    char buff[32];  //我们这里发送一个字符串
};


int main()
{
    int msgid = msgget((key_t)9527, IPC_CREAT | 0600); //创建一个消息队列
    assert(msgid != -1);
    
    struct mess dt; //开始定义的那个消息结构体
    dt.type = 2;    //把消息的类型定义为2
    strcpy(dt.buff,"hello world 2");//我们要发送的字符串是“hello world 2”
    
    msgsnd(msgid, (void*)&dt, 32, 0); //往消息队列中添加消息
        
    exit(0);    
}

上面是 msg_send.c 的具体代码,下面的是 msg_recv.c 的具体代码:

/*
	msg_recv 获取队列中的消息并打印到屏幕
*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>
#include<string.h>
#include<sys/msg.h>

struct mess
{
    long type;
    char buff[32];    
};

int main()
{
    int msgid = msgget((key_t)9527, IPC_CREAT | 0600);
    assert(msgid != -1);
    
    struct mess dt; //这里的消息结构体用来接收消息
    msgrcv(msgid, (void*)&dt, 32, 2, 0); //获取消息队列中的符合类型的消息
    
    printf("buff=%s\n", dt.buff);    
    exit(0);    
}

让我们运行一下这两个程序,并且用 ipcs  -q查看该消息队列的内容,其结果为:

可以看到,当我们运行 msg_send 后,发现消息队列里已经存在了一条消息。当我们运行 msg_recv 后,这条消息就被我们读取了,成功打印出“hello  world  2”,消息队列里之后就没有内容了。

当我们多次运行msg_send程序时,这条消息也会重复写入:

可以看到,我们运行该程序三次后,消息队列中有了96字节的数据,正好是执行一次的3倍。

     读者可以自己试验写几个 msg_send 程序,每个人程序定义不同类型的数据往消息队列中写入,再用 msg_recv

去获取不同类型的消息,这样就可以体会到 msgrcv() 函数的第四个参数的作用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值