UNIX环境高级编程——system V消息队列
unix早期通信机制中的信号能够传送的信息量有限,管道则只能传送无格式字节流,这远远是不够的。
消息队列(也叫报文队列)客服了这些缺点:
消息队列就是一个消息的链表。
可以把消息看作一个记录,具有特定的格式。
进程可以按照一定的规则向消息队列中添加新消息;另一些进程可以从消息队列中读走消息。
消息队列是随内核持续的,只有内核重启或人工删除时,该消息队列才会被删除。
system V消息队列使用消息队列标识符标识。具有足够特权的任何进程都可以往一个给定队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息。
消息队列具有一定的FIFO特性,但是它可以实现消息的随机查询,比FIFO具有更大的优势。同时这些消息又存在于内核中,由“队列”ID来标识消息队列的实现包括创建或打开消息队列,添加消息,读取消息和控制消息队列这四种操作。
对于系统中的每个消息队列,内核维护一个定义在<sys/msg.h>头文件中的信息结构。
- struct msqid_ds {
- struct ipc_perm msg_perm;
- struct msg *msg_first; /* first message on queue,unused */
- struct msg *msg_last; /* last message in queue,unused */
- __kernel_time_t msg_stime; /* last msgsnd time */
- __kernel_time_t msg_rtime; /* last msgrcv time */
- __kernel_time_t msg_ctime; /* last change time */
- unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
- unsigned long msg_lqbytes; /* ditto */
- unsigned short msg_cbytes; /* current number of bytes on queue */
- unsigned short msg_qnum; /* number of messages in queue */
- unsigned short msg_qbytes; /* max number of bytes on queue */
- __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
- __kernel_ipc_pid_t msg_lrpid; /* last receive pid */
- };
我们可以将内核中的某个特定的消息队列画为一个消息链表,如图假设有一个具有三个消息的队列,消息长度分别为1字节,2字节和3字节,而且这些消息就是以这样的顺序写入该队列的。再假设这三个消息的类型分别为100,200,300.
1.msgget函数
msgget函数用于创建一个新的消息队列或访问一个已存在的消息队列。
- #include <sys/msg.h>
- int msgget(key_t key,int oflag);
返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的,而key即可以是ftok的返回值,也可以是常值IPC_PRIVATE。
oflag是读写权限值得组合。它还可以与IPC_CREAT或IPC_CREAT | IPC_EXCL按位或,IPC_NOWAIT --- 读写消息队列要求无法得到满足时,不阻塞。
当创建一个新消息队列时,msqid_ds结构的如下成员被初始化。
(1)msg_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cgid成员被设置成当前进程的有效组ID。
(2)oflag中的读写权限位存放在msg_perm.mode中。
(3)msg_qnum,msg_lspid,msg_lrpid,msg_stime和msg_rtime被置为0.
(4)msg_ctime被设置成当前时间。
(5)msg_qbytes被设置成系统限制值。
- huangcheng@ubuntu:~$ ipcs -q ----查看消息队列
- ------ Message Queues --------
- key msqid owner perms used-bytes messages
2.msgsnd函数
使用msgget函数打开一个消息队列后,我们使用msgsnd往其上放置一个消息。
- #include <sys/msg.h>
- int msgsnd(int msgid,const void *ptr,size_t length,int flag);
其中msqid是由msgget返回的标识符。ptr是一个结构指针,该结构具有如下模板,它定义在<sys/msg.h>中。
- struct msgbuf {
- long mtype; /* message type, must be > 0 */
- char mtext[1]; /* message data */
- };
msgsnd的length参数以字节为单位指定待发送消息的长度。这是位于长整数消息类型之后的用户自定义数据的长度(注意:不包括消息类型)。该长度可以是0.
flag参数既可以是0,也可以是IPC_NUWAIT。IPC_NOWAIT标志使得msgsnd调用非阻塞:如果没有存放新消息的可用空间(即消息队列已满),该函数马上返回。这个条件发生的情形包括:
(1)在指定的队列中已有太多的字节(对于该队列的msqid_ds结构中的msg_qbytes值);
(2)在系统范围存在太多的消息。
如果这两个条件中有一个存在,而且IPC_NUWAIT标志已指定,msgsnd就返回一个EAGAIN错误。如果这两个条件有一个存在,但是IPC_NUWAIT标志未指定,那么调用线程被投入睡眠直到:
(1)具备存放新消息的空间;
(2)由msqid标志的消息队列从系统中删除(这种情况下返回一个EIDRM错误);
(3)调用线程被某个捕获的信号所中断(这种情况下返回一个EINTR错误)。
3.msgrcv函数
使用msgrcv函数从某个消息队列中读取一个消息。
- #include <sys/msg.h>
- ssize_t msgrcv(int msqid,void *ptr,size_t lengh,long type,int flag);
其中ptr参数指定所接收消息的存放位置。跟msgsnd一样,该指针指向紧挨在真正的消息数据之前返回的长整数类型字段。
length指定了由ptr指向的缓冲区中数据部分的大小。这是该函数能返回的最大数据量。该长度不包括长整数类型字段。
type指定希望从所给定的队列中读出什么样的消息。
(1)如果type为0,那就返回该队列中的第一个消息,既然每个消息队列都是作为一个FIFO链表维护的,因此type为0指定返回该队列中最早的消息。
(2)如果type大于0,那就返回其类型值为type的第一个消息。
(3)如果type小于0,那就返回其类型小于或等于type参数的绝对值的消息中类型值最小的第一个消息。
msgrcv的flag参数指定所请求类型的消息不在所指定的队列中时该做何处理。在没有消息可得的情况下,如果设置了flag中的IPC_NOWAIT位,msgrcv函数就立即返回一个ENOMSG错误。否则,设置了flag为0,调用者被阻塞到下列某个事件发生为止:
(1)有一个所请求类型的消息可获取;
(2)由msqid标志的消息队列从系统中删除(这种情况下返回一个EIDRM错误);
(3)调用线程被某个捕获的信号所中断(这种情况下返回一个EINTR错误)。
flag参数中另有一位可以指定:MSG_NOERROR。当所接收消息的真正数据部分大于length参数时,如果设置了该位,msgrcv函数就只是截短数据部分,而不返回错误。否则,ms_grcv返回一个E2BIC错误。
成功返回时,msgrcv返回的是所接收消息中数据的字节数。它不包括也通过ptr参数返回的长整数消息类型所需的几个字节。
4.msgctl函数
msgctl函数提供在一个消息队列上的各种控制操作。
- #include <sys/msg.h>
- int msgctl(int msqid,int cmd,struct msqid_ds *buf);
msgctl函数提供3个命令。
IPC_RMID 从系统中删除由msqid指定的消息队列。当前在该队列上的任何消息都被丢弃。对于该命令而言,msgctl函数的第三个参数被忽略。
IPC_SET 给所指定的消息队列设置其msgid_ds结构的以下4个成员:msg_perm.uid,msg_perm.gid,msg_perm.mode和msg_qbytes。他们的值来自buff参数指向的结构中的相应的成员。
IPC_STAT 读取消息队列的属性,并将其保存在buf指向的缓冲区中。
msgrcv.c用于接收消息,msgsend.c用于发送消息:
msgrcv.c
- /* Here's the receiver program. */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <unistd.h>
- #include <sys/msg.h>
- struct my_msg_st {
- long int my_msg_type;
- char some_text[BUFSIZ];
- };
- int main()
- {
- int running = 1;
- int msgid;
- struct my_msg_st some_data;
- long int msg_to_receive = 0;
- /* First, we set up the message queue. */
- msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
- if (msgid == -1) {
- fprintf(stderr, "msgget failed with error: %d\n", errno);
- exit(EXIT_FAILURE);
- }
- /* Then the messages are retrieved from the queue, until an end message is encountered.
- Lastly, the message queue is deleted. */
- while(running) {
- if (msgrcv(msgid, (void *)&some_data, BUFSIZ,msg_to_receive, 0) == -1) {
- fprintf(stderr, "msgrcv failed with error: %d\n", errno);
- exit(EXIT_FAILURE);
- }
- printf("You wrote: %s", some_data.some_text);
- if (strncmp(some_data.some_text, "end", 3) == 0)
- running = 0;
- }
- if (msgctl(msgid, IPC_RMID, 0) == -1) {
- fprintf(stderr, "msgctl(IPC_RMID) failed\n");
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- }
msgsend.c
- /* The sender program is very similar to msg1.c. In the main set up, delete the
- msg_to_receive declaration and replace it with buffer[BUFSIZ], remove the message
- queue delete and make the following changes to the running loop.
- We now have a call to msgsnd to send the entered text to the queue. */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <unistd.h>
- #include <sys/msg.h>
- #define MAX_TEXT 512
- struct my_msg_st {
- long int my_msg_type;
- char some_text[MAX_TEXT];
- };
- int main()
- {
- int running = 1;
- struct my_msg_st some_data;
- int msgid;
- char buffer[BUFSIZ];
- msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
- if (msgid == -1) {
- fprintf(stderr, "msgget failed with error: %d\n", errno);
- exit(EXIT_FAILURE);
- }
- while(running) {
- printf("Enter some text: ");
- fgets(buffer, BUFSIZ, stdin);
- some_data.my_msg_type = 1;
- strcpy(some_data.some_text, buffer);
- if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) {
- fprintf(stderr, "msgsnd failed\n");
- exit(EXIT_FAILURE);
- }
- if (strncmp(buffer, "end", 3) == 0)
- running = 0;
- }
- exit(EXIT_SUCCESS);
- }
运行结果:
- huangcheng@ubuntu:~$ ./msgsend
- Enter some text: ctt
- Enter some text: huangcheng
- Enter some text: end
- huangcheng@ubuntu:~$ ./msgrcv
- You wrote: ctt
- You wrote: huangcheng
- You wrote: end
UNIX环境高级编程——system V消息队列的更多相关文章
- UNIX环境高级编程——System V 共享内存区
共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最 ...
- UNIX环境高级编程——system V信号量
1. 信号量(semaphore)主要用于保护临界资源.进程可以根据它判断是否能访问某些共享资源.信号量除了用于访问控制外,还可用于进程同步,也就是进程间通信.2. 信号量分类:a. 二值信号量: 信 ...
- UNIX环境高级编程——system函数
system函数 功能:调用fork产生子进程,由子进程来调用:/bin/sh -c command来执行参数command所代表的命令,阻塞当前进程直到command命 令执行完毕. int sys ...
- linux c编程:System V消息队列一
消息队列可以认为是一个消息链表,System V 消息队列使用消息队列标识符标识.具有足 够特权的任何进程都可以往一个队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息.在某个进 ...
- (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (十二) 一起学 Unix 环境高级编程 (APUE) 之 进程间通信(IPC)
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- 《UNIX环境高级编程(第3版)》
<UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison ...
- (八) 一起学 Unix 环境高级编程 (APUE) 之 信号
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
随机推荐
- 整理spring定时器corn表达式
1.结构 corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份 2.各字段的含义 字段 允许值 允许的特殊字符 秒 0~59 - * / 分 0~59 - * / ...
- 吐槽:【计算机网络与通信】.张元.高清文字版.pdf
看了这本书的pdf,发现了一处错误,瞬间就不想再看了.新下载了谢希仁老师的<计算机网络>.
- git提交项目常用命令及git分支的用法
1.第一步首先从git托管平台clone项目,我这里就使用idea为例: 填写git的url与存放本地目录名及项目名 2.如果你对项目进行了一些修改,就可以执行git命令,进行提交. 有两种方 ...
- 网络安全之在Kali Linux上安装Openvas
本文目录: 一.解决和配置更新源问题 二.安装Openvas 三.自定义登陆密码 四.升级Openvas 五.查看Openvas运行情况 六.修改OpenVAS远程链接 =============== ...
- android自定义View之3D索引效果
效果图: 我的小霸王太卡了. 最近工作比较忙,今天搞了一下午才搞出来这个效果,这种效果有很多种实现方式,最常见的应该是用贝塞尔曲线实现的.今天我们来看另一种不同的实现方式,只需要用到 canvas.s ...
- Android简易实战教程--第四十九话《满屏拖动的控件》
今天做个有意思的效果吧,控件的拖拽,简单实用,逻辑清晰点3分钟看完. 说的很高大上,其实就是拖动Button按钮跟着鼠标位置满手机屏幕跑罢了. 直接上简单的代码吧: public class Main ...
- ROS探索总结(十九)——如何配置机器人的导航功能
1.概述 ROS的二维导航功能包,简单来说,就是根据输入的里程计等传感器的信息流和机器人的全局位置,通过导航算法,计算得出安全可靠的机器人速度控制指令.但是,如何在特定的机器人上实现导航功能包的功能, ...
- 理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息
本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(3)--不总是参数嗅探的错 前面已经提到过关于存储过程在SSMS中运行很 ...
- 【完整的App项目】颖火虫笔记
这是本人花大概一个星期开发出来的一款App,这是一款类似印象笔记的App,随时记录您的生活点滴.首先说一下自己为何要开发这款App,因为自己手机系统自带的笔记应用功能太low,界面不够漂亮,所以自己就 ...
- EBS开发技术之trace
trace的目的 trace主要是用于程序调优,优化,程序bug调试,程序运行系统情况跟踪 trace步骤 1.并发定义中,勾上"启用跟踪" 2.提交一个请求,得到请求编号 注意: ...