前言
信號 signal,并不是線程間同步的信號量 semaphore。后者是線程間同步機制的一種,而前者是線程間異步通信的一種。
官方文檔里對其解釋是:“信號(又稱為軟中斷信號),在軟件層次上是對中斷機制的一種模擬,在原理上,一個線程收到一個信號與處理器收到一個中斷請求可以說是類似的?!?/p>
信號本質(zhì)是**軟中斷**,用來通知線程發(fā)生了異步事件,**用做線程之間的異常通知、應(yīng)急處理**。一個**線程不必通過任何操作來等待信號的到達(dá)**,事實上,**線程也不知道信號到底什么時候到達(dá)**。線程之間可以互相通過調(diào)用 `rt_thread_kill` 發(fā)送信號。
以上畫線部分是我特意要大家注意的,我們要看待中斷回調(diào)函數(shù)那樣,看待信號回調(diào)函數(shù)**被執(zhí)行的實機**,但不需要過分擔(dān)憂的是回調(diào)函數(shù)**執(zhí)行時間**,因為**終究信號回調(diào)函數(shù)還是在線程上下文被執(zhí)行的**。
從官方文檔可以清楚了解到,使用信號很簡單,安裝信號、解除信號掩碼、發(fā)送信號、處理信號等幾個過程。
更多關(guān)于信號的原理詳見官方文檔 [信號]( https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2?id=%e4%bf%a1%e5%8f%b7 )
一個示例引起的血案
官方原版示例筆者就不貼出來了,直接拷貝到自己的項目完美運行。但是,筆者經(jīng)過如下修改,發(fā)現(xiàn)一點兒疑問。
/* 線程 1 的入口函數(shù) */
static void thread1_entry(void *parameter)
{
...
while (cnt < 10)
{
...
tick = rt_tick_get();
rt_thread_mdelay(1000);
tick = rt_tick_get();
}
...
}
把延時時間增長,前后添加測時。多次運行發(fā)現(xiàn) tick 值改變只有 300 (`rt_thread_mdelay(300)`)。這說明了線程響應(yīng) signal 后,處理了信號回調(diào)函數(shù)之后放棄了之前的延時!那么問題來了,應(yīng)用層想要的延時時間不足,應(yīng)用層知道嗎?答案是,*不知道!*
rt-thread 中阻塞函數(shù)列表
前一段時間在文章 rt-thread 那些你必須知道的幾類 api 里總結(jié)了 *禁止在中斷中調(diào)用*、*必須在任務(wù)調(diào)度器運行以后才能使用*、*不能用在線程自己身上*的幾類 api。
可能還缺一種:哪些 api 會引起線程調(diào)度,使得當(dāng)前線程放棄 cpu 使用權(quán)——所有調(diào)用 `rt_schedule` 的函數(shù)都屬于這類。這里邊又分三種情況,一種是時間片耗盡讓出 cpu 使用權(quán);一種是釋放資源或者信號讓出 cpu 使用權(quán);還有一種是等待資源而被動放棄 cpu。最后這種情況,是有目地的,往往希望有資源可用了之后從阻塞中恢復(fù)繼續(xù)運行,如果線程從阻塞中恢復(fù)運行但同時沒有資源可用是不是就烏龍了?以下的關(guān)注重點也是這類函數(shù)。
所有第三類引起線程調(diào)度的函數(shù)和上面的 `rt_thread_mdelay` 一樣,在 signal 面前可能遇到一樣的遭遇。大體上,分這么幾類:
- 延時函數(shù)
- 線程間同步機制函數(shù)
- 線程間通信機制部分函數(shù)(signal除外)
- posix 下的 select poll 等接口(可能使用了線程間同步和通信機制)
這幾類在遇到 signal 之后行為分別是什么樣的?
被阻塞函數(shù)遇到 signal 后什么反應(yīng)?
延時函數(shù)遇到 signal
這個前面已經(jīng)經(jīng)過測試的了,它會退出阻塞提前結(jié)束延時,但是應(yīng)用層并不知道是達(dá)到延時時間還是有信號。
線程間同步通信機制函數(shù)遇到 signal
- `rt_sem_take` 線程 error 非 RT_EOK (包括 RT_EINTR)直接返回線程錯誤狀態(tài)
/* do schedule */
rt_schedule();
if (thread->error != RT_EOK)
{
return thread->error;
}
- `rt_mutex_take` 考慮到了 signal 的影響,返回繼續(xù)阻塞等待 `time` 時間。這是 ipc 里唯一例外的一個。
/* do schedule */
rt_schedule();
if (thread->error != RT_EOK)
{
#ifdef RT_USING_SIGNALS
/* interrupt by signal, try it again */
if (thread->error == -RT_EINTR) goto __again;
#endif /* RT_USING_SIGNALS */
其它,其余的 ipc 都和 `rt_sem_take` 一樣。
完成量遇到 signal
`rt_completion_wait` 返回線程錯誤狀態(tài)。
/* do schedule */
rt_schedule();
/* thread is waked up */
result = thread->error;
level = rt_hw_interrupt_disable();
}
}
...
return result;
select poll 等接口與 signal
因為文件描述符對應(yīng)的設(shè)備不盡相同,設(shè)備底層實現(xiàn) `poll` 的方式可能也千差萬別,但是他們大概率是使用上面的線程間同步和通信機制了。
`poll` 實現(xiàn)過程調(diào)用個超時等待函數(shù) `poll_wait_timeout` ,它也沒有區(qū)分超時和信號兩種情況。
rt_schedule();
level = rt_hw_interrupt_disable();
}
ret = !pt->triggered;
rt_hw_interrupt_enable(level);
return ret;
我們發(fā)現(xiàn),`rt_sem_take` 結(jié)束了阻塞,并可能返回了 `RT_EINTR` ,而 `rt_mutex_take` 繼續(xù)了循環(huán)阻塞。
等待資源而被動放棄 cpu 時怎么應(yīng)對 signal 才合適?
現(xiàn)做以下約定,等待資源而被動放棄 cpu 的線程在此約定下,當(dāng)有 signal 的時候會提前結(jié)束阻塞,返回應(yīng)用層,應(yīng)用層可以根據(jù)線程錯誤狀態(tài)區(qū)別處理。
1. 復(fù)位線程錯誤狀態(tài)為 `RT_EOK` 。
2. 調(diào)用 `rt_schedule` 進(jìn)行線程調(diào)度,線程被阻塞掛起。
3. 從 `rt_schedule` 恢復(fù)喚醒,有一定手段通知到應(yīng)用層(返回線程錯誤狀態(tài)),應(yīng)用層可以區(qū)分出是因為資源可用還是因為信號。
哪些 api 做到了以上這幾點呢?
```
rt_completion_wait
rt_sem_take
rt_event_recv
rt_mb_send_wait
rt_mb_recv
rt_mq_send_wait
rt_mq_recv
rt_data_queue_push
rt_data_queue_pop
rt_mp_alloc
哪些 api 沒有做到以上幾點?
```
rt_mutex_take
rt_thread_sleep
rt_thread_delay
rt_thread_delay_until
rt_thread_mdelay
rt_wqueue_wait
筆者曾經(jīng)在 gitee 上提交過一個 [issue]( https://gitee.com/rtthread/rt-thread/issues/I44JNS ) ,當(dāng)時筆者隱隱中認(rèn)為 ipc 中的不一致行為總有些隱患,感覺所有的阻塞等待都應(yīng)該處理一下意外喚醒后的超時等待。卻沒意識到有什么意外情況可以讓這些函數(shù)從阻塞等待中提前退出。通過研究 signal 實現(xiàn)原理的過程中發(fā)現(xiàn),這種意外情況還有存在的,只是擔(dān)憂的問題重點變了,不是處理阻塞等待剩余時間,而是在 signal 的影響下通知應(yīng)用層的問題。
解決方案
有了上面的梳理,下面的修改方向就有了,改動范圍也確定了。
- 幾個延時函數(shù)返回 `thread->error` 代替目前的 `RT_EOK` ;
- `rt_mutex_take` 去掉 `goto __again` 也返回 `thread->error` ;
- `rt_wqueue_wait` 返回 `thread->error` 代替目前的 `RT_EOK` 。
- `poll` 目前返回值是 >= 0 的,返回 0 可能是超時,也可能是被信號中斷了。暫時不發(fā)表修改意見。
結(jié)束語
以上搜索不一定完整完全,但應(yīng)該包括了大部分受到影響的函數(shù)。如果看客有發(fā)現(xiàn)其它的 api 有不符合上述約定行為的,請留言告知,謝謝!
本人能力有限,文中難免有錯誤。望各位同仁不吝賜教。
相關(guān)文章
rt-thread 優(yōu)化系列(0) SysTick 優(yōu)化分析
rt-thread 優(yōu)化系列(一) 之 過多關(guān)中斷
rt-thread 優(yōu)化系列(二) 之 同步和消息關(guān)中斷分析
rt-thread優(yōu)化系列(三)軟定時器的定時漂移問題分析
審核編輯:湯梓紅
-
信號
+關(guān)注
關(guān)注
11文章
2809瀏覽量
77167 -
IPC
+關(guān)注
關(guān)注
3文章
353瀏覽量
52111 -
signal
+關(guān)注
關(guān)注
0文章
110瀏覽量
24993 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1306瀏覽量
40437
發(fā)布評論請先 登錄
相關(guān)推薦
評論