0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

rt-thread 優(yōu)化系列(二) 之 同步和消息關中斷分析

出出 ? 來源:出出 ? 作者:出出 ? 2022-06-21 09:47 ? 次閱讀

前言

書接前文,上篇優(yōu)化聊的是關中斷操作,在很多地方過保護,導致關中斷時間太久,可能引起其它中斷不能及時響應。今天特意說說線程間同步和通信,分析一下它們是怎么影響關中斷時間的,比起前文會有些深入分析。

從 rt_mq_send_wait 說起

為了方便談問題先貼一段代碼,這段代碼是從 4.0.4 版本的 `rt_mq_send_wait` 函數(shù)中摘取的部分。

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

這段代碼的大致流程是:關全局中斷,取消息隊列,如果沒有空閑消息進入等待,將當前線程注冊到消息隊列等待線程列表,啟動當前線程內置硬定時器(等待超時機制),開全局中斷,執(zhí)行任務調度,被喚醒后進行是超時喚醒還是消息隊列空喚醒處理。

> 首先申明,這段代碼在設置等待超時時間的情況下才有效,當?shù)谒膫€參數(shù) timeout 為 0 的時候對本次分析無效。鑒于在中斷回調函數(shù)中要求不能設置 timeout 值,也就是不能進行阻塞調用,但是本文章仍然討論在中斷回調函數(shù)中的阻塞調用情況,權作參考。下面就分兩種情況分別分析。

非中斷,線程中阻塞調用 `rt_mq_send_wait`

假設線程中調用執(zhí)行了函數(shù) `rt_mq_send_wait` 第四個參數(shù) `timeout` 不為 0 。那么上面的代碼一定執(zhí)行 while 循環(huán)體。這個 while 循環(huán)暴露的多個問題不提,讓我們把目光聚焦到啟動線程定時器和開全局中斷部分。
前言部分預先提醒了今天的主題是:啟動超時等待定時器的操作有必要在關中斷中嗎?可不可以先開中斷,然后啟動定時器?

