0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

rt-thread 优化系列(二) 之 同步和消息关中断分析

出出 来源:出出 作者:出出 2022-06-21 09:47 次阅读

前言

书接前文,上篇优化聊的是关中断操作,在很多地方过保护,导致关中断时间太久,可能引起其它中断不能及时响应。今天特意说说线程间同步和通信,分析一下它们是怎么影响关中断时间的,比起前文会有些深入分析。

从 rt_mq_send_wait 说起

为了方便谈问题先贴一段代码,这段代码是从 4.0.4 版本的 `rt_mq_send_wait` 函数中摘取的部分。

   /* disable interrupt */
   temp = rt_hw_interrupt_disable();
   /* get a free list, there must be an empty item */
   msg = (struct rt_mq_message *)mq->msg_queue_free;
   /* for non-blocking call */
   if (msg == RT_NULL && timeout == 0)
   {
       /* enable interrupt */
       rt_hw_interrupt_enable(temp);

       return -RT_EFULL;
   }

   /* message queue is full */
   while ((msg = (struct rt_mq_message *)mq->msg_queue_free) == RT_NULL)
   {
       /* reset error number in thread */
       thread->error = RT_EOK;

       /* no waiting, return timeout */
       if (timeout == 0)
       {
           /* enable interrupt */
           rt_hw_interrupt_enable(temp);

           return -RT_EFULL;
       }

       RT_DEBUG_IN_THREAD_CONTEXT;
       /* suspend current thread */
       rt_ipc_list_suspend(&(mq->suspend_sender_thread),
                           thread,
                           mq->parent.parent.flag);

       /* has waiting time, start thread timer */
       if (timeout > 0)
       {
           /* get the start tick of timer */
           tick_delta = rt_tick_get();

           RT_DEBUG_LOG(RT_DEBUG_IPC, ("mq_send_wait: start timer of thread:%s\n",
                                       thread->name));

           /* reset the timeout of thread timer and start it */
           rt_timer_control(&(thread->thread_timer),
                            RT_TIMER_CTRL_SET_TIME,
                            &timeout);
           rt_timer_start(&(thread->thread_timer));
       }

       /* enable interrupt */
       rt_hw_interrupt_enable(temp);

       /* re-schedule */
       rt_schedule();

       /* resume from suspend state */
       if (thread->error != RT_EOK)
       {
           /* return error */
           return thread->error;
       }

       /* disable interrupt */
       temp = rt_hw_interrupt_disable();

       /* if it's not waiting forever and then re-calculate timeout tick */
       if (timeout > 0)
       {
           tick_delta = rt_tick_get() - tick_delta;
           timeout -= tick_delta;
           if (timeout < 0)
               timeout = 0;
       }
   }

这段代码的大致流程是:关全局中断,取消息队列,如果没有空闲消息进入等待,将当前线程注册到消息队列等待线程列表,启动当前线程内置硬定时器(等待超时机制),开全局中断,执行任务调度,被唤醒后进行是超时唤醒还是消息队列空唤醒处理。

> 首先申明,这段代码在设置等待超时时间的情况下才有效,当第四个参数 timeout 为 0 的时候对本次分析无效。鉴于在中断回调函数中要求不能设置 timeout 值,也就是不能进行阻塞调用,但是本文章仍然讨论在中断回调函数中的阻塞调用情况,权作参考。下面就分两种情况分别分析。

非中断,线程中阻塞调用 `rt_mq_send_wait`

假设线程中调用执行了函数 `rt_mq_send_wait` 第四个参数 `timeout` 不为 0 。那么上面的代码一定执行 while 循环体。这个 while 循环暴露的多个问题不提,让我们把目光聚焦到启动线程定时器和开全局中断部分。
前言部分预先提醒了今天的主题是:启动超时等待定时器的操作有必要在关中断中吗?可不可以先开中断,然后启动定时器?

为了说明这个问题,让我们再设想一种使用情况,假设在线程中单纯的启动一个定时器,如下:

   rt_timer_t timer;
   timer = rt_timer_create("tim1", timer_timeout,
                           RT_NULL,  1000,
                           RT_TIMER_FLAG_PERIODIC);
   rt_timer_start(timer);

或者,

   int timeout = 1000;
   struct rt_thread *thread = rt_thread_self();
   rt_timer_control(&(thread->thread_timer),
                   RT_TIMER_CTRL_SET_TIME,
                   &timeout);
   rt_timer_start(&(thread->thread_timer));

