Linux的消息队列(queue)实质上是一个链表, 它有消息队列标识符(queue ID). msgget创建一个新队列或打开一个存在的队列; msgsnd向队列末端添加一条新消息; msgrcv从队列中取消息, 取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息.

1. 标识符(des)和键(key):

消息队列, 信号量和共享存储段, 都属于内核中的IPC结构, 它们都用标识符来描述. 这个标识符是一个非负整数, 与文件描述符不同的是, 创建时并不会重复利用通过删除回收的整数, 而是每次+1, 直到整数最大值回转到0.

标识符是IPC对象的内部名, 而它的外部名则是key(键), 它的基本类型是key_t, 在头文件<sys/types.h>中定义为长整型. 键由内核变换成标识符.

2. 消息队列状态msqid_ds:

每个消息队列都有一个msqid_ds结构与其关联:

struct msqid_ds
  {
    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 */
};

3. 由路径名和项目ID产生一个key:

如果客户进程和服务器进程认同一个路径名和项目ID(0~255间的字符值), 接着调用ftok将这两个值变换为一个key.

原型: key_t ftok(const char *path, int id); 
头文件: <sys/ipc.h> 
返回值: 成功则返回key, 出错则返回(key_t)-1. 
参数: path参数必须引用一个现存文件. 当产生key时 只使用id参数的低8位. 
说 明: 如果两个路径名引用两个不同的文件, 对这两个路径名调用ftok通常返回不同的key. 但是, 因为i节点号和key通常存放在长整型中, 于是创建key时可能会丢失信息. 这意味着, 如果使用同一项目ID, 那么对于不同文件的两个路径名可能产生相同的key. 该函数的工作方式为: 
按给定的路径名取得其stat结构. 
从该结构中取出部分st_dev和st_ino字段, 与项目ID组合起来. 
4. 创建/打开消息队列:

msgget可以创建一个新队列或打开一个存在的队列.

原型: int msgget(key_t key, int flag); 
头文件: <sys/msg.h> 
返回值: 成功则返回消息队列ID, 出错则返回-1. 
参数: 
key: 消息队列的key值. 
flag: 标志位. 
说明: 
创建队列有两种方法: 
key是IPC_PRIVATE. 
key当前未与特定类型的IPC结构相结合, 并且flag中指定了IPC_CREAT位. 
初始化msqid_ds成员: 
ipc_perm中的mode成员按flag进行设置. 
msg_qnum, msg_lspid, msg_lrpid, msg_stime和msg_rtime都设置为0. 
msg_ctime设置为当前时间. 
msg_qbytes设置为系统限制值. 
5. 消息队列的垃圾桶函数:

msgctl类似于驱动程序中的ioctl函数, 可对消息队列执行多种操作.

原型: int msgctl(int msqid, int cmd, struct msgqid_ds *buf); 
头文件: <sys/msg.h> 
返回值: 成功则返回0, 出错则返回-1. 
参数: cmd参数说明对msqid指定的队列要执行的命令: 
IPC_STAT: 取此队列的msqid_ds结构, 并将它存放在buf指向的结构中. 
IPC_SET: 按由buf指向结构中的值, 设置与此队列相关结构中的msg_perm.uid, msg_perm.gid, msg_perm.mode和msg_qbytes. 该命令只有下列两种进程可以执行: 
有效用户ID等于msg_perm.cuid或msg_per.uid. 
具有超级用户特权的进程. 
IPC_RMID: 从系统中删除该消息队列以及仍在该队列中的所有数据. 执行权限同上. 
6. 将数据放到消息队列:

调用msgsnd将数据放到消息队列中.

原型: int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag); 
头文件: <sys/msg.h> 
返回值: 成功则返回0, 出错则返回-1. 
说 明: 可以定义一个消息结构, 结构中带类型, 这样就可用非先进先出顺序取消息了. 当msgsnd成功返回, 与消息队列相关的msqid_ds结构得到更新, 以标明发出该调用的进程ID(msg_lsqid), 进行该调用的时间(msg_stime), 并指示队列中增加了一条消息(msg_qnum). 
7. 从消息队列中取消息:

调用msgrcv将从消息队列中取消息.

