前言
書接前文,上篇優(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 )
審核編輯:湯梓紅
-
中斷
+關注
關注
5文章
895瀏覽量
41353 -
優(yōu)化
+關注
關注
0文章
220瀏覽量
23859 -
RT-Thread
+關注
關注
31文章
1265瀏覽量
39853
發(fā)布評論請先 登錄
相關推薦
評論