前言
書接前文,上篇優(yōu)化聊的是關(guān)中斷操作,在很多地方過保護(hù),導(dǎo)致關(guān)中斷時間太久,可能引起其它中斷不能及時響應(yīng)。今天特意說說線程間同步和通信,分析一下它們是怎么影響關(guān)中斷時間的,比起前文會有些深入分析。
從 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;
}
}
這段代碼的大致流程是:關(guān)全局中斷,取消息隊列,如果沒有空閑消息進(jìn)入等待,將當(dāng)前線程注冊到消息隊列等待線程列表,啟動當(dāng)前線程內(nèi)置硬定時器(等待超時機(jī)制),開全局中斷,執(zhí)行任務(wù)調(diào)度,被喚醒后進(jìn)行是超時喚醒還是消息隊列空喚醒處理。
> 首先申明,這段代碼在設(shè)置等待超時時間的情況下才有效,當(dāng)?shù)谒膫€參數(shù) timeout 為 0 的時候?qū)Ρ敬畏治鰺o效。鑒于在中斷回調(diào)函數(shù)中要求不能設(shè)置 timeout 值,也就是不能進(jìn)行阻塞調(diào)用,但是本文章仍然討論在中斷回調(diào)函數(shù)中的阻塞調(diào)用情況,權(quán)作參考。下面就分兩種情況分別分析。
非中斷,線程中阻塞調(diào)用 `rt_mq_send_wait`
假設(shè)線程中調(diào)用執(zhí)行了函數(shù) `rt_mq_send_wait` 第四個參數(shù) `timeout` 不為 0 。那么上面的代碼一定執(zhí)行 while 循環(huán)體。這個 while 循環(huán)暴露的多個問題不提,讓我們把目光聚焦到啟動線程定時器和開全局中斷部分。
前言部分預(yù)先提醒了今天的主題是:啟動超時等待定時器的操作有必要在關(guān)中斷中嗎?可不可以先開中斷,然后啟動定時器?
為了說明這個問題,讓我們再設(shè)想一種使用情況,假設(shè)在線程中單純的啟動一個定時器,如下:
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));
如上用法我們會把它放到關(guān)中斷里面嗎?答案是不需要!
那么,`rt_mq_send_wait` 中的關(guān)中斷是為了保護(hù)什么?同樣是在線程中執(zhí)行,同樣的兩句代碼,同樣的使用為什么被 rt_mq_send_wait 執(zhí)行時就需要被關(guān)中斷保護(hù)了?
執(zhí)行這兩句代碼時,無論是發(fā)生普通中斷,或者有任務(wù)調(diào)度切換到了其它線程,都不應(yīng)該會影響到這個定時器被正常啟動。至于說這個 `thread` 指針,在當(dāng)前這個線程不會被強(qiáng)制刪除的前提下, `thread` 指針一直有效。
所以說,啟動超時等待定時器的操作**沒**必要在關(guān)中斷中。可以**先開中斷,然后啟動定時器**
中斷,阻塞調(diào)用 rt_mq_send_wait
> 再次申明,實際使用中避免這種用法,這里僅僅做交流,并非使用建議。
和在線程中不一樣的地方在于,線程中調(diào)用 `rt_mq_send_wait` 時 `thread` 指針*肯定*是當(dāng)前線程;在中斷中調(diào)用 rt_mq_send_wait ,因中斷不定什么時候出現(xiàn), `thread` 指針可能是任意被創(chuàng)建的(有機(jī)會進(jìn)入運行態(tài)的)線程。
為了不失一般性,我們再次假設(shè),這個中斷優(yōu)先級比較低,可能被另外一個中斷嵌套。而且假設(shè)在執(zhí)行 `rt_timer_control` 和 `rt_timer_start` 時被其它中斷打斷。這種極端情況下會出現(xiàn)什么結(jié)果以及影響?
1. 中斷中再次被中斷,同時有阻塞操作。這個定時器會被兩個地方同時使用,鑒于 `rt_timer_control` 和 `rt_timer_start` 內(nèi)部也有中斷,即便在兩個函數(shù)中間出現(xiàn)新中斷,在新中斷中這個定時器也可以被正常配置而不影響在新中斷中的定時任務(wù)!
2. 中斷中出現(xiàn) SysTick 中斷,然后有任務(wù)調(diào)度。因為前邊中斷中使用的定時器是某個不確定線程的,這時候出現(xiàn)任務(wù)調(diào)度,新線程是另外一個不確定線程,而且可以保證的是肯定是另一個不同的線程。這個新的進(jìn)程會有可能強(qiáng)制刪除前一個線程嗎?
說到這里,我們會發(fā)現(xiàn),***即便是在中斷里阻塞調(diào)用 `rt_mq_send_wait` 函數(shù), `rt_timer_control` 和 `rt_timer_start` 操作不需要被關(guān)中斷保護(hù)!***
小結(jié)
綜上分析,可以*先開中斷,然后啟動定時器,最后進(jìn)行任務(wù)調(diào)度*。而不用擔(dān)心數(shù)據(jù)共享的問題。
類似用法的函數(shù)很多,ipc.c 中每一個 rt_xxx_send 和 rt_xxx_recv rt_xxx_take 都是一個模式。
timeout 的一些討論(無關(guān)中斷)
開篇貼的代碼,考慮到了被其它線程喚醒的情況,假如等待阻塞中,等待時間還沒到但是其它線程特意喚醒了它,會執(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;
}
這段代碼本身沒多少問題,有問題的是接下來判斷消息隊列是否有空閑消息,如果沒有進(jìn)入下一次阻塞中。
判斷是否有空閑消息后,判斷 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;
}
結(jié)尾
我們知道,關(guān)中斷后總需要在最短的時間內(nèi)盡快打開中斷,在【關(guān)中斷/開中斷】操作對總數(shù)無法改變的前提下。【關(guān)開/關(guān)開/關(guān)開】與【關(guān)關(guān)關(guān)/開開開】兩種框架模式是有本質(zhì)區(qū)別的:第一種模式把關(guān)中斷時間分化,每次開中斷間隙可以有機(jī)會處理中斷異常;第二種總關(guān)中斷時間可能是第一種的三倍!極大提高了丟中斷的可能性。
作為上一篇的延續(xù)和總結(jié),關(guān)中斷的話題就聊到這兒,歡迎各位一起討論。
> 本優(yōu)化系列所有提到的更改已經(jīng)提交到 gitee ,歡迎大家測試
https://gitee.com/thewon/rt_thread_repo
相關(guān)文章:
[rt-thread 系統(tǒng)優(yōu)化系列(一) 之 關(guān)中斷]( 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 )
審核編輯:湯梓紅
-
中斷
+關(guān)注
關(guān)注
5文章
904瀏覽量
42510 -
優(yōu)化
+關(guān)注
關(guān)注
0文章
220瀏覽量
24225 -
RT-Thread
+關(guān)注
關(guān)注
32文章
1368瀏覽量
41499
發(fā)布評論請先 登錄
RT-Thread記錄(六、IPC機(jī)制之信號量互斥量事件集)

【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集
RT-Thread編程指南
RT-Thread學(xué)習(xí)筆記系列之OTA升級(1)

RT-Thread Studio驅(qū)動SD卡

RT-Thread學(xué)習(xí)筆記 RT-Thread的架構(gòu)概述

基于RT-Thread Studio學(xué)習(xí)

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

評論