zoukankan      html  css  js  c++  java
  • TLPI读书笔记第52章-POSIX消息队列

    本章将介绍 POSIX 消息队列,它允许进程之间以消息的形式交换数据。 POSIX 消息队列与 System V 消息队列的相似之处在于数据的交换单位是整个消息,但它们之间仍然存在一些显著的差异。 1.POSIX 消息队列是引用计数的。 只有当所有当前使用队列的进程都关闭了队列之后才会对队列进行标记以便删除。 2.每个 System V 消息都有一个整数类型,并且通过 msgrcv()可以以各种方式类选择消息。与之形成鲜明对比的是, POSIX 消息有一个关联的优先级,并且消息之间是严格按照优先级顺序排队的(以及接收)。 3.POSIX 消息队列提供了一个特性允许在队列中的一条消息可用时异步地通知进程。 POSIX 消息队列被添加到 Linux 中的时间相对来讲是比较短的,所需的实现支持在内核2.6.6 中才被加入(此外,还需要 glibc 2.3.4 或之后的版本)。 POSIX 消息队列支持是一个通过 CONFIG_POSIX_MQUEUE 选项配置的可选内核组件。

    52.1 概述

    POSIX 消息队列 API 中的主要函数如下。

    1.mq_open()函数创建一个新消息队列或打开一个既有队列,返回后续调用中会用到的消息队列描述符。

    2.mq_send()函数向队列写入一条消息。

    3.mq_receive()函数从队列中读取一条消息。

    4.mq_close()函数关闭之前打开的一个消息队列。

    5.mq_unlink()函数删除一个消息队列名并当所有进程关闭该队列时对队列进行标记以便删除。

    上面的函数所完成的功能是相当明显的。 此外, POSIX 消息队列 API 还具备一些特别的特性。

    1.每个消息队列都有一组关联的特性,其中一些特性可以在使用 mq_open()创建或打开队列时进行设置。获取和修改队列特性的工作则是由两个函数来完成的: mq_getattr() 和 mq_setattr()。

    2.mq_notify()函数允许一个进程向一个队列注册接收消息通知。在注册完之后,当一条消息可用时会通过发送一个信号或在一个单独的线程中调用一个函数来通知进程。

    52.2 打开、关闭和断开链接消息队列

    本节将介绍用来打开、关闭和删除消息队列的函数。

    #include<mqueue.h>
    #include<fcntl.h>
    #include<sys/stat.h>
    mqd_t mq_open(const char *name,int oflag,...
                /* mode_t mode,struct mq_attr *attr */)这些是可选参数

    打开一个消息队列mq_open()函数创建一个新消息队列或打开一个既有队列。 name 参数标识出了消息队列,其取值需要遵循 51.1 节中给出的规则。 oflag 参数是一个位掩码,它控制着 mq_open()操作的各个方面。下表对这个掩码中可以包含的值进行了总结。

    标 记 描 述
    O_CREAT O_EXCL 队列不存在时创建队列 与 O_CREAT 一起排它地创建队列
    O_RDONLY O_WRONLY O_RDWR 只读打开 只写打开 读写打开
    O_NONBLOCK 以非阻塞模式打开

    oflag 参数的其中一个用途是,确定是打开一个既有队列还是创建和打开一个新队列。如果在 oflag 中不包含 O_CREAT,那么将会打开一个既有队列。如果在 oflag 中包含了 O_CREAT,并且与给定的 name 对应的队列不存在,那么就会创建一个新的空队列。

    如果在 oflag 中同时包含 O_CREAT 和 O_EXCL, 并且与给定的 name 对应的队列已经存在, 那么 mq_open()就会失败。 oflag 参数还能够通过包含 O_RDONLY、 O_WRONLY 以及 O_RDWR 这三个值中的一个来表明调用进程在消息队列上的访问方式。 剩下的一个标记值 O_NONBLOCK 将会导致以非阻塞的模式打开队列。如果后续的mq_receive()或 mq_send()调用无法在不阻塞的情况下执行, 那么调用就会立即返回 EAGAIN 错误。 mq_open()通常用来打开一个既有消息队列,这种调用只需要两个参数,但如果在 flags中指定了 O_CREAT,那么就还需要另外两个参数: mode 和 attr。(如果通过 name 指定的队列已经存在,那么这两个参数会被忽略。 )这些参数的用法如下。

    1.mode 参数是一个位掩码,它指定了施加于新消息队列之上的权限。这个参数可取的位值与文件上的掩码值(表 15-4)是一样的,并且与 open()一样, mode 中的值会与进程的 umask( 15.4.6 节)取掩码。要从一个队列中读取消息( mq_receive())就必须要将读权限赋予相应的用户,要向队列写入消息( mq_send())就需要写权限。

    2.attr 参数是一个 mq_attr 结构,它指定了新消息队列的特性。如果 attr 为 NULL,那么将使用实现定义的默认特性创建队列。在 52.4 节中将会对 mq_attr 结构进行介绍。 mq_open()在成功结束时会返回一个消息队列描述符,它是一个类型为 mqd_t 的值,在后续的调用中将会使用它来引用这个打开着的消息队列。 SUSv3 对这个数据类型的唯一约束是它不能是一个数组,即需要确保这个类型是一个能在赋值语句中使用或能作为函数参数传递的的类型。 (如在 Linux 上, mqd_t 是一个 int,而在 Solaris 上将其定义为 void *。 ) 程序清单 52-2 给出了一个使用 mq_open()的例子。

    fork()、 exec()以及进程终止对消息队列描述符的影响

    在 fork()中子进程会接收其父进程的消息队列描述符的副本,并且这些描述符会引用同样的打开着的消息队列描述。 (在 52.3 节中将会对消息队列描述进行介绍。 )子进程不会继承其父进程的任何消息通知注册。

    当一个进程执行了一个 exec()或终止时,所有其打开的消息队列描述符会被关闭。关闭消息队列描述符的结果是进程在相应队列上的消息通知注册会被注销。

    关闭一个消息队列

    mq_close()函数关闭消息队列描述符 mqdes。

    #include<mqueue.h>
    int mq_close(mqd_t mqdes);
    int mq_unlink(const char *name);

    如果调用进程已经通过 mqdes 在队列上注册了消息通知( 52.6 节),那么通知注册会自动被删除,并且另一个进程可以随后向该队列注册消息通知。

    当进程终止或调用 exec()时,消息队列描述符会被自动关闭。与文件描述符一样,应用程序应该在不再使用消息队列描述符的时候显式地关闭消息队列描述符以防止出现进程耗尽消息队列描述符的情况。

    与文件上的 close()一样,关闭一个消息队列并不会删除该队列。要删除队列则需要使用mq_unlink(),它是 unlink()在消息队列上的版本。

    删除一个消息队列

    mq_unlink()函数删除通过 name 标识的消息队列,并将队列标记为在所有进程使用完该队列之后销毁该队列(这可能意味着会立即删除,前提是所有打开该队列的进程已经关闭了该队列)。

    52.3 描述符和消息队列之间的关系

    消息队列描述符和打开着的消息队列之间的关系与文件描述符和打开着的文件描述符之间的关系类似(见图 5-2)。消息队列描述符是一个进程级别的句柄,它引用了系统层面的打开着的消息队列描述表中的一个条目,而该条目则引用了一个消息队列对象。图 52-1 对这种关系进行了描绘。

    图 52-1 有助于阐明消息队列描述符的使用方面的细节问题(所有这些都与文件描述符的使用类似)。

    1.一个打开的消息队列描述拥有一组关联的标记。 SUSv3 只规定了一种这样的标记,即NONBLOCK,它确定了 I/O 是否是非阻塞的。

    2.两个进程能够持有引用同一个打开的消息队列描述的消息队列描述符(图中的描述符x)。当一个进程在打开了一个消息队列之后调用 fork()时就会发生这种情况。这些描述符会共享 O_NONBLOCK 标记的状态。

    3.两个进程能够持有引用不同消息队列描述(它们引用了同一个消息队列)的打开的消息队列描述(如进程 A 中的描述符 z 和进程 B 中的描述符 y 都引用了/mq-r)。当两个进程分别使用 mq_open()打开同一个队列时就会发生这种情况

    52.4 消息队列特性

    mq_open()、 mq_getattr()以及 mq_setattr()函数都会接收一个参数,它是一个指向 mq_attr结构的指针。这个结构是在<mqueue.h>中进行定义的,其形式如下。

    #include<mqueue.h>
    struct mq_attr{
       long mq_flags;
       long mq_maxmsg;
       long mq_msgsize;
       long mq_curmsgs;
    }

    在开始深入介绍 mq_attr 的细节之前有必要指出以下几点。

    1.这三个函数中的每个函数都只用到了其中几个字段。上面给出的结构定义中的注释指出了各个函数所用到的字段。

    2.这个结构包含了与一个消息描述符相关联的打开的消息队列描述( mq_flags)的相关信息以及该描述符所引用的队列的相关信息( mq_maxmsg、 mq_msgsize、 mq_curmsgs)。

    2.其 中一些字段中 包 含的信息 在 使用 mq_open() 创 建队 列时 就 已 经确定下来了( mq_maxmsg 和 mq_msgsize);其他字段则会返回消息队列描述( mq_flags)或消息队列( mq_curmsgs)的当前状态的相关信息。

    在创建队列时设置消息队列特性

    在使用 mq_open()创建消息队列时可以通过下列 mq_attr 字段来确定队列的特性。

    1.mq_maxmsg 字段定义了使用 mq_send()向消息队列添加消息的数量上限, 其取值必须大于 0。

    2.mq_msgsize 字段定义了加入消息队列的每条消息的大小的上限,其取值必须大于 0。

    内核根据这两个值来确定消息队列所需的最大内存量。

    mq_maxmsg 和 mq_msgsize 特性是在消息队列被创建时就确定下来的,并且之后也无法 修改这两个特性。在 52.8 节中将会介绍两个/proc 文件,它们为 mq_maxmsg 和 mq_msgsize 特性的取值设定了一个系统层面的限制。 程序清单 52-2 中的程序为 mq_open()函数提供了一个命令行界面并展示了在 mq_open()中如何使用 mq_attr 结构。 消息队列特性可以通过两个命令行参数来指定: –m 用于指定 mq_maxmsg, –s 用于指定mq_msgsize。只要指定了其中一个选项,那么一个非 NULL 的 attrp 参数就会被传递给 mq_open()。如果在命令行中只指定了–m 和–s 选项中的一个,那么 attrp 指向的 mq_attr 结构中的一些字段就会取默认值。如果两个选项都被没有被指定,那么在调用 mq_open()时会将attrp 指定为 NULL,这将会导致使用由实现定义的队列特性的默认值来创建队列。

    获取消息队列特性

    mq_getattr()函数返回一个包含与描述符 mqdes 相关联的消息队列描述和消息队列的相关信息的 mq_attr 结构。 除了上面已经介绍的 mq_maxmsg 和 mq_msgsize 字段之外, attr 指向的返回结构中还包含下列字段。 mq_flags 这些是与描述符 mqdes 相关联的打开的消息队列描述的标记,其取值只有一个:O_NONBLOCK。这个标记是根据 mq_open()的 oflag 参数来初始化的,并且使用 mq_setattr()可以修改这个标记。 mq_curmsgs 这个当前位于队列中的消息数。这个信息在 mq_getattr()返回时可能已经发生了改变,前提是存在其他进程从队列中读取消息或向队列写入消息。 程序清单 52-3 中的程序使用了 mq_getattr()来获取通过命令行参数指定的消息队列的特性,然后在标准输出中显示这些特性

    修改消息队列特性

    mq_setattr()函数设置与消息队列描述符 mqdes 相关联的消息队列描述的特性,并可选地返回与消息队列有关的信息。

    #include<mqueue.h>
    int mq_setattr(mqd_t mqdes,const struct mq_attr *newattr,  struct mq_attr *oldattr);
    int mq_getattr(mqd_t mqdes,struct mq_attr *attr);

    mq_setattr()函数执行下列任务。 1.它使用 newattr 指向的 mq_attr 结构中的 mq_flags 字段来修改与描述符 mqdes 相关联的消息队列描述的标记。 2.如果 oldattr 不为 NULL,那么就返回一个包含之前的消息队列描述标记和消息队列特性的 mq_attr 结构(即与 mq_getattr()执行的任务一样)。

    SUSv3 规定使用 mq_setattr()能够修改的唯一特性是 O_NONBLOCK 标记的状态。

    为支持一个特定的实现可能会定义其他可修改的标记或 SUSv3 后面可能会增加新的标记 , 一 个 可 移 植 的 应 用 程 序 应 该 通 过 使 用 mq_getattr() 来 获 取 mq_flags 值 并 修 改O_NONBLOCK 位来修改 O_NONBLOCK 标记的状态以及调用 mq_setattr()来修改 mq_flags 设置。如为启用 O_NONBLOCK 需要编写下列代码:

    52.5 交换消息

    本节将介绍用来向队列发送消息和从队列中接收消息的函数。

    52.5.1 发送消息

    mq_send()函数将位于 msg_ptr 指向的缓冲区中的消息添加到描述符 mqdes 所引用的消息队列中。

    #include<mqueue.h>
    int mq_send(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned int msg_prio);

    msg_len 参数指定了 msg_ptr 指向的消息的长度, 其值必须小于或等于队列的 mq_msgsize特性,否则 mq_send()就会返回 EMSGSIZE 错误。长度为零的消息是允许的。 每条消息都拥有一个用非负整数表示的优先级,它通过 msg_prio 参数指定。消息在队列中是按照优先级倒序排列的(即 0 表示优先级最低)。当一条消息被添加到队列中时,它会被放置在队列中具有相同的优先级的所有消息之后。如果一个应用程序无需使用消息优先级,那么只需要将 msg_prio 指定为 0 即可

    如果消息队列已经满了(即已经达到了队列的 mq_maxmsg 限制),那么后续的 mq_send()调用会阻塞直到队列中存在可用空间为止或者在 O_NONBLOCK 标记起作用时立即失败并返回 EAGAIN 错误。

    52.5.2 接收消息

    mq_receive()函数从 mqdes 引用的消息队列中删除一条优先级最高、存在时间最长的消息并将删除的消息放置在 msg_ptr 指向的缓冲区。

    #include<mqueue.h>
    int mq_receive(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned int msg_prio);

    调用者使用 msg_len 参数来指定 msg_ptr 指向的缓冲区中的可用字节数。

    不管消息的实际大小是什么, msg_len(即 msg_ptr 指向的缓冲区的大小)必须要大于或等于队列的 mq_msgsize 特性,否则 mq_receive()就会失败并返回 EMSGSIZE 错误。如果不清楚一个队列的 mq_msgsize 特性的值,那么可以使用 mq_getattr()来获取这个值。

    如果 msg_prio 不为 NULL,那么接收到的消息的优先级会被复制到 msg_prio 指向的位置处。如 果 消 息 队 列 当 前 为 空 , 那 么 mq_receive() 会 阻 塞 直 到 存 在 可 用 的 消 息 或 在O_NONBLOCK 标记起作用时会立即失败并返回 EAGAIN 错误。 (管道就不存在类似的行为,即当一端不存在写者时读者不会看到文件结束。 )

    52.5.3 在发送和接收消息时设置超时时间

    mq_timedsend()和 mq_timedreceive()函数与 mq_send()和 mq_receive()几乎是完全一样的,它 们 之 间 唯 一 的 差 别 在 于 如 果 操 作 无 法 立 即 被 执 行 , 并 且 该 消 息 队 列 描 述 上 的O_NONBLOCK 标记不起作用,那么 abs_timeout 参数就会为调用阻塞的时间指定一个上限。

    #include<mqueue.h>
    int mq_timedsend(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned int msg_prio,
                   const struct timespec *abs_timeout);
    int mq_timedreceive(mqd_t mqdes,const char *msg_ptr,size_t msg_len,
                       unsigned int msg_prio,
                      const struct timespec *abs_timeout);

    abs_timeout 参数是一个 timespec 结构( 23.4.2 节),它将超时时间描述为自新纪元到现在的一个绝对值,其单位为秒数和纳秒数。要指定一个相对超时则可以使用 clock_gettime()来获取 CLOCK_REALTIME 时钟的当前值并在该值上加上所需的时间量来生成一个恰当初始化过的 timespec 结构。 如果 mq_timedsend()或 mq_timedreceive()调用因超时而无法完成操作, 那么调用就会失败并返回 ETIMEDOUT 错误。 在 Linux 上将 abs_timeout 指定为 NULL 表示永远不会超时,但这种行为并没有在 SUSv3中得到规定,因此可移植的应用程序不应该依赖这种行为。 mq_timedsend()和 mq_timedreceive()函数最初源自 POSIX.1d (1999), 所有 UNIX 实现都没有提供这两个函数。

    52.6 消息通知

    POSIX 消息队列区别于 System V 消息队列的一个特性是 POSIX 消息队列能够接收之前为空的队列上有可用消息的异步通知(即队列从空变成了非空)。这个特性意味着已经无需执行一个阻塞的调用或将消息队列描述符标记为非阻塞并在队列上定期执行 mq_receive()调用 (“拉”)了,因为一个进程能够请求消息到达通知,然后继续执行其他任务直到收到通知为止。进程可以选择通过信号的形式或通过在一个单独的线程中调用一个函数的形式来接收通知。 POSIX 消息队列的通知特性与 23.6 节中介绍的 POSIX 定时器通知工具类似。 (这两组 API都源自 POSIX.1b。 ) mq_notify()函数注册调用进程在一条消息进入描述符 mqdes 引用的空队列时接收通知。

    #include<mqueue.h>
    int mq_notify(mqd_t mqdes,const struct sig_event *notification);

    notification 参数指定了进程接收通知的机制。在深入介绍 notification 参数的细节之前,有关消息通知需要注意以下几点。

    1.在任何一个时刻都只有一个进程(“注册进程”)能够向一个特定的消息队列注册接收通知。如果一个消息队列上已经存在注册进程了,那么后续在该队列上的注册请求将会失败( mq_notify()返回 EBUSY 错误)。

    2.只有当一条新消息进入之前为空的队列时注册进程才会收到通知。 如果在注册的时候队列中已经包含消息,那么只有当队列被清空之后有一条新消息达到之时才会发出通知。

    3.当向注册进程发送了一个通知之后就会删除注册信息,之后任何进程就能够向队列注册接收通知了。换句话说,只要一个进程想要持续地接收通知,那么它就必须要在每次接收到通知之后再次调用 mq_notify()来注册自己。

    4.注册进程只有在当前不存在其他在该队列上调用 mq_receive()而发生阻塞的进程时才会收到通知。如果其他进程在 mq_receive()调用中被阻塞了,那么该进程会读取消息,注册进程会保持注册状态。

    5.一个进程可以通过在调用 mq_notify()时传入一个值为 NULL 的 notification 参数来撤销自己在消息通知上的注册信息。 在 23.6.1 节中已经对 notification 参数的类型 sigevent 结构进行了介绍。下面给出的是该结构的一个简化版本,它只列出了与 mq_notify()相关的字段。 这个结构的 sigev_notify 字段将会被设置成下列值中的一个。 SIGEV_NONE

    注册这个进程接收通知,但当一条消息进入之前为空的队列时不通知该进程。与往常一 样,当新消息进入空队列之后注册信息会被删除。

    SIGEV_SIGNAL 通过生成一个在 sigev_signo 字段中指定的信号来通知进程。如果 sigev_signo 是一个实时信号,那么 sigev_value 字段将会指定信号都带的数据( 22.8.1 节)。通过传入信号处理器的siginfo_t 结构中的 si_value 字段或通过调用 sigwaitinfo()或 sigtimedwait()返回值能够取得这部分数据。 siginfo_t 结构中的下列字段也会被填充: si_code,其值为 SI_MESGQ; si_signo,其值是信号编号; si_pid,其值是发送消息的进程的进程 ID;以及 si_uid,其值是发送消息的进程的真实用户 ID。 ( si_pid 和 si_uid 字段在其他大多数实现上不会被设置。 )

    SIGEV_THREAD 通过调用在 sigev_notify_function 中指定的函数来通知进程,就像是在一个新线程中启动该函数一样。 sigev_notify_attributes 字段可以为 NULL 或是一个指向定义了线程的特性的 pthread_attr_t 结构的指针(29.8 节)。 sigev_value 中指定的联合 sigval 值将会作为参数传入这个函数。

    52.7 Linux 特有的特性

    POSIX 消息队列在 Linux 上的实现提供了一些非标准的却相当有用的特性。

    通过命令行显示和删除消息队列对象

    在 51 章中提到过 POSIX IPC 对象被实现成了虚拟文件系统中的文件,并且可以使用 ls和 rm 来列出和删除这些文件。为列出和删除 POSIX 消息队列就必须要使用形如下面的命令来将消息队列挂载到文件系统中。 source 可以是任意一个名字(通常将其指定为字符串 none),其唯一的意义是它将出现在/proc/mounts 中并且 mount 和 df 命令会显示出这个名字。 target 是消息队列文件系统的挂载点。 下面的 shell 会话显示了如何挂载消息队列文件系统和显示其内容。首先为文件系统创建一个挂载点并挂载它。接着显示新挂载在/proc/mounts 中的记录,然后显示挂载目录上的权限。 在 ls 命令的输出中需要注意的一点是消息队列文件系统在挂载时会自动为挂载目录设置粘滞位。(从 ls 的输出中的 other-execute 权限字段中有一个 t 就可以看出这一点。 )这意味着非特权进程只能在它所拥有的消息队列上执行断开链接的操作。 接着创建一个消息队列,使用 ls 来表明它在文件系统中是可见的,然后删除该消息队列。

    获取消息队列的相关信息

    可以显示消息队列文件系统中的文件的内容,每个虚拟文件都包含了其关联的消息队列的相关信息。

    QSIZE 字段的值为队列中所有数据的总字节数,剩下的字段则与消息通知相关。如果NOTIFY_PID 为非零,那么进程 ID 为该值的进程已经向该队列注册接收消息通知了,剩下的字段则提供了与这种通知相关的信息。

    1.NOTIFY 是一个与其中一个 sigev_notify 常量对应的值: 0 表示 SIGEV_SIGNAL, 1表示 SIGEV_NONE, 2 表示 SIGEV_THREAD。

    2.如果通知方式是 SIGEV_SIGNAL, 那么 SIGNO 字段指出了哪个信号会用来分发消息通知。

    下面的 shell 会话对这些字段中包含的信息进行了说明。

    使用另一种 I/O 模型操作消息队列

    在 Linux 实现上,消息队列描述符实际上是一个文件描述符,因此可以使用 I/O 多路复用系统调用( select()和 poll())或 epoll API 来监控这个文件描述符。

    这样就能够避免在使用 System V 消息队列时同时等待一个消息队列和一个文件描述符上的输入的困难局面(参见 46.9 节)的出现。但这项特性不是标准特性, SUSv3 并没有要求将消息队列描述符实现成文件描述符。

    52.8 消息队列限制

    SUSv3 为 POSIX 消息队列定义了两个限制。

    MQ_PRIO_MAX

    在 52.5.1 中已经对这个限制进行了介绍,它定义了一条消息的最大优先级。

    MQ_OPEN_MAX

    一个实现可以定义这个限制来指明一个进程最多能打开的消息队列数量。 SUSv3 要求这个限制最小为_POSIX_MQ_OPEN_MAX( 8)。 Linux 并没有定义这个限制,相反,由于 Linux将消息队列描述符实现成了文件描述符( 52.7 节),因此适用于文件描述符的限制将适用于消息队列描述符。 (换句话说,在 Linux 上,每个进程以及系统所能打开的文件描述符的数量限制实际上会应用于文件描述符数量和消息队列描述符数量之和。 )

    除了上面列出的由 SUSv3 规定的限制之外, Linux 还提供了一些/proc 文件来查看和修改(需具备特权)控制 POSIX 消息队列的使用的限制。下面这三个文件位于/proc/sys/fs/ mqueue 目录中。

    msg_max

    这个限制为新消息队列的 mq_maxmsg 特性的取值规定了一个上限(即使用 mq_open()创建队列时 attr.mq_maxmsg 字段的上限值)。这个限制的默认值是 10,最小值是 1(在早于 2.6.28 的内核中是 10),最大值由内核常量 HARD_MSGMAX 定义,该常量的值是通过公式(131072 / sizeof(void *))计算得来的,在 Linux/x86-32 上其值为 32768。当一个特权进程( CAP_SYS_RESOURCE)调用 mq_open()时 msg_max 限制会被忽略,但 HARD_MSGMAX 仍然担当着 attr.mq_maxmsg 的上限值的角色。

    msgsize_max

    这个限制为非特权进程创建的新消息队列的 mq_msgsize 特性的取值规定了一个上限(即使用 mq_open()创建队列时 attr.mq_msgsize 字段的上限值)。这个限制的默认值是 8192,最小值是 128(在早于 2.6.28 的内核中是 8192),最大值是 1048576(在早于 2.6.28 的内核中是INT_MAX)。 当一个非特权进程( CAP_SYS_RESOURCE)调用 mq_open()时会忽略这个限制。

    queues_max

    这是一个系统级别的限制,它规定了系统上最多能够创建的消息队列的数量。一旦达到这个限制,就只有特权进程( CAP_SYS_RESOURCE)才能够创建新队列。这个限制的默认值是 256,其取值可以为范围从 0 到 INT_MAX 之间的任意一个值。 Linux 还提供了 RLIMIT_MSGQUEUE 资源限制,它可以用来为属于调用进程的真实用户ID 的所有消息队列所消耗的空间规定一个上限,细节信息请参考 36.3 节。

    52.9 POSIX 和 System V 消息队列比较

    51.2 节列出了 POSIX IPC 接口与 System V IPC 接口相比存在的各种优势: POSIX IPC 接口更加简单并且与传统的 UNIX 文件模型更加一致,同时 POSIX IPC 对象是引用计数的,这样就简化了确定何时删除一个对象的任务。

    优势

    POSIX 消息队列与 System V 消息队列相比还具备下列优势。 1.消息通知特性允许一个(单个)进程能够在一条消息进入之前为空的队列时异步地通过信号或线程的实例化来接收通知。 2.在 Linux(不包括其他 UNIX 实现) 上可以使用 poll()、 select()以及 epoll 来监控 POSIX消息队列。 System V 消息队列并没有这个特性。

    劣势

    1.POSIX 消息队列的可移植性稍差,即使在不同的 Linux 系统上也存在这个问题,因为直到内核 2.6.6 才提供了对消息队列的支持。 2.与 POSIX 消息队列严格按照优先级排序相比, System V 消息队列能够根据类型来选择消息的功能的灵活性更强。

    52.10 总结

    POSIX 消息队列允许进程以消息的形式交换数据。每条消息都有一个关联的整数优先级,消息按照优先级顺序排列(从而会按照这个顺序接收消息)。 POSIX 消息队列与 System V 消息队列相比具备一些优势,特别是它们是引用计数的并且一个进程在一条消息进入空队列时能够异步地收到通知,但 POSIX 消息队列的移植性要比System V 消息队列稍差。

  • 相关阅读:
    My family No.1
    机器学习之数学原理笔记(五)
    机器学习之数学原理笔记(四)
    机器学习之数学原理笔记(三)
    机器学习笔记(一)
    有关矩阵的几点总结
    C++笔记(二)------ 头文件
    网络编程基础
    第四次测试--面向对象
    面试题整理
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/14866470.html
Copyright © 2011-2022 走看看