為了說明這個問題,讓我們再設想一種使用情況,假設在線程中單純的啟動一個定時器,如下:

   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` 中的關中斷是為了保護什么?同樣是在線程中執(zhí)行,同樣的兩句代碼,同樣的使用為什么被 rt_mq_send_wait 執(zhí)行時就需要被關中斷保護了?
執(zhí)行這兩句代碼時,無論是發(fā)生普通中斷,或者有任務調度切換到了其它線程,都不應該會影響到這個定時器被正常啟動。至于說這個 `thread` 指針,在當前這個線程不會被強制刪除的前提下, `thread` 指針一直有效。

所以說,啟動超時等待定時器的操作**沒**必要在關中斷中??梢?*先開中斷,然后啟動定時器**

中斷,阻塞調用 rt_mq_send_wait

> 再次申明,實際使用中避免這種用法,這里僅僅做交流,并非使用建議。

和在線程中不一樣的地方在于,線程中調用 `rt_mq_send_wait` 時 `thread` 指針*肯定*是當前線程;在中斷中調用 rt_mq_send_wait ,因中斷不定什么時候出現(xiàn), `thread` 指針可能是任意被創(chuàng)建的(有機會進入運行態(tài)的)線程。

為了不失一般性,我們再次假設,這個中斷優(yōu)先級比較低,可能被另外一個中斷嵌套。而且假設在執(zhí)行 `rt_timer_control` 和 `rt_timer_start` 時被其它中斷打斷。這種極端情況下會出現(xiàn)什么結果以及影響?
1. 中斷中再次被中斷,同時有阻塞操作。這個定時器會被兩個地方同時使用,鑒于 `rt_timer_control` 和 `rt_timer_start` 內部也有中斷,即便在兩個函數(shù)中間出現(xiàn)新中斷,在新中斷中這個定時器也可以被正常配置而不影響在新中斷中的定時任務!
2. 中斷中出現(xiàn) SysTick 中斷,然后有任務調度。因為前邊中斷中使用的定時器是某個不確定線程的,這時候出現(xiàn)任務調度,新線程是另外一個不確定線程,而且可以保證的是肯定是另一個不同的線程。這個新的進程會有可能強制刪除前一個線程嗎?

說到這里,我們會發(fā)現(xiàn),***即便是在中斷里阻塞調用 `rt_mq_send_wait` 函數(shù), `rt_timer_control` 和 `rt_timer_start` 操作不需要被關中斷保護!***

小結

綜上分析,可以*先開中斷,然后啟動定時器,最后進行任務調度*。而不用擔心數(shù)據(jù)共享的問題。
類似用法的函數(shù)很多,ipc.c 中每一個 rt_xxx_send 和 rt_xxx_recv rt_xxx_take 都是一個模式。

timeout 的一些討論(無關中斷)

開篇貼的代碼,考慮到了被其它線程喚醒的情況,假如等待阻塞中,等待時間還沒到但是其它線程特意喚醒了它,會執(zhí)行下面這段代碼

       /* 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 說明等待時間已經(jīng)超時,開中斷退出返回。這里判斷 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;
   }

結尾

我們知道,關中斷后總需要在最短的時間內盡快打開中斷,在【關中斷/開中斷】操作對總數(shù)無法改變的前提下?!娟P開/關開/關開】與【關關關/開開開】兩種框架模式是有本質區(qū)別的:第一種模式把關中斷時間分化,每次開中斷間隙可以有機會處理中斷異常;第二種總關中斷時間可能是第一種的三倍!極大提高了丟中斷的可能性。
作為上一篇的延續(xù)和總結,關中斷的話題就聊到這兒,歡迎各位一起討論。

> 本優(yōu)化系列所有提到的更改已經(jīng)提交到 gitee ,歡迎大家測試
https://gitee.com/thewon/rt_thread_repo

相關文章:
[rt-thread 系統(tǒng)優(yōu)化系列(一) 之 關中斷]( https://club.rt-thread.org/ask/article/2931.html )
[rt-thread 系統(tǒng)優(yōu)化系列(二) 之 線程間同步和通信對中斷的影響]( https://club.rt-thread.org/ask/article/2939.html )
[rt-thread 系統(tǒng)優(yōu)化系列(三) 之 軟定時器]( https://club.rt-thread.org/ask/article/2967.html )
[ rt-thread 系統(tǒng)優(yōu)化系列(四) 之 再談 ipc 中的 bug]( https://club.rt-thread.org/ask/article/3044.html )

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 中斷
    +關注

    關注

    5

    文章

    895

    瀏覽量

    41353
  • 優(yōu)化
    +關注

    關注

    0

    文章

    220

    瀏覽量

    23859
  • RT-Thread
    +關注

    關注

    31

    文章

    1265

    瀏覽量

    39853
收藏 人收藏

    評論

    相關推薦

    RT-Thread記錄(九、RTT中斷處理與階段小結)

    RT-Thread 內核部分最后一個點 中斷管理,順帶著對前面所學知識做個小結。
    的頭像 發(fā)表于 06-24 10:25 ?6124次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(九、RTT<b class='flag-5'>中斷</b>處理與階段小結)

    RT-Thread記錄(、RT-Thread內核啟動流程)

    在前面我們RT-Thread Studio工程基礎之上講一講RT-Thread內核啟動流程.
    的頭像 發(fā)表于 06-20 00:30 ?4940次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(<b class='flag-5'>二</b>、<b class='flag-5'>RT-Thread</b>內核啟動流程)

    RT-Thread記錄(六、IPC機制信號量互斥量事件集)

    上文說到 RT-Thread 對臨界區(qū)的處理方式有多種,其中已經(jīng)分析了關閉調度器和屏蔽中斷的方式, 本文就來學學另外的線程同步方式。
    的頭像 發(fā)表于 06-21 10:40 ?5144次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(六、IPC機制<b class='flag-5'>之</b>信號量互斥量事件集)

    【原創(chuàng)精選】RT-Thread征文精選技術文章合集

    優(yōu)化系列(零) SysTick 優(yōu)化分析rt-thread 優(yōu)化系列(一)
    發(fā)表于 07-26 14:56

    RT-Thread編程指南

    RT-Thread編程指南——RT-Thread開發(fā)組(2015-03-31)。RT-Thread做為國內有較大影響力的開源實時操作系統(tǒng),本文是RT-Thread實時操作系統(tǒng)的編程指南
    發(fā)表于 11-26 16:06 ?0次下載

    RT-Thread學習筆記系列OTA升級(1)

    RT-Thread學習筆記系列OTA升級前言硬件介紹和Bootloader配置需求與功能設計功能實現(xiàn)過程前言做一個產(chǎn)品,首先是需要設計后期可升級更新功能,否則沒有升級功能則每次出現(xiàn)問題則需要寄回
    發(fā)表于 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卡前言一、創(chuàng)建基本工程1、創(chuàng)建Bootloader2、創(chuàng)建項目工程、配置RT-Thread Settings三、代碼分析1.引入庫2.讀入數(shù)據(jù)
    發(fā)表于 12-27 19:13 ?20次下載
    <b class='flag-5'>RT-Thread</b> Studio驅動SD卡

    RT-Thread學習筆記 RT-Thread的架構概述

    RT-Thread 簡介 作為一名 RTOS 的初學者,也許你對 RT-Thread 還比較陌生。然而,隨著你的深入接觸,你會逐漸發(fā)現(xiàn) RT-Thread 的魅力和它相較于其他同類型 RTOS
    的頭像 發(fā)表于 07-09 11:27 ?4467次閱讀
    <b class='flag-5'>RT-Thread</b>學習筆記 <b class='flag-5'>RT-Thread</b>的架構概述

    RT-Thread文檔_RT-Thread 簡介

    RT-Thread文檔_RT-Thread 簡介
    發(fā)表于 02-22 18:22 ?5次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 簡介

    RT-Thread文檔_線程間同步

    RT-Thread文檔_線程間同步
    發(fā)表于 02-22 18:29 ?1次下載
    <b class='flag-5'>RT-Thread</b>文檔_線程間<b class='flag-5'>同步</b>

    RT-Thread文檔_中斷管理

    RT-Thread文檔_中斷管理
    發(fā)表于 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 介紹與移植
    發(fā)表于 02-22 18:31 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> SMP 介紹與移植

    基于RT-Thread Studio學習

    前期準備:從官網(wǎng)下載 RT-Thread Studio,弄個賬號登陸,開啟rt-thread學習之旅。
    的頭像 發(fā)表于 05-15 11:00 ?3783次閱讀
    基于<b class='flag-5'>RT-Thread</b> Studio學習

    RT-Thread入門學習筆記-熟悉全局中斷的操作

    RT-Thread中,全局中斷的操作很多,大家都知道全局中斷的【disable】與【enable】
    的頭像 發(fā)表于 06-07 14:58 ?2015次閱讀
    <b class='flag-5'>RT-Thread</b>入門學習筆記-熟悉全局<b class='flag-5'>中斷</b>的操作

    RT-Thread v5.0.2 發(fā)布

    RT-Thread 代碼倉庫地址: ●? https://github.com/RT-Thread/rt-thread RT-Thread 5.0.2 版本發(fā)布日志詳情: ●? htt
    的頭像 發(fā)表于 10-10 18:45 ?1371次閱讀
    <b class='flag-5'>RT-Thread</b> v5.0.2 發(fā)布