- 兩個 thread 要溝通的方式,是透過共享的記憶體來完成;而如果此記憶體沒有排他性,這個記憶體有可能會不同步。
- 因此進入一塊共享的記憶體一次只允許一個 thread 來使用,這樣即可保證其資料的一致性
- 進入此共享記憶體則叫做 critical region
- RT-Thread 利用 7 種方式來完成同步:關閉中斷、scheduler lock、semaphore、互斥鎖、事件、mail box 及 message
關閉中斷#
level = rt_hw_interrupt_disable(); |
- 此方式是最強大的一種,但此 critical region 不可以佔用太多時間
Scheduler lock#
rt_enter_critical() |
- 使用此方式可確保當前 thread 不會被 scheduler 踢出,但還是有可能會被中斷影響。
進入 scheuler 鎖#
File: scheduler.c
功能 | 回傳值 |
---|---|
進入 scheuler 鎖 | void |
360 | /** |
- 即,將
rt_scheduler_lock_nest
加一
離開 scheduler 鎖#
功能 | 回傳值 |
---|---|
離開 scheuler 鎖 | void |
381 | /** |
- 即,將
rt_scheduler_lock_nest
減一
392 | if (rt_scheduler_lock_nest <= 0) |
- 如果
rt_scheduler_lock_nest
被減至 0 或以下,進行一次調度
功能 | 回傳值 |
---|---|
回傳 scheuler 鎖的值 | scheuler 鎖的值 |
409 | /** |
- 即,回傳
rt_scheduler_lock_nest
值
Semaphore#
- 為一個值,代表同時可用的個數
- 不等於 0 時可用,取用時將值減 1
- 當不可用時,將 thread 掛在等待的鏈上
File: rtdef.h
結構#
591 |
|
flags#
569 | /** |
File: ipc.c
建立 semaphore#
動態記憶體管理#
功能 | 回傳值 |
---|---|
建立 semaphore | semaphore |
*name |
value |
flag |
---|---|---|
名字 | semaphore 值,即最大可同時使用人數 | FIFO / PRIO |
247 | /** |
- 首先需要一塊 semaphore 的大小,初始化 ipc 物件,再依序寫入初始值及 flag
靜態記憶體管理#
功能 | 回傳值 |
---|---|
初始化 semaphore | RT_EOK |
sem |
*name |
value |
flag |
---|---|---|---|
semaphore 本體 | 名字 | semaphore 值,即最大可同時使用人數 | FIFO / PRIO |
186 | /** |
- 由於使用靜態記憶體,這裡就不需要再 allocate。
刪除 semaphore#
動態記憶體管理#
功能 | 回傳值 | sem |
---|---|---|
刪除 semaphore | RT_EOK |
欲刪除的 semaphore |
282 | /** |
- 首先需把所有正在等待此 semaphore 的 thread 叫醒
- 接著呼叫
rt_object_delete
清除此物件(semaphore)
靜態記憶體管理#
功能 | 回傳值 | sem |
---|---|---|
刪除 semaphore | RT_EOK |
欲刪除的 semaphore |
220 | /** |
- 這裡則透過
rt_object_detach
清除
使用 semaphore#
- 呼叫
rt_sem_take
來取得 semaphore,傳入的 time 是等待時間
功能 | 回傳值 |
---|---|
要求 semaphore | RT_EOK |
sem |
time |
---|---|
欲要求的 semaphore | 等待時間(如果需要) |
311 | /** |
- 由於待會會修改 semaphore 的值,這裡先將中斷關閉
333 | RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s take sem:%s, which value is: %d\n", |
- 如過
sem->value
值大於 0 代表可用,接著減一,並開啟中斷
346 | else |
- 如果 semaphore 不可用時:
- 且 time 為 0,表示不等待,直接開啟中斷並 return
355 | else |
- 如果要等待,則將 thread 插入 suspend list
374 | /* has waiting time, start thread timer */ |
- 且如果等待時間大於 0,則啟動一個 timeout 為 time 的 timer
386 | /* enable interrupt */ |
- 最後開啟中斷,做一次調度
- 若是不想等待,可以呼叫
rt_sem_trytake
- 即呼叫
rt_sem_take
及傳入time
為 0
功能 | 回傳值 | sem |
---|---|---|
要求 semaphore(不等待) | RT_EOK |
欲要求的 semaphore |
408 | /** |
- 還 semaphore 則使用
rt_sem_release
功能 | 回傳值 | sem |
---|---|---|
釋放 semaphore | RT_EOK |
欲要求的 semaphore |
421 | /** |
- 首先將待會會遇到的 flag(
need_schedule
)設為 false - 因為待會也會修改 semaphore 的值,這裡需要關閉中斷
444 | RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s releases sem:%s, which value is: %d\n", |
- 如果有人在等此 semaphore,先恢復他,並修改
need_schedule
為 true
455 | else |
- 如果沒有人在等待,則加一
457 | /* enable interrupt */ |
- 最後開啟中斷,並根據
need_schedule
來決定需不需要執行一次調度
互斥鎖(mutex)#
- 即一種值為 1 的特殊 semaphore,特別的是具有防止優先級翻轉的特性
File: rtdef.h
結構#
604 |
|
- 為了防止優先權翻轉,在持有鎖的過程中可能會被提升優先權,在結構中就需要紀錄原本的優先級。
File: ipc.c
建立 mutex#
動態記憶體管理#
功能 | 回傳值 |
---|---|
建立 mutex | mutex |
*name |
flag |
---|---|
名字 | FIFO / PRIO |
576 | /** |
- 首先 allocate 一個物件,初始化
596 | /* init ipc object */ |
- value 設為 1,擁有者為 NULL,原始權限最低(255),持有次數為 0
603 | /* set flag */ |
- 同時填入 flag
靜態記憶體管理#
功能 | 回傳值 |
---|---|
初始化 mutex | RT_EOK |
mutex |
*name |
flag |
---|---|---|
mutex 本體 | 名字 | FIFO / PRIO |
516 | /** |
- 這裡不需要 allocate,只需要初始化物件
刪除 mutex#
動態記憶體管理#
功能 | 回傳值 |
---|---|
刪除 mutex | RT_EOK |
mutex |
---|
欲刪除的 mutex |
612 | /** |
- 與 semaphore 類似,先將正在等待此鎖的所有 thread 叫醒,接著透過
rt_object_delete
刪除 mutex
靜態記憶體管理#
功能 | 回傳值 |
---|---|
刪除 mutex | RT_EOK |
mutex |
---|
欲刪除的 mutex |
549 | /** |
- 這裡則運用
rt_object_detach
刪除
使用 mutex#
- 呼叫
rt_mutex_take
來取得鎖
功能 | 回傳值 |
---|---|
要求 mutex | RT_EOK |
mutex |
time |
---|---|
欲要求的 mutex | 等待時間(如果需要) |
641 | /** |
- 下面將會修改 mutex 的一些資料,這裡先將中斷關閉
667 | RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent))); |
- 若此 mutex 的擁有者與要求著相同,持有數加 1
681 | else |
- 如果不同,且 mutex 可用,先將 value
--
- 設定所有者,紀錄當前權限,持有數加 1
697 | else |
- 如果不可用,且不等待,則啟用中斷,
return -RT_ETIMEOUT
710 | else |
- 若需要等待:
- 為了避免優先權翻轉的情形發生,如需等待的 thread 的優先級大於持有 mutex 的優先級,提升持有者的
724 | /* suspend current thread */ |
- 插入 suspend list,並啟動一個 timeout 為 time 的 timer
742 | /* enable interrupt */ |
- 開啟中斷,並做一次調度
747 | if (thread->error != RT_EOK) |
- 如果因為中斷再次回到此 thread,重新要一次 mutex
755 | else |
- 還鎖則使用
rt_mutex_release
功能 | 回傳值 | mutex |
---|---|---|
釋放 mutex | RT_EOK |
欲釋放的 mutex |
778 | /** |
- 下面將會修改 mutex 的一些資料,這裡先將中斷關閉
806 | RT_DEBUG_LOG(RT_DEBUG_IPC, |
- 檢查歸還者是否為擁有者
822 | /* decrease hold */ |
- 持有數減 1
824 | /* if no hold */ |
- 若已不再擁有此 mutex,且優先權有被更改過,調整回來
834 | /* wakeup suspended thread */ |
- 若有人在等待此 mutex,將 mutex 傳遞給第一個正在等待的 thread
855 | else |
- 如果沒有人在等,value 加 1,將資料初始化
865 | /* enable interrupt */ |
- 開啟中斷,並視情況做一次調度
事件#
- 可實現一對多,多對多
- 僅用來同步,無傳輸的功能
實作#
- thread 的結構中有一個 32 位的事件標記,一個事件的資訊
File: rtdef.h
530 |
|
- 標記的每一位代表一個事件,資訊包含 AND、OR 及 CLEAR
- 當事件標記的第 2、4 位為 1,其餘為 0,代表此 thread 設置第 2、4 個事件
- AND:即需同時接收到 2 號與 4 號事件才會被喚醒
- OR:只需接收到一個
- CLEAR:表示接收完事件喚醒後,是否須將標記清除
結構#
630 |
|
File: ipc.c
建立事件#
動態記憶體管理#
功能 | 回傳值 |
---|---|
建立事件 | 事件 |
*name |
flag |
---|---|
名字 | FIFO / PRIO |
957 | /** |
- 一樣 allocate 記憶體,填入 flag,初始化,最後設定值為 0
靜態記憶體管理#
功能 | 回傳值 |
---|---|
初始化事件 | RT_EOK |
event |
*name |
flag |
---|---|---|
事件本體 | 名字 | FIFO / PRIO |
901 | /** |
- 這裡則不需要 allocate
刪除事件#
靜態記憶體管理#
功能 | 回傳值 | event |
---|---|---|
刪除事件 | RT_EOK |
欲刪除的事件 |
989 | /** |
- 相同的,需要先將正在等待此事件的 thread 叫醒,再刪除
靜態記憶體管理#
功能 | 回傳值 | event |
---|---|---|
刪除事件 | RT_EOK |
欲刪除的事件 |
932 | /** |
- 這裡則用
rt_object_detach
傳遞事件#
功能 | 回傳值 |
---|---|
傳遞事件 | RT_EOK |
event |
set |
---|---|
欲傳遞的事件 | 事件編號 |
1016 | /** |
- 下面會修改事件的資料,這裡先將中斷關閉
1044 | /* set event */ |
- 設定事件編號
1046 | RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(event->parent.parent))); |
- 如果有人在等待此事件,且滿足條件時,設定為 OK
- 這裡為 AND,即事件編號應與 thread 所設定的一致
1066 | else if (thread->event_info & RT_EVENT_FLAG_OR) |
- 若為 OR,則只需有一位相同即可
1077 | /* move node to the next */ |
- 接著走向下一顆
1079 | /* condition is satisfied, resume thread */ |
- 如有人滿足條件,且被設定 CLEAR,清除其標記位
1085 | /* resume thread, and thread list breaks out */ |
- 並恢復此 thread,設定待會需要調度
1093 | /* enable interrupt */ |
- 最後開啟中斷,視情況做一次調度
接收事件#
功能 | 回傳值 |
---|---|
接收事件 | RT_EOK |
event |
set |
option |
timeout |
*recved |
---|---|---|---|---|
欲接收的事件 | 事件編號 | AND /OR | 等待時間(如果需要) | 傳遞成功的事件號碼 |
1110 | /** |
- 下面會修改事件的資料,這裡先將中斷關閉
1153 | /* check event set */ |
- 如果滿足條件,表示已接收到事件,設定為 OK
1169 | if (status == RT_EOK) |
- 如果已接收到事件,設定 recved 參數
- 視情況看需不需要清除標記
1179 | else if (timeout == 0) |
- 若需等待事件,但 timeout 為 0
- 即不等待,將錯誤碼設為 TIMEOUT
1184 | else |
- 如欲等待,將資訊掛在 thread 的結構上
1189 | /* put thread to suspended thread list */ |
- 並插入等待的鏈上
1193 | /* if there is a waiting timeout, active thread timer */ |
- 啟動一個 timer
1202 | /* enable interrupt */ |
- 最後開啟中斷,並做一次調度
1207 | if (thread->error != RT_EOK) |
- 最終接收到事件,一樣設定 recved 參數,回傳錯誤碼