06.System V 消息队列

1、概述

System V消息队列使用消息队列标识符(message queue identifier)标识。具有足够特权的任何进程都可以往一个给定队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息。跟Posix消息队列一样,在某个进程往一个队列中写入一个消息之前,不求另外某个进程正在等待该队列上一个消息的到达。

对于系统中的每个消息队列,内核维护一个定义在<sys/msg.h>头文件中的信息结构。

struct msqid_ds 
{
    struct ipc_perm   msg_perm;      /* read_write perms: Section 3.3 */
    struct msg       *msg_first;     /* ptr to first message on queue */
    struct msg       *msg_last;      /* ptr to last message on queue */
    msglen_t          msg_cbytes;    /* current # bytes on queue */
    msgqnun_t         msg_qnum;      /* current # of messages on queue */
    msgien_t          msg_qbytes;    /* max # of bytes allowed on queue */
    pid_t             msg_lspid;     /* pid of last msgsnd() */
    pid_t             msg_lrpid;     /* pid of last msgrcv() */
    time_t            msg_stime;     /* time of last msgsnd() */
    time_t            msg_rtime;     /* time of last msgrcv() */
    time_t            msg_ctime;     /* time of last magct1() (that changed the above) */
};

我们可以将内核中某个特定的消息队列画为一个消息链表,如图6-1所示。假设有一个具有三个消息的队列,消息长度分别为1字节、2字节和3字节,而且这些消息就是以这样的顺序写入该队列的。再假设这三个消息的类型(type)分别为100,200和300。

2、msgget 函数

msgget 函数用于创建一个新的消息队列或访问一个已存在的消息队列。

#include <sys/msg.h>
int msgget(key_t key, int oflag);
        // 返回:若成功则为非负标识符,若出错则为-1

返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的,而key既可以是ftok的返回值,也可以是常值IPC_PRIVATE。

oflag读写权限值的组合。它还可以与IPC_CREAT或IPC_CREAT | IPC_EXCL按位或。

当创建一个新消息队列时,msqid_ds 结构的如下成员被初始化。

  • msg_perm 结构的uid和cuid成员被设置成当前进程的有效用户ID, gid和cgid成员被设置成当前进程的有效组ID。
  • oflag中的读写权限位存放在msg_perm.mode中。
  • msg_qnum、msg_lspid、msg_lrpid、msg_stime和msg_rtime被置为0。
  • msg_ctime被设置成当前时间。
  • msg_qbytes被设置成系统限制值。

 

3、msgsnd 函数

使用msgget打开一个消息队列后,我们使用msgsnd往其上放置一个消息。

#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t length, int flag);
    	            // 返回:若成功则为0,若出错则为-1

其中msqid是由msgget返回的标识符。ptr是一个结构指针,该结构具有如下的模板,它定义在<sys/msg.h>中

struct msgbuf 
{
    long mtype;      /* message type, must be > 0 */
    char mtext 1];   /* message data */
};

 

4、msgrcv 函数

使用msgrcv函数从某个消息队列中读出一个消息。

#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t length, long type, int flag);
	// 返回:若成功则为读入缓冲区中数据的字节数,若出错则为-1

其中ptr参数指定所接收消息的存放位置。跟msgsnd一样,该指针指向紧挨在真正的消息数据之前返回的长整数类型字段

length指定了由ptr指向的缓冲区中数据部分的大小。这是该函数能返回的最大数据量。该长度不包括长整数类型字段。

type指定希望从所给定的队列中读出什么样的消息。

 

  • 如果type为0,那就返回该队列中的第一个消息。既然每个消息队列都是作为一个FIFO(先进先出)链表维护的,因此ype为0指定返回该队列中最早的消息
  • 如果type大于0,那就返回其类型值为type的第一个消息
  • 如果tpe小于0,那就返回其类型值小于或等于type参数的绝对值的消息中类型值最小的第一个消息

 

5、msgctl 函数

msgctl 函数提供在一个消息队列上的各种控制操作

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buff);
            // 返回:若成功则为0,若出错则为-1

msgctl 函数提供3个命令。

  • IPC_RMID 从系统中删除由msqid指定的消息队列。当前在该队列上的任何消息都被丢弃。
  • IPC-SET    给所指定的消息队列设置其msqid_ds结构的以下4个成员: msg_perm.uid、msg_perm.gid、                           msg_perm.mode  和msg_qbytes。 它们的值来自由buff参数指向的结构中的相应成员。
  • IPC-STAT  (通过buff参数)给调用者返回与所指定消息队列对应的当前msgid_ds结构。

例子:

下面程序创建一个消息队列,往该队列中放置一个含有1字节数据的消息,发出msgctl的IPC_STAT命令,使用system函数执行ipcs命令,最后使用msgctl的IPC_RMID命令删除该队列。