如上用法我们会把它放到关中断里面吗?答案是不需要!
那么,`rt_mq_send_wait` 中的关中断是为了保护什么?同样是在线程中执行,同样的两句代码,同样的使用为什么被 rt_mq_send_wait 执行时就需要被关中断保护了?
执行这两句代码时,无论是发生普通中断,或者有任务调度切换到了其它线程,都不应该会影响到这个定时器被正常启动。至于说这个 `thread` 指针,在当前这个线程不会被强制删除的前提下, `thread` 指针一直有效。

所以说,启动超时等待定时器的操作**没**必要在关中断中。可以**先开中断,然后启动定时器**

中断,阻塞调用 rt_mq_send_wait

> 再次申明,实际使用中避免这种用法,这里仅仅做交流,并非使用建议。

和在线程中不一样的地方在于,线程中调用 `rt_mq_send_wait` 时 `thread` 指针*肯定*是当前线程;在中断中调用 rt_mq_send_wait ,因中断不定什么时候出现, `thread` 指针可能是任意被创建的(有机会进入运行态的)线程。

为了不失一般性,我们再次假设,这个中断优先级比较低,可能被另外一个中断嵌套。而且假设在执行 `rt_timer_control` 和 `rt_timer_start` 时被其它中断打断。这种极端情况下会出现什么结果以及影响?
1. 中断中再次被中断,同时有阻塞操作。这个定时器会被两个地方同时使用,鉴于 `rt_timer_control` 和 `rt_timer_start` 内部也有中断,即便在两个函数中间出现新中断,在新中断中这个定时器也可以被正常配置而不影响在新中断中的定时任务!
2. 中断中出现 SysTick 中断,然后有任务调度。因为前边中断中使用的定时器是某个不确定线程的,这时候出现任务调度,新线程是另外一个不确定线程,而且可以保证的是肯定是另一个不同的线程。这个新的进程会有可能强制删除前一个线程吗?

说到这里,我们会发现,***即便是在中断里阻塞调用 `rt_mq_send_wait` 函数, `rt_timer_control` 和 `rt_timer_start` 操作不需要被关中断保护!***

小结

综上分析,可以*先开中断,然后启动定时器,最后进行任务调度*。而不用担心数据共享的问题。
类似用法的函数很多,ipc.c 中每一个 rt_xxx_send 和 rt_xxx_recv rt_xxx_take 都是一个模式。

timeout 的一些讨论(无关中断)

开篇贴的代码,考虑到了被其它线程唤醒的情况,假如等待阻塞中,等待时间还没到但是其它线程特意唤醒了它,会执行下面这段代码

       /* if it's not waiting forever and then re-calculate timeout tick */
       if (timeout > 0)
       {
           tick_delta = rt_tick_get() - tick_delta;
           timeout -= tick_delta;
           if (timeout < 0)
               timeout = 0;
       }

这段代码本身没多少问题,有问题的是接下来判断消息队列是否有空闲消息,如果没有进入下一次阻塞中。
判断是否有空闲消息后,判断 timeout 是否为 0 ,为 0 说明等待时间已经超时,开中断退出返回。这里判断 timeout == 0 是可以和上面这段代码合并的。

   while (msg == RT_NULL)
   {
      ...
       /* if it's not waiting forever and then re-calculate timeout tick */
       tick_delta = rt_tick_get() - tick_delta;
       timeout -= tick_delta;
       if (timeout < 0)
           timeout = 0;
       /* no waiting, return timeout */
       if (timeout == 0)
       {
           /* enable interrupt */
           rt_hw_interrupt_enable(temp);

           return -RT_EFULL;
       }
       msg = (struct rt_mq_message *)mq->msg_queue_free;
   }

结尾

我们知道,关中断后总需要在最短的时间内尽快打开中断,在【关中断/开中断】操作对总数无法改变的前提下。【关开/关开/关开】与【关关关/开开开】两种框架模式是有本质区别的:第一种模式把关中断时间分化,每次开中断间隙可以有机会处理中断异常;第二种总关中断时间可能是第一种的三倍!极大提高了丢中断的可能性。
作为上一篇的延续和总结,关中断的话题就聊到这儿,欢迎各位一起讨论。

> 本优化系列所有提到的更改已经提交到 gitee ,欢迎大家测试
https://gitee.com/thewon/rt_thread_repo