原型: ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag); 
头文件: <sys/msg.h> 
返回值: 成功则返回消息的数据部分的长度, 出错则返回-1. 
参数: 
ptr: 指向一个长整型数(返回的消息类型存放在其中), 跟随其后的是存放实际消息数据的缓冲区. 
nbytes: 数据缓冲区的长度. 若返回的消息大于nbytes, 且在flag中设置了MSG_NOERROR, 则该消息被截短. 
type: 
type == 0: 返回队列中的第一个消息. 
type > 0: 返回队列中消息类型为type的第一个消息. 
type < 0: 返回队列中消息类型值小于或等于type绝对值的消息, 如果这种消息有若干个, 则取类型值最小的消息. 
说明: 当msgrcv成功返回时, 与消息队列相关的msqid_ds结构被更新, 以指示调用者的进程ID(msg_lrpid), 调用时间(msg_rtime)和队列中的消息数(msg_qnum)减1. 
四、消息队列应用实例
消息队列应用相对较简单,下面实例基本上覆盖了对消息队列的所有操作,同时,程序输出结果有助于加深对前面所讲的某些规则及消息队列限制的理解。

#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
void msg_stat(int,struct msqid_ds );
main()
{
int gflags,sflags,rflags;
key_t key;
int msgid;
int reval;
struct msgsbuf{
        int mtype;
        char mtext[1];
    }msg_sbuf;
struct msgmbuf
    {
    int mtype;
    char mtext[10];
    }msg_rbuf;
struct msqid_ds msg_ginfo,msg_sinfo;
char* msgpath="/unix/msgqueue";
key=ftok(msgpath,'a');
gflags=IPC_CREAT|IPC_EXCL;
msgid=msgget(key,gflags|00666);
if(msgid==-1)
{
    printf("msg create error\n");
    return;
}
//创建一个消息队列后,输出消息队列缺省属性
msg_stat(msgid,msg_ginfo);
sflags=IPC_NOWAIT;
msg_sbuf.mtype=10;
msg_sbuf.mtext[0]='a';
reval=msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);
if(reval==-1)
{
    printf("message send error\n");
}
//发送一个消息后,输出消息队列属性
msg_stat(msgid,msg_ginfo);
rflags=IPC_NOWAIT|MSG_NOERROR;
reval=msgrcv(msgid,&msg_rbuf,4,10,rflags);
if(reval==-1)
    printf("read msg error\n");
else
    printf("read from msg queue %d bytes\n",reval);
//从消息队列中读出消息后,输出消息队列属性
msg_stat(msgid,msg_ginfo);
msg_sinfo.msg_perm.uid=8;//just a try
msg_sinfo.msg_perm.gid=8;//
msg_sinfo.msg_qbytes=16388;
//此处验证超级用户可以更改消息队列的缺省msg_qbytes
//注意这里设置的值大于缺省值
reval=msgctl(msgid,IPC_SET,&msg_sinfo);
if(reval==-1)
{
    printf("msg set info error\n");
    return;
}
msg_stat(msgid,msg_ginfo);
//验证设置消息队列属性
reval=msgctl(msgid,IPC_RMID,NULL);//删除消息队列
if(reval==-1)
{
    printf("unlink msg queue error\n");
    return;
}
}
void msg_stat(int msgid,struct msqid_ds msg_info)
{
int reval;
sleep(1);//只是为了后面输出时间的方便
reval=msgctl(msgid,IPC_STAT,&msg_info);
if(reval==-1)
{
    printf("get msg info error\n");
    return;
}
printf("\n");
printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
printf("number of messages in queue is %d\n",msg_info.msg_qnum);
printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes);
//每个消息队列的容量(字节数)都有限制MSGMNB,值的大小因系统而异。在创建新的消息队列时,//msg_qbytes的缺省值就是MSGMNB
printf("pid of last msgsnd is %d\n",msg_info.msg_lspid);
printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid);
printf("last msgsnd time is %s", ctime(&(msg_info.msg_stime)));
printf("last msgrcv time is %s", ctime(&(msg_info.msg_rtime)));
printf("last change time is %s", ctime(&(msg_info.msg_ctime)));
printf("msg uid is %d\n",msg_info.msg_perm.uid);
printf("msg gid is %d\n",msg_info.msg_perm.gid);
}

消息队列状态:struct msqid_ds的更多相关文章

  1. Linux进程间通信(二) - 消息队列

    消息队列 消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据. 消息队列和之前讨论过的管道和FIFO有很大的区别,主要有以下两点(管道请查阅我的另一篇文章 ...

  2. Linux:进程通信之消息队列Message实例

    /*send.c*/ /*send.c*/ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h&g ...

  3. 消息队列内核结构和msgget、msgctl 函数

    一.消息队列 1.消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法 2.每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值 3.消息队列与管道不同的是,消息队列是基于 ...

  4. 第二十五章 system v消息队列(一)

    IPC对象的持续性 随进程持续 :一直存在直到打开的最后一个进程结束.(如pipe和FIFO) 随内核持续 :一直存在直到内核自举(内核自举就是把主引导记录加载到内存,并跳转执行这段内存)或显示删除( ...

  5. IPC 进程间通信方式——消息队列

    消息队列 消息队列是内核中的一个链表 用户进程将数据传输到内核后,内核重新添加一些如用户ID.组ID.读写进程的ID和优先级等相关信息后并打包成一个数据包称为消息 允许一个或多个进程往消息队列中读写消 ...

  6. 进程间通信——XSI IPC之消息队列

    进程间通信XSI IPC有3种:消息队列.共享内存.信号量.它们之间有很多相似之处,但也有各自的特殊的地方.消息队列作为其中比较简单的一种,它会有些什么东西呢,来一起探讨探讨.. 消息队列结构 消息队 ...

  7. IPC进程间通信---消息队列

    消息队列 消息队列:消息队列是一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识.与管道不同的是消息队 列存放在内核中,只有在内核重启(即操作系统重启)或者显式地删除一个消息队列时,该消息队 ...

  8. 【windows 操作系统】进程间通信(IPC)简述|无名管道和命名管道 消息队列、信号量、共享存储、Socket、Streams等

    一.进程间通信简述 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进 ...

  9. linux 进程间消息队列通讯

    转自:http://blog.csdn.net/lifan5/article/details/7588529 http://www.cnblogs.com/kunhu/p/3608589.html 前 ...

随机推荐

  1. [转]使用 ssh -R 穿透局域网访问内部服务器主机,反向代理 无人值守化

    原文: https://www.cnblogs.com/phpdragon/p/5314650.html ----------------------------------------------- ...

  2. Mongoose Connection best practice

    There is often quite a lot of confusion about how best to set up a database connection with Mongoose ...

  3. (纪录片)统计的乐趣 The Joy of Stats (2010)

    简介: 导演: 丹·希尔曼主演: Hans Rosling类型: 纪录片官方网站: www.bbc.co.uk/programmes/b00wgq0l制片国家/地区: 英国语言: 英语上映日期: 20 ...

  4. Android 基本 Jackson Marshalling(serialize)/Unmarshalling(deserialize)

    本文内容 基本 Jack Marshalling 忽略属性 忽略 Null 字段 改变字段名字 基本 Jackson Marshalling 把 JSON 解析成 JsonNode Unmarshal ...

  5. oracle 复制表结构表数据

    create table Uc_t_Department3 as (select * from Uc_t_Department where 1=2);insert into Uc_t_Departme ...

  6. javascript简写精练

    一.算术运算符 var n = 5,v; 1.n = n*5; 2.n*=5; 同 n = n*5 二.条件判断 var b = true; 1.if (!false) { alert('true') ...

  7. 微软BI 之SSRS 系列 - 报表中分组聚合中处理不规则层次结构的技巧(没有子元素的时候不展开, 删除+符号)

    分组聚合的展开和收起效果在SSRS Report中非常常用,并且有时还要处理一些比较特别的情况.比如分组合并时有的层次结构是不规则的,有的组有两层,遇到这种情况应该如何处理?   注意到下面的这个需求 ...

  8. git ingore添加忽略文件无较的解决方法

    一.启动Git Bash并切换到项目目录下 二.执行下列语句: git rm -r --cached . git add . git commit -m 'update .gitignore' 三.O ...

  9. vs2015使用Apache Cordova用JavaScript来访问本地设备的功能,比如摄像头、加速计

    看到下面这张图就代表着我VS2015 跨平台Moblie开发工具安装成功了. 上周安装成功后本想一睹跨平台开发的乐趣,可是一直找不到合适的入口.这周又来捯饬一下结果发现了一个入口.于是来写一个Hell ...

  10. hadoop multipleoutputs

    http://grepalex.com/2013/05/20/multipleoutputs-part1/ http://grepalex.com/2013/07/16/multipleoutputs ...