// ctl.c
#include  "unpipc.h"
int main(int argc, char **argv)
{
  int msqid;
  struct msqid_ds info;
  struct msgbuf buf;

  msqid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);

  buf.mtype = 1;
  buf.mtext[0] = 1;
  Msgsnd(msqid, &buf, 1, 0); 

  Msgctl(msqid, IPC_STAT, &info);
  printf("read-write: %03o, cbytes = %lu, qnum = %lu, qbytes = %lu\n",
       info.msg_perm.mode & 0777, (ulong_t) info.msg_cbytes,
       (ulong_t) info.msg_qnum, (ulong_t) info.msg_qbytes);

  system("ipcs -q");

  Msgctl(msqid, IPC_RMID, NULL);
  exit(0);
}

执行结果:

 

6、简单的程序

6.1 msgcreate 函数,创建一个消息队列

// msgcreate.c
#include  "unpipc.h"
int main(int argc, char **argv)
{
  int c, oflag, mqid;

  oflag = SVMSG_MODE | IPC_CREAT;
  while ( (c = Getopt(argc, argv, "e")) != -1) 
  {
    switch (c) 
    {   
    case 'e':
      oflag |= IPC_EXCL;
      break;
    }   
  }
  if (optind != argc - 1)
    err_quit("usage: msgcreate [ -e ] <pathname>");

  mqid = Msgget(Ftok(argv[optind], 0), oflag);
  exit(0);
}

6.2 msgsnd 函数,将消息放置到队列中

// msgsnd.h
#include  "unpipc.h"
int main(int argc, char **argv)
{
  int   mqid;
  size_t  len;
  long  type;
  struct msgbuf *ptr;

  if (argc != 4)
    err_quit("usage: msgsnd <pathname> <#bytes> <type>");
  len = atoi(argv[2]);
  type = atoi(argv[3]);

  mqid = Msgget(Ftok(argv[1], 0), MSG_W);

  ptr = Calloc(sizeof(long) + len, sizeof(char));
  ptr->mtype = type;

  Msgsnd(mqid, ptr, len, 0); 

  exit(0);
}

6.3 msgrcv 函数,从队列中读出一个消息。

-n 命令行选择指定非阻塞

-t 命令行选项指定msgrcv函数的type参数

#include  "unpipc.h"

#define MAXMSG  (8192 + sizeof(long))

int main(int argc, char **argv)
{
  int   c, flag, mqid;
  long  type;
  ssize_t n;
  struct msgbuf *buff;

  type = flag = 0;
  while ( (c = Getopt(argc, argv, "nt:")) != -1)
  {
    switch (c) 
    {
    case 'n':
      flag |= IPC_NOWAIT;
      break;

    case 't':
      type = atol(optarg);
      break;
    }
  }
  
  if (optind != argc - 1)
    err_quit("usage: msgrcv [ -n ] [ -t type ] <pathname>");

  mqid = Msgget(Ftok(argv[optind], 0), MSG_R);
  buff = Malloc(MAXMSG);

  n = Msgrcv(mqid, buff, MAXMSG, type, flag);
  printf("read %d bytes, type = %ld\n", n, buff->mtype);

  exit(0);
}

6.4 msgrmid 函数,删除一个消息队列

#include  "unpipc.h"
int main(int argc, char **argv)
{
  int   mqid;

  if (argc != 2)
    err_quit("usage: msgrmid <pathname>");

  mqid = Msgget(Ftok(argv[1], 0), 0); 
  Msgctl(mqid, IPC_RMID, NULL);

  exit(0);
}

6.5 例子执行

创建一个消息队列并往其中写入三个消息:

不以FIFO顺序读取消息:

 

6.6 msgrcvid 函数

要访问一个System V消息队列,调用msgget并不是必须的:我们只需知道该消息队列的标识符(使用ipcs极易得到),并拥有该队列的读权限。

例子:

#include  "unpipc.h"
#define MAXMSG  (8192 + sizeof(long))
int main(int argc, char **argv)
{
  int   mqid;
  ssize_t n;
  struct msgbuf *buff;

  if (argc != 2)
    err_quit("usage: msgrcvid <mqid>");
  
  mqid = atoi(argv[1]);
  buff = Malloc(MAXMSG);
  n = Msgrcv(mqid, buff, MAXMSG, 0, 0); 
  printf("read %d bytes, type = %ld\n", n, buff->mtype);

  exit(0);
} 

执行结果:

 

7、小结

System V消息队列与Posix消息队列类似。新的应用程序应考虑使用Posix消息队列,不过大量的现有代码使用System V消息队列。然而把一个应用程序从使用System V消息队列重新编写成使用Posix消息队列不是件难事。 Posix消息队列缺失的主要特性是从队列中读出指定优先级的消息的能力。这两种消息队列都不使用真正的描述符,从而造成在消息队列上使用select或poll的困难。

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值