相关文章:
[rt-thread 系统优化系列(一) 之 关中断]( https://club.rt-thread.org/ask/article/2931.html )
[rt-thread 系统优化系列(二) 之 线程间同步和通信对中断的影响]( https://club.rt-thread.org/ask/article/2939.html )
[rt-thread 系统优化系列(三) 之 软定时器]( https://club.rt-thread.org/ask/article/2967.html )
[ rt-thread 系统优化系列(四) 之 再谈 ipc 中的 bug]( https://club.rt-thread.org/ask/article/3044.html )

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 中断
    +关注

    关注

    5

    文章

    898

    浏览量

    41490
  • 优化
    +关注

    关注

    0

    文章

    220

    浏览量

    23903
  • RT-Thread
    +关注

    关注

    31

    文章

    1288

    浏览量

    40111
收藏 人收藏

    评论

    相关推荐

    RT-Thread记录(九、RTT中断处理与阶段小结)

    RT-Thread 内核部分最后一个点 中断管理,顺带着对前面所学知识做个小结。
    的头像 发表于 06-24 10:25 6255次阅读
    <b class='flag-5'>RT-Thread</b>记录(九、RTT<b class='flag-5'>中断</b>处理与阶段小结)

    RT-Thread记录(RT-Thread内核启动流程)

    在前面我们RT-Thread Studio工程基础之上讲一讲RT-Thread内核启动流程.
    的头像 发表于 06-20 00:30 5032次阅读
    <b class='flag-5'>RT-Thread</b>记录(<b class='flag-5'>二</b>、<b class='flag-5'>RT-Thread</b>内核启动流程)

    RT-Thread记录(六、IPC机制信号量互斥量事件集)

    上文说到 RT-Thread 对临界区的处理方式有多种,其中已经分析了关闭调度器和屏蔽中断的方式, 本文就来学学另外的线程同步方式。
    的头像 发表于 06-21 10:40 5234次阅读
    <b class='flag-5'>RT-Thread</b>记录(六、IPC机制<b class='flag-5'>之</b>信号量互斥量事件集)

    【原创精选】RT-Thread征文精选技术文章合集

    优化系列(零) SysTick 优化分析rt-thread 优化系列(一)
    发表于 07-26 14:56

    RT-Thread编程指南

    RT-Thread编程指南——RT-Thread开发组(2015-03-31)。RT-Thread做为国内有较大影响力的开源实时操作系统,本文是RT-Thread实时操作系统的编程指南
    发表于 11-26 16:06 0次下载

    RT-Thread学习笔记系列OTA升级(1)

    RT-Thread学习笔记系列OTA升级前言硬件介绍和Bootloader配置需求与功能设计功能实现过程前言做一个产品,首先是需要设计后期可升级更新功能,否则没有升级功能则每次出现问题则需要寄回
    发表于 12-14 18:50 1次下载
    <b class='flag-5'>RT-Thread</b>学习笔记<b class='flag-5'>系列</b><b class='flag-5'>之</b>OTA升级(1)

    RT-Thread Studio驱动SD卡

    RT-Thread Studio驱动SD卡前言一、创建基本工程1、创建Bootloader2、创建项目工程、配置RT-Thread Settings三、代码分析1.引入库2.读入数据
    发表于 12-27 19:13 20次下载
    <b class='flag-5'>RT-Thread</b> Studio驱动SD卡

    RT-Thread学习笔记 RT-Thread的架构概述

    RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类型 RTOS
    的头像 发表于 07-09 11:27 4556次阅读
    <b class='flag-5'>RT-Thread</b>学习笔记 <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_线程间同步

    RT-Thread文档_线程间同步
    发表于 02-22 18:29 1次下载
    <b class='flag-5'>RT-Thread</b>文档_线程间<b class='flag-5'>同步</b>

    RT-Thread文档_中断管理

    RT-Thread文档_中断管理
    发表于 02-22 18:30 1次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>中断</b>管理

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 9次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植

    基于RT-Thread Studio学习

    前期准备:从官网下载 RT-Thread Studio,弄个账号登陆,开启rt-thread学习之旅。
    的头像 发表于 05-15 11:00 3958次阅读
    基于<b class='flag-5'>RT-Thread</b> Studio学习

    RT-Thread入门学习笔记-熟悉全局中断的操作

    RT-Thread中,全局中断的操作很多,大家都知道全局中断的【disable】与【enable】
    的头像 发表于 06-07 14:58 2172次阅读
    <b class='flag-5'>RT-Thread</b>入门学习笔记-熟悉全局<b class='flag-5'>中断</b>的操作

    RT-Thread v5.0.2 发布

    RT-Thread 代码仓库地址: ●  https://github.com/RT-Thread/rt-thread RT-Thread 5.0.2 版本发布日志详情: ●  htt
    的头像 发表于 10-10 18:45 1485次阅读
    <b class='flag-5'>RT-Thread</b> v5.0.2 发布