范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

进程间通信(IPC)系列Posix消息队列

  消息队列是 Linux IPC 中很常用的一种通信方式,今天分析一下Posix消息队列,本文中所讲的消息队列均为Posix消息队列。什么是 Posix 消息队
  消息队列可以认为它是一个消息链表,有足够写权限的进程可以往队列中发送消息,有足够读权限的进程可以往队列中接收消息。
  每个消息都是一个记录,它由发送者赋予一个优先级。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这也就说明消息队列具有随内核的持续性,也就是说进程关闭后,消息队列依然存在,除非内核重新自举。
  Posix 消息队列有如下特点:对Posix消息队列的读总是返回优先级最高最早的消息。当往空的消息队列中放置一个消息时,Posix消息队列允许产生一个信号或者启动一个接收线程。
  Posix 消息队列中的每条消息通常具有以下属性:一个表示优先级的整数;消息的数据部分的长度;消息数据本身;
  消息队列的基本操作
  打开或创建一个 posix 消息队列操作接口
  mqd_t  mq_open(const  char  *name, int  oflag, mode_t  mode, struct mq_attr *attr);
  Link with -lrt.
  参数 name 为 posix IPC 名字, 即将要被打开或创建的消息队列对象,为了便于移植,需要指定为"/name"的格式。
  参数oflag必须要有O_RDONLY(只读)、标志O_RDWR (读写), O_WRONLY(只写)之一,除此之外还可以指定 O_CREAT(没有该对象则创建)、O_EXCL(如果 O_CREAT 指定,但 name 不存在,就返回错误),O_NONBLOCK(以非阻塞方式打开消息队列,在正常情况下mq_receive和mq_send 函数会阻塞的地方,使用该标志打开的消息队列会返回 EAGAIN 错误)。
  当操作一个新队列时,使用 O_CREAT 标识,此时后面两个参数需要被指定,参数 mode 为指定权限位,attr 指定新创建队列的属性。
  关闭进程描述符操作接口int mq_close(mqd_t mqdes);
  关闭之后告诉进程不在使用该描述符,但消息队列不会从系统中删除。
  系统中删除某个消息队列操作接口int mq_unlink(const char *name);
  参数为mq_open()函数第一个参数,调用该接口后删除会马上发生,即使该队列的描述符引用计数仍然大于0。
  关于消息队列中设置和和获取消息队列属性接口mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr); mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
  每个消息队列有四个属性, mq_getattr返回所有的这些属性, mq_setattr设置其中的某个属性
  消息队列的消息具体属性如下struct mq_attr { long mq_flags; /* Flags: 0 or O_NONBLOCK */ long mq_maxmsg; /* Max. # of messages on queue */ long mq_msgsize; /* Max. message size (bytes) */ long mq_curmsgs; /* # of messages currently in queue */ }
  指向mq_attr的指针可以作为mq_open函数的第四个参数传递, 从而在创建队列初就设置好每个消息的最大长度和允许存在的最大消息数量, 另外两个成员被忽略。
  mq_setattr给所指定队列设置属性, 但是只使用由attr指向的mq_attr结构的mq_flags成员, 以设置或清除非阻塞标志, 其他三个成员则被忽略(其中两个只能在创建队列时指定, 还有一个及时获取)。当然, mq_setattr的最后一个参数用于接收之前的属性和当前状态
  向消息队列放置和取走消息的操作接口int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);  ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
  参数msg_ptr为指向消息的指针。
  msg_len为消息长度,该值不能大于属性值中mq_msgsize的值。
  msg_prio为优先级,消息在队列中将按照优先级大小顺序来排列消息。
  如果消息队列已满,mq_send()函数将阻塞,直到队列有可用空间再次允许放置消息或该调用被信号打断;如果O_NONBLOCK被指定,mq_send()那么将不会阻塞,而是返回EAGAIN错误。
  如果队列空,mq_receive()函数将阻塞,直到消息队列中有新的消息;如果O_NONBLOCK被指定,mq_receive()那么将不会阻塞,而是返回EAGAIN错误。消息队列的原理分析
  消息队列的初始化static int __init init_mqueue_fs(void) {         ...          //注册消息队列文件系统         error = register_filesystem(&mqueue_fs_type);          //构建struct vfsmount结构主要是获取文件系统的super_block对象与根目录的inode与dentry对象,并将这些对象加入到系统链表         if (IS_ERR(mqueue_mnt = kern_mount(&mqueue_fs_type))) {             ...         }          queues_count = 0;         spin_lock_init(&mq_lock);          return 0;  out_filesystem:  out_sysctl:          return error; }  __initcall(init_mqueue_fs);
  消息队列文件系统初始化很简单,主要工作如下:注册文件系统,把 mqueue_fs_type 加入到 file_systems 链表中。构建struct vfsmount结构,把获取的文件系统的super_block对象与根目录的 inode 与 dentry 对象,并将这些对象加入到系统链表中。
  mq_open 接口分析asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, struct mq_attr __user *u_attr) {          ...           //获取一个未使用的文件描述符         fd = get_unused_fd();          mutex_lock(&mqueue_mnt->mnt_root->d_inode->i_mutex);         //获取一个名字为name的dentry结构           dentry = lookup_one_len(name, mqueue_mnt->mnt_root, strlen(name));          mntget(mqueue_mnt);         //若是新创建         if (oflag & O_CREAT) {                 if (dentry->d_inode) {  /* entry already exists */                         audit_inode(name, dentry);                         error = -EEXIST;                         if (oflag & O_EXCL)                                 goto out;                         //若已经存在,则直接打开file                         filp = do_open(dentry, oflag);                 } else {                         //创建一个file结构,把inode和dentry与之关联                         filp = do_create(mqueue_mnt->mnt_root, dentry,                                                 oflag, mode, u_attr);                 }         } else { //否则,直接获取                 error = -ENOENT;                 if (!dentry->d_inode)                         goto out;                 audit_inode(name, dentry);                 filp = do_open(dentry, oflag);         }          if (IS_ERR(filp)) {                 error = PTR_ERR(filp);                 goto out_putfd;         }          //给描述符设置close_on_exec标志         set_close_on_exec(fd, 1);          //文件描述符与file进行关联         fd_install(fd, filp);          goto out_upsem;          ...          return fd; }
  mq_open 的操作很简单,操作如下:获取一个未使用的文件描述符 fd;根据参数name获取一个 dentry;根据 oflag 表示判断是否是新创建还是使用已存在的 file,若新创建,则生成一个 file 结构,同时与 fd 、 inode、dentry 进行关联;否则打开一个已存在的file。
  mq_send 接口分析
  当使用 mq_send 发送消息时,比如如下调用,mq_send(mqd, msg, msg_len, msg_prio)
  发送消息时,最终会调用如下函数mq_timedsend(mqd, msg, msg_len, msg_prio, NULL)
  asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout) {  ...  //获取file结构 filp = fget(mqdes); if (unlikely(!filp)) goto out;  inode = filp->f_path.dentry->d_inode; //获取inode 下的 mqueue_inode_info info = MQUEUE_I(inode);  //把用户传来的消息转换成内核消息链表,若用户消息长度大于PAGE_SIZE,则链表结构为 msg_msg-> msg_msgseg -> msg_msgseg 每个节点都已一个页大小,除了最后一个节点 //若用户消息大小小于PAGE_SIZE,链表只有一个节点 msg_msg  // msg_ptr 指向链表头节点 msg_msg msg_ptr = load_msg(u_msg_ptr, msg_len); //msg_msg 中记录消息的总长度,和 消息优先级 msg_ptr->m_ts = msg_len;  msg_ptr->m_type = msg_prio;  spin_lock(&info->lock); //若消息数量达到最大值 if (info->attr.mq_curmsgs == info->attr.mq_maxmsg) { //若非阻塞,则返回 if (filp->f_flags & O_NONBLOCK) { spin_unlock(&info->lock); ret = -EAGAIN; //若超时时间小于0,则返回超时时间 } else if (unlikely(timeout < 0)) { spin_unlock(&info->lock); ret = timeout; } else { //阻塞调用,则进程休眠,把wait加入到info中的SEND等待队列中 wait.task = current; wait.msg = (void *) msg_ptr; wait.state = STATE_NONE; ret = wq_sleep(info, SEND, timeout, &wait); }  } else { //若info 接收队列中有阻塞的进程,则把要发送的数据挂到阻塞的进程的消息节点上,然后唤醒阻塞的接收进程 receiver = wq_get_first_waiter(info, RECV); if (receiver) { pipelined_send(info, msg_ptr, receiver); } else { //把消息挂到消息队列上,并进行通知 msg_insert(msg_ptr, info); __do_notify(info); } inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; spin_unlock(&info->lock); ret = 0; }  return ret; }
  sys_mq_timedsend 功能如下:发送消息前先把用户消息转换成消息链表。若消息队列满了,则根据条件进行是否阻塞。消息队列未满,若接收队列中存在阻塞的接收进程,则把要发送的数据挂到阻塞的进程的消息节点上,然后唤醒阻塞的接收进程;否则把消息加到消息队列中。
  mq_receive 接口分析
  当使用 mq_receive 接收消息时,比如如下调用,
  接收消息时,最终会调用如下函数mq_timedreceive(mqd, msg, msg_len, &msg_prio, NULL)具体实现如下asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout) {          ...          //获取file结构         filp = fget(mqdes);          inode = filp->f_path.dentry->d_inode;         //获取inode 下的 mqueue_inode_info         info = MQUEUE_I(inode);         audit_inode(NULL, filp->f_path.dentry);          spin_lock(&info->lock);         //消息队列当前没有消息         if (info->attr.mq_curmsgs == 0) {                 //若非阻塞,则返回                 if (filp->f_flags & O_NONBLOCK) {                         spin_unlock(&info->lock);                         ret = -EAGAIN;                         msg_ptr = NULL;                 //若超时时间小于0,则返回超时时间                 } else if (unlikely(timeout < 0)) {                  } else {                         //阻塞调用,则进程休眠,把wait加入到info中的RECV等待队列中                         wait.task = current;                         wait.state = STATE_NONE;                         ret = wq_sleep(info, RECV, timeout, &wait);                         msg_ptr = wait.msg;                 }         } else {                 //从消息队列的最高优先级中获取一个消息                 msg_ptr = msg_get(info);                  inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;                  //由于从消息队列中已经取出一个消息了,有剩余的空间,因此把等待SEND队列上的进程的消息挂到消息队里中,然后唤醒发送消息的进程                 pipelined_receive(info);                 spin_unlock(&info->lock);                 ret = 0;         }         if (ret == 0) {                 ret = msg_ptr->m_ts;                 //把消息拷贝到用户态                 if ((u_msg_prio && put_user(msg_ptr->m_type, u_msg_prio)) ||                         store_msg(u_msg_ptr, msg_ptr, msg_ptr->m_ts)) {                         ret = -EFAULT;                 }                 //释放消息空间                 free_msg(msg_ptr);         } out_fput:         fput(filp); out:         return ret; }
  sys_mq_timedreceive 功能如下:获取消息前,先判断消息队列中是否有消息,若没有,则根据条件进行是否阻塞。若消息队列中有消息,则从消息队列的获取一个最高优先级的消息。唤醒因为消息队列满而阻塞的发送进程。把消息拷贝到用户缓冲区中。
  mq_unlink 接口分析asmlinkage long sys_mq_unlink(const char __user *u_name) {         ...         //引用计数减1,删除目录项         err = vfs_unlink(dentry->d_parent->d_inode, dentry);          return err; }
  该函数作用很简单,就是减少引用计数,删除目录项。
  经过上述的分析,有关消息队列的内存结构可以总结如下:

古村寻踪大山深处藏古村泉水飞溅似珍珠天上二十八宿,珍珠二十八泉。淄博市博山区源泉镇的珍珠村建于明末清初,当初为了躲避战乱,附近山民迁居于此。8月14日,一片欢声笑语让这颗珍珠更加璀璨,连日的降雨使得山上泉水相继涌现,中国大手笔!匈牙利双喜临门赚大了近日,匈牙利真的是双喜临门!第一喜是俄罗斯送的俄匈已经达成协议,俄超大规模的供气,并且过路费也给了乌克兰。在德国为了省能源都开始烧劈柴洗澡只洗四个部位和火葬场5人合炉火花之际,匈牙山西地质博物馆一日游地质博物馆大门恐龙化石大自然的鬼斧神工大家伙人还不少漂亮大家伙大家伙今日有空,约同事一起逛一下地质博物馆,不用预约,需五日核酸检测报告,健康码。暑假期间人很多,大部分是家长带小孩子探幽鬼谷洞鬼谷大仙披门先师修真之所,王利隐居处鬼谷洞,位于玉泉街道三桥村清溪山北面的半山腰上。这里山势陡峭,凌空交合,形成一条很长的峡谷。古时候,生长在这里的山民,沿袭着巴人洞葬的奔丧习俗。死了的人,便将尸体搬到峡谷两边山腰的晒晒我的退休生活60岁退休后,我回到了天津,第一件事就是参加了夕阳红骑行队。日常,与老伙伴们结队跨上自行車,在城里的街道和乡间公路上飞驰。我们的身影遍布津沽大地,逛公园,看展览,游寺庙特别是春秋两皖北往事之秦山寺作者赵汗青刘欣华一山一寺一苦僧,数年坚守敲磬钟。若非朱棣来擒山,寺名仍然叫卧龙。题秦山寺2022年8月10日上午,在表兄丙奇的带领下,我们前往宿州市埇桥区曹村镇西境大山里的秦山寺采古村寻踪大山深处藏古村泉水飞溅似珍珠天上二十八宿,珍珠二十八泉。淄博市博山区源泉镇的珍珠村建于明末清初,当初为了躲避战乱,附近山民迁居于此。8月14日,一片欢声笑语让这颗珍珠更加璀璨,连日的降雨使得山上泉水相继涌现,中国大手笔!匈牙利双喜临门赚大了近日,匈牙利真的是双喜临门!第一喜是俄罗斯送的俄匈已经达成协议,俄超大规模的供气,并且过路费也给了乌克兰。在德国为了省能源都开始烧劈柴洗澡只洗四个部位和火葬场5人合炉火花之际,匈牙经常喝小米粥,到底是会升高血糖,还是能降低血糖?告诉你真相王先生今年50岁,是一名糖尿病患者,平时血糖控制得还算不错,但有一件事却成为了他的困扰,就是每次喝完小米粥,他的餐后血糖就会有所升高。一开始以为只是小问题,可最近情况逐渐加重,本来经常喝小米粥,到底是会升高血糖,还是能降低血糖?告诉你真相王先生今年50岁,是一名糖尿病患者,平时血糖控制得还算不错,但有一件事却成为了他的困扰,就是每次喝完小米粥,他的餐后血糖就会有所升高。一开始以为只是小问题,可最近情况逐渐加重,本来华为概念第一龙头,多重形态叠加,股价7。2元,能否冲高回暖?技术派的看过来,这货完全可以用技术解读。头肩底启明星仙人指路同时出现,操盘的人一定是个技术高手,股价已经经历了86。10的跌幅,如今只剩7。20元,能否成功变盘,拭目以待。核心概念
中国天眼取得系列重要科学成果图来源新华网1月5日,在中科院国家天文台举行的新闻发布会现场。1月5日,中国科学院在中科院国家天文台举行新闻发布会,发布中国天眼(FAST)高质量开放运行取得的系列重要科学成果,其中安卓苹果的手下败将曾是全球第二大手机系统,2022正式关停又一个手机操作系统宣布关停。从2022年1月4日起,黑莓(BlackBerry)正式终止对搭载BlackBerryOS设备提供支持。换句话说,所有不是运行安卓软件的旧黑莓设备将变成IMSCARED不是你在玩游戏,游戏也不是在玩你友情提示,本文包含剧透写在前面恐怖游戏一直以来都是一个套路,像鬼屋一样,厂商推出游戏,玩家花钱被吓,甚至和鬼屋一样的是,有的游戏,玩家不但不会感到恐惧,还会因为游戏的敷衍而深感不值上当了没办法退货!Doinb透露训练赛,有些顶级选手打得很差前言S12赛季的比赛马上就要开始了,相信绝大多数的玩家都关注了最近一段时间的休赛期,各大战队都进入到了最紧张的训练阶段。这几天里,几乎每一名职业选手都保持着高强度的rank,在每天卡普空不介意?铁杆玩家重制了生化危机代号维罗妮卡2020年4月份的时候,博士和大家分享了文章生化危机代号维罗妮卡重制有戏?卡普空的问卷调查暗藏玄机,综合了大量爆料和线索之后,我们知道卡普空会继续推出生化危机的重制作品和正统续作。Chovy韩服被抓爆,赛后狂喷辅助Zhuo十五分钟这眼插得有问题前言众所周知,TES辅助选手Zhuo,因为在德杯的比赛中发挥失利,多盘比赛都没有展现出较为强力的效果,虽然最后TES还是成功夺冠,但这位选手依旧受到了不少观众的质疑。而在近日,Zh甲状腺结节囊肿乳腺增生都有通络散结的三样东西冬季正是吃橘子的季节,我们看到橘子有些桔子上还带着翠绿的叶子,桔子的外形呈扁圆形或近圆球形,它的果皮有的很薄,有的很厚,桔子的果肉是一瓣一瓣分开的,它的味道吃起来又酸又甜,很开胃,末日来袭1月18日全平台公测近年来,绝境求生系的影视和游戏作品大受年轻人喜爱,灾难降临的沉浸式冲击,躲避危险的紧张感,生死拼杀的感官刺激,面临抉择的人性与未来世界的不确定走向,都刚刚好戳中人们的猎奇神经,让观操控灵敏平台多元,谷粒金刚2Pro游戏手柄体验喜欢玩游戏的小伙伴肯定少不了搭配一款设计先进,操控灵敏的游戏手柄。相比于键盘鼠标来说,手柄的操控感和反馈效果无疑会更为出色。我就选择了这样一款谷粒金刚2Pro游戏手柄,它支持swi娃可以穿别人送的旧衣服,但这4样东西最好拒绝现在社会越来越发达,家庭条件也越来越好,每个家长都对自己的孩子开销特别舍得,但是又觉得孩子长大很快,没必要什么东西都买,亲戚家的孩子刚好一些没有怎么用的旧衣服,奶瓶,玩具,勺子,奶宝宝这几件东西最好不要送人,就算是别人送自己也要委婉拒绝导语孩子们小时候长得很快。不管他们吃什么,穿什么,玩什么,很多东西买了之后很快就不合适了。以服装为例,一件衣服穿两次后就不能穿了。很多东西都是浪费的。有些家长就是因为怕浪费,会考虑梦幻西游嘉年华新召唤兽神秘面纱揭开,连续200次师门空车爱生活,爱梦幻,关注兕大王,每期都有新资讯。新召唤兽真面目亮相大家还记得前面嘉年华晚会公布的新召唤兽剪影吗?现在已经公布啦!一起来先睹为快吧!这就是新召唤兽觉醒涂山雪,这是初阶的形三国杀隐藏女将上线测试服,神秘身份大揭秘三国杀两位隐藏女将上线测试服,神秘身份大揭秘大家好!好久不见,小蛙在这里给大家赔个不是先。三国杀移动版3999测试服已于22。01。04正式上线(4。0还会远吗?),严教的钟妈张菖玉兔二号发现神秘月宫?关于地外文明我们始终保持这敬畏与好奇之心,在我国古代也有对月亮的种种传说。吴桂伐树,嫦娥奔月也是家喻户晓,每逢中秋我们品尝月饼,仰望天空。我们是真的对那个熟悉的月亮足够了解吗?根据红白机古巴战士共有多少子弹,神秘BCKN弹你都见过吗?诺亚主机头条第14期经典游戏古巴战士可谓是红白机平台少数几个以历史真实事件改编的比较成功的游戏之一了。游戏以切格瓦拉和卡斯特罗作为主角,试图还原两位英雄人物在古巴战争中做出的杰出贡年报行情来袭,A股127家公司发布业绩预告,五家净利润同增3001月4日,容百科技(688005。SH)高开高走,截至下午收盘,报120。80元股,上涨4。52,总市值541。23亿元。银柿财经记者注意到,1月3日晚间,容百科技发布2021年度新诛仙手游新地图万剑山神秘现世,远古熊猫一族也亮相了就在12月30日,新诛仙手游的全新版本万剑归宗终于上线,随着新版本新玩法到来的同时,全新地图万剑山也随之开启,如梦如幻的风景,仙气十足的神秘蜀地中,似乎也暗藏着实力的精怪!接下来就未分配利润是什么意思?现金流与利润比,谁更重要?投资者不是慈善家,也不是公益大使。他们的目的很明确,那就是赚钱!无利可图的事,他们肯定不会干的。当然企业盈利以后,也会通过未分配利润来体现的。未分配利润是什么意思?企业的未分配利润面对弱旅,易建联玩嗨了,球霸刘权标一打五,徐昕单手劈扣文青海sir广东队上半场让易建联周鹏威姆斯胡明轩主打,拉开与同曦队比分,下半场让刘权标赵嘉仁徐昕主打,也不换人,似乎在增量打磨内线,以备不时之需,毕竟广东连输几场,急需涨士气。两队勇敢追梦!李磊启程飞赴苏黎世开启留洋生涯佩戴国足队徽口罩北京时间1月4日消息,瑞超球队草蜢官方草蜢新援中国国脚李磊于今日上午正式启程飞赴瑞士苏黎世。在上海浦东国际机场,多位国安球迷前来为李磊送行。期待李磊在草蜢勇敢追梦!离开之际,李磊接唐艺昕婚后还是这么美,张若昀真会疼媳妇唐艺昕,女,1987年12月9日出生于四川省,毕业于重庆大学美视电影学院表演系,中国内地女演员。2011年,出演个人首部电视剧后宫甄嬛传,正式进入演艺圈。先后出演了西游降魔篇百万新农村出身,无钱无势的赵丽颖是如何成为一线明星的呢?学历低出身差的赵丽颖凭什么当上了女总裁?她是如何从一个农村姑娘逆袭成为娱乐圈顶流的?她的资本之路可以追溯到2011年,新作陆贞传奇的大火,使赵丽颖迎来了一次里程碑式的飞跃。身为85