前言
多任務(wù)系統(tǒng),線程和中斷是兩個(gè)競(jìng)爭(zhēng)關(guān)系的各自獨(dú)立的實(shí)體。很多 api 是禁止在中斷中調(diào)用的。
和線程運(yùn)行息息相關(guān)的函數(shù),要求必須在任務(wù)調(diào)度運(yùn)行起來(lái)以后才能使用。
以及,一些 api 被設(shè)計(jì)出來(lái)是用來(lái)在某線程操作另外一個(gè)線程,是不可以某線程針對(duì)自己使用的。
今天就說(shuō)說(shuō)哪些 api 禁止在中斷中調(diào)用、哪些必須在任務(wù)調(diào)度器運(yùn)行以后才能使用、哪些不能線程用在自己身上。
禁止在中斷調(diào)用的 api 列表
內(nèi)存堆操作類(lèi)
rt_system_heap_init
rt_malloc
rt_realloc
rt_free
內(nèi)存池類(lèi)
rt_mp_create
rt_mp_delete
rt_mp_alloc
內(nèi)核對(duì)象類(lèi)
rt_object_allocate
rt_object_find
idle 線程
rt_defunct_execute
ipc 同步和消息機(jī)制類(lèi)
rt_sem_create
rt_sem_delete
rt_mutex_create
rt_mutex_trytake
rt_mutex_delete
rt_event_create
rt_event_delete
rt_mb_create
rt_mb_delete
rt_mq_create
rt_mq_delete
完成量
rt_completion_wait
隊(duì)列類(lèi)
rt_wqueue_wait
rt_data_queue_push
rt_data_queue_pop
延時(shí)
rt_thread_sleep
rt_thread_delay
rt_thread_delay_until
rt_thread_mdelay
注:源碼中摘錄,并無(wú)理論考證,更無(wú)實(shí)際驗(yàn)證
所有被禁止在中斷中調(diào)用的函數(shù)都有個(gè)相似的特征 —— 它可能是阻塞的,導(dǎo)致中斷無(wú)法短時(shí)間內(nèi)返回;或者它想調(diào)用可能發(fā)生阻塞的 api 。
任何引用了他們的函數(shù)也被帶跑了,不能在中斷中使用。
RT_DEBUG_NOT_IN_INTERRUPT 調(diào)試宏定義
rt-thread 定義了一個(gè)宏,當(dāng)我們開(kāi)啟調(diào)試的時(shí)候,它可以幫我檢查當(dāng)前函數(shù)是否被中斷調(diào)用了。其實(shí)現(xiàn)如下:
#define RT_DEBUG_NOT_IN_INTERRUPT \
do \
{ \
rt_base_t level; \
level = rt_hw_interrupt_disable(); \
if (rt_interrupt_get_nest() != 0) \
{ \
rt_kprintf("Function[%s] shall not be used in ISR\n", __FUNCTION__); \
RT_ASSERT(0) \
} \
rt_hw_interrupt_enable(level); \
} \
while (0)
當(dāng)你在源碼里看見(jiàn)某個(gè)函數(shù)體中有一行
引用 RT_DEBUG_NOT_IN_INTERRUPT 調(diào)試宏的幾點(diǎn)問(wèn)題
1. rt-thread 中有多處引用了這個(gè)宏,上面函數(shù)列表里絕大部分有;
2. 有一些沒(méi)有,比如 memheap.c 文件中的 `rt_memheap_alloc` `rt_memheap_realloc` 等操作。
3. 仔細(xì)查看引用了 `RT_DEBUG_NOT_IN_INTERRUPT` 的所有函數(shù),有部分使用宏的地方真讓人揪心,以 `rt_defunct_execute` 函數(shù)為例:
static void rt_defunct_execute(void)
{
// Loop until there is no dead thread.
// So one call to rt_defunct_execute
// will do all the cleanups. */
while (1)
{
register rt_base_t level;
rt_thread_t thread;
void (*cleanup)(struct rt_thread *tid);
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = RT_NULL;
#endif
RT_DEBUG_NOT_IN_INTERRUPT; // <<--
為什么放 while 里?不是 while 之前?不應(yīng)該放到函數(shù)開(kāi)頭醒目的地方嗎?有人反駁了啊,放哪兒不一樣,放哪也是一行代碼的事兒!
RT_DEBUG_IN_THREAD_CONTEXT 也有用樣的使用不當(dāng),放到某個(gè)判斷內(nèi)部,而不是函數(shù)開(kāi)頭就引用。
4. 那么我還有個(gè)疑問(wèn):這是 idle 線程的內(nèi)部調(diào)用的局部函數(shù),會(huì)被中斷調(diào)用了?!誰(shuí)有權(quán)限在中斷里引用它?!**`rt_defunct_execute` 函數(shù)應(yīng)該從上面的列表里刪除掉**,這個(gè)筆者已經(jīng)在 github 上 pr 修改過(guò)了
必須在線程上下文調(diào)用的 api 列表
rt_signal_wait
ipc 同步和消息機(jī)制
rt_sem_take
rt_mutex_take
rt_mutex_release
rt_event_recv
rt_mb_send_wait
rt_mb_recv
rt_mq_send_wait
rt_mq_recv
線程操作類(lèi)
rt_thread_detach
rt_thread_delete
rt_thread_yield
rt_thread_delay
rt_thread_delay_until
rt_thread_mdelay
rt_thread_suspend
rt_thread_resume
其它
rt_tick_get
rt_enter_critical
rt_exit_critical
可以這么說(shuō),任務(wù)調(diào)度器啟動(dòng)前,只允許 init/create/startup 某線程。
雖然,沒(méi)有發(fā)現(xiàn)有誰(shuí)在任務(wù)調(diào)度器啟動(dòng)前調(diào)用 `rt_thread_suspend` ,但是,有一大批人想使用 `rt_thread_mdelay` 幾個(gè)延時(shí)函數(shù)。以上這幾個(gè)函數(shù)開(kāi)頭都應(yīng)該添加 `RT_DEBUG_IN_THREAD_CONTEXT` 檢測(cè),在源碼中用于明示此 api 的使用限制。
RT_DEBUG_IN_THREAD_CONTEXT 調(diào)試宏定義
首先,貼出來(lái)它的定義
#define RT_DEBUG_IN_THREAD_CONTEXT \
RT_DEBUG_NOT_IN_INTERRUPT; \
do \
{ \
rt_base_t level; \
level = rt_hw_interrupt_disable(); \
if (rt_thread_self() == RT_NULL) \
{ \
rt_kprintf("Function[%s] shall not be used before scheduler start\n", \
__FUNCTION__); \
RT_ASSERT(0) \
} \
rt_hw_interrupt_enable(level); \
} \
while (0)
#else
#define RT_DEBUG_NOT_IN_INTERRUPT
#define RT_DEBUG_IN_THREAD_CONTEXT
#endif
注:此定義代碼略有改動(dòng)。
在線程上下文調(diào)用的函數(shù)有兩個(gè)特征:
1. 不能在中斷中調(diào)用。
2. 不能在任務(wù)調(diào)度器啟動(dòng)前調(diào)用,必須線程啟動(dòng)后,被線程入口函數(shù)調(diào)用。
因此,函數(shù)體中添加了此宏引用的函數(shù),也不能在中斷響應(yīng)過(guò)程中被調(diào)用。
鑒于此,本篇開(kāi)頭的***“禁止在中斷調(diào)用的 api 列表” 需要進(jìn)行擴(kuò)充,添加上 “必須在線程上下文調(diào)用的 api 列表” 中的所有 api***。
不能用在線程自己身上的 api
首先,這類(lèi) api 有個(gè)特征,那就是形參有個(gè) rt_thread_t 類(lèi)型參數(shù)。
rt_thread_detach
rt_thread_delete
這倆不多說(shuō),一個(gè)針對(duì)靜態(tài)線程對(duì)象,一個(gè)針對(duì)動(dòng)態(tài)線程對(duì)象。作用均是退出線程、清理線程。當(dāng)前線程退出可以直接從 while 循環(huán)里跳出來(lái),從線程入口函數(shù) return 就可以。
`rt_thread_startup` 當(dāng)一個(gè)線程正在運(yùn)行的時(shí)候,它自己再 startup 自己是不是就很詭異了。
`rt_thread_resume` 線程掛起由得自己,喚醒就由不得自己了。
`rt_thread_control` 控制某線程的動(dòng)作可能有:改變優(yōu)先級(jí)、啟動(dòng)線程、關(guān)閉線程、以及多核cpu上綁定線程到某 cpu。某線程偷偷修改自己的運(yùn)行優(yōu)先級(jí)也說(shuō)得過(guò)去;自己想換個(gè) cpu 核心做依靠,看似也可以,但是,知道當(dāng)前核的感受嗎?有 cpu 資源可用已經(jīng)不錯(cuò)了,不是嗎?
因此,這個(gè) api 只能用來(lái)修改自己的優(yōu)先級(jí)。
特別的,目前 rt-thread 只支持當(dāng)前線程自己主動(dòng) suspend ,然后等待中斷或者其它線程 resume 它。而且,僅限于定時(shí)、線程間同步和通信機(jī)制里使用。不支持 A 線程 suspend B 線程,然后某個(gè)時(shí)機(jī)再 resume B。不支持 `rt_thread_suspend` `rt_thread_resume` 直接調(diào)用。
雖然 `rt_thread_detach` `rt_thread_delete` 用來(lái)退出線程,但是,不了解線程運(yùn)行機(jī)制,千萬(wàn)別隨意使用這倆函數(shù)退出其它線程。如果有需要,使用消息機(jī)制讓現(xiàn)在自己跳出線程入口函數(shù)的 while 循環(huán),自己從線程入口函數(shù)返回,這樣更安全可靠。
結(jié)束
感謝您的閱讀,歡迎各位提出意見(jiàn),對(duì)文中的不當(dāng)之處不吝賜教。
審核編輯:湯梓紅
-
API
+關(guān)注
關(guān)注
2文章
1510瀏覽量
62296 -
多任務(wù)系統(tǒng)
+關(guān)注
關(guān)注
0文章
11瀏覽量
6878 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1305瀏覽量
40315
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論