關於
本文件說明執行緒信號,這是一種用於保護執行緒的 Zircon 核心機制 實作執行緒暫停和終止作業。執行緒訊號不相關 物件信號。
目標對像是核心開發人員,以及任何有意瞭解的使用者 暫停與終止作業在核心中的運作方式
停權並終止要求
暫停和終止是指可對執行緒執行的作業。您可以 作業為非同步性質,呼叫端必須等待作業完成 完成。這些作業會在核心內實作為執行個體 Thread 結構體上的方法:
Thread::Suspend
:要求執行緒暫停執行作業,直到
是使用 Thread::Resume 繼續執行暫停用於執行
偵錯工具暫停之後,執行緒的註冊狀態可變為
讀取/寫入要求。使用者會看到這項作業
模式。zx_task_suspend()
。
Thread::Kill
:要求執行緒自行終止。這個
不會直接顯示在使用者模式中。也就是嘗試
至 zx_task_kill()
執行緒是錯誤,不過,這項作業
代表經由程序銷毀,間接暴露於
自願性。
請注意,這兩種作業都會描述為要求。來電者是 要求目標暫停,或者在終止的情況下終止其 呼叫端無法強制暫停或終止 目標。目標無法拒絕要求,但可能會延遲動作,直到 適當的時間和地點這是設計的重要元素。
若要瞭解這些作業為何屬於請求,請考慮使用 強制終止或暫停執行緒。如果執行緒在執行期間遭到強制終止 拿起資源 (例如互斥鎖) 也無法 資源就會遭到刪除您可能會因為記憶體不足而永久流失 以及鎖定、資料結構毀損等問題
模擬終止及暫停作業,做為只能由 我們為目標執行緒提供了一種方法,讓目標能釋放資源,並對其執行 任何必要清理作業都會暫時發生 (如果是 暫停) 或永久 (終止時)。
安全點數
在介紹如何發出終止和暫停要求前,先來談談 執行緒終止的安全性
執行緒隨時可以放心暫停或終止。 「邊緣」才傳回核心 返回使用者模式返回使用者模式之前,執行緒會解開 呼叫堆疊,執行任何 RAII 物件的解構函式。到了 即將返回使用者模式 核心堆疊。執行緒可以安全地暫停或終止 和執行作業
具體來說,執行緒可暫停或暫停兩次的安全點。 終止服務。這類呼叫剛好是從系統呼叫返回使用者模式, 才會從例外狀況/錯誤/中斷處理常式返回使用者模式 (簡稱為例外狀況處理常式)。
請注意,在使用者模式中執行時,並非只是叫用例外狀況處理常式。他們 您也可以在核心模式下執行時叫用。返回 外部核心模式,因此在核心模式下暫停或終止,並不安全 但網路脈絡仍可能包含資源換句話說,例外狀況處理常式 才是安全的時間點。
正在傳送信號
我們知道殺死和暫停是只是被動提出,完全取決於 目標執行緒,以決定處理要求的時間和方式。我們也瞭解 是執行緒的唯一安全位置,就在 返回使用者模式之前執行緒信號如何? 哪些方面?
執行緒信號是要求暫停和終止機制的機制。每項
執行緒物件中有一個欄位含有一組聲明信號。還有一點
暫停 THREAD_SIGNAL_SUSPEND
,加上一些可殺死的 THREAD_SIGNAL_KILL
。
透過設定 在目標執行緒物件上設定適當的位元後,再根據目標的 取而代之,就是在某些事情上 。實際的輪輻類型取決於目標執行緒的狀態: 睡眠/封鎖、暫停或跑步請注意,目前有兩種 休眠/封鎖、不中斷且不中斷我們會將重點放在 可中斷且忽略不中斷
睡眠或已封鎖
如果目標執行緒處於休眠或封鎖狀態,表示該執行緒並未執行,
但位於核心裡由於只有執行中的執行緒可以檢查其信號
必須喚醒或解除封鎖。當執行緒遭解除封鎖或喚醒時,系統會
zx_status_t
。這個值通常為 ZX_OK
或 ZX_ERR_TIMED_OUT
。不過
提前喚醒執行緒時,我們會使用特殊的 zx_status_t
值。
ZX_ERR_INTERNAL_INTR_KILLED
適用於終止作業,
ZX_ERR_INTERNAL_INTR_RETRY
適用於暫停作業。
執行緒喚醒/解除封鎖時,會看到 zx_status_t
結果並開始
從核心返回,並展開堆疊。一般來說,任何核心函式
傳回兩個特殊值之一時,呼叫端會立即
傳回的值
最後,當堆疊失誤時,執行緒會位於邊緣
點。在這裡,執行緒會在返回使用者模式前
並呼叫
arch_iframe_process_pending_signals()
或
x86_syscall_process_pending_signals()
。
暫停使用
如同休眠/遭封鎖的情況,執行緒必須按照順序恢復執行
才算被終止如果是終止的情況,系統會透過下列方法解除封鎖執行緒
ZX_ERR_INTERNAL_INTR_KILLED
並放鬆,直到返回使用者前
模式會據此回應信號
執行中
目標執行緒可以執行使用者程式碼或核心程式碼。正在執行 使用者程式碼,就必須強制要求程式碼進入核心,以便檢查 其 Thread 結構的信號欄位。如果執行的是核心程式碼 這必須確保系統能在合理的時間範圍內,檢查是否有待處理的信號。
傳送者無法得知目標是否為核心模式或使用者模式,因此可以運作 都是一樣的傳送方會將處理序間中斷 (IPI) 傳送至 以及目標目前正在執行的 CPU部分中斷內容 處理常式工作是檢查並選擇性處理待處理信號。
如果在使用者環境中叫用處理常式,也就是 CPU 處於使用者模式
在連線中斷時,暫停/終止也是安全的地方
處理常式將呼叫 arch_iframe_process_pending_signals()
。
然而,如果已在核心環境中叫用處理常式,則處理常式將 不執行任何動作,因為根本無法得知執行緒的狀態 已中斷。在此暫停/終止並不安全。而是將 會返回叫用核心結構定義,由此可見 外部關聯資訊,最終會注意到信號並到達安全點。
您可能會好奇,IPI 是否為必要性。有兩種情況
因此至關重要第一種是目標執行緒在使用者模式下執行時
不必自行進入核心即可網站上載入的系統沒有
其他中斷流量,執行緒可能無法長時間進入核心
或是在無限迴圈的情況下在這個例子中,我們需要 IPI
確保目標執行緒觀測及處理
以便即時處理問題第二種是,目標執行緒執行了較長時間
正在核心中運作,但不會檢查待處理信號。這些
但確實存在最佳範例是執行訪客 OS
透過 zx_vcpu_enter()
。中斷會導致 VMEXIT 傳回主機
可用來檢查待處理信號及放鬆的狀態。
統整
以下將舉例說明運作方式。假設執行緒 A 是
暫停執行緒 B,因為 B 正在執行 zx_port_wait()
。視乎
確切的執行時間,我們最後可以
不同的情境以下將簡單介紹各種情境。
情境 1:在 syscall 之前暫停,以使用者模式執行
執行緒 A 會在執行緒 B 開始 zx_port_wait()
前就暫停暫停作業
syscall。執行緒 B 仍在使用者模式下,正在執行中。執行緒 A 設定執行緒
B 的 THREAD_SIGNAL_SUSPEND
位元,並發出 IPI 至執行緒 B 目前的 CPU。
Thread B 的 CPU 會釋放中斷,並呼叫中斷處理常式。早於
返回使用者模式,執行緒 B 會檢查其待處理信號。看到這樣
THREAD_SIGNAL_SUSPEND
已設定,目前會自動暫停。以下是執行緒的討論串
B 的呼叫堆疊:
suspend_self()
interrupt_handler()
---- interrupt ----
user code
之後繼續執行版本後,執行緒 B 會回到使用者模式,就好像 什麼都不用做
情境 2:在系統呼叫期間暫停,但尚未封鎖
執行緒 A 會在執行緒 B 進入核心後,對暫停作業發出暫停通知
執行 zx_port_wait()
syscall。Thread B 正在執行核心
且尚未遭到封鎖。就像情境 1 一樣,執行緒 A 會
IPI,因此執行緒 B 會檢查待處理信號:
interrupt_handler()
---- interrupt ----
PortDispatcher::Dequeue()
sys_port_wait()
syscall_dispatch()
---- syscall ----
vdso
zx_port_wait()
user code
不過,這次中斷處理常式發現已在核心中叫用
而不是使用者情境,因此這麼做也不必將自身停權。改為使用
會傳回叫用的核心內容。執行緒 B 達到
zx_port_wait()
作業的核心,也就是系統不提示時封鎖的時間點
沒有可用的封包。執行緒 B 發現沒有可用的封包
並準備封鎖:
WaitQueue::BlockEtcPreamble()
WaitQueue::BlockEtc()
PortDispatcher::Dequeue()
sys_port_wait()
syscall_dispatch()
---- syscall ----
vdso
zx_port_wait()
user code
封鎖前,系統會檢查待處理信號,會判斷
要求暫停。而非封鎖,會傳回 ZX_ERR_INTERNAL_INTR_RETRY
而呼叫堆疊則會展開至邊緣,然後再返回使用者模式:
WaitQueue::BlockEtcPreabmle() ZX_ERR_INTERNAL_INTR_RETRY
WaitQueue::BlockEtc() |
PortDispatcher::Dequeue() |
sys_port_wait() |
syscall_dispatch() V
---- syscall ----
vdso
zx_port_wait()
user code
執行緒會檢查待處理的信號並自行暫停,會
已恢復,執行緒會回到使用者模式 (返回 vDSO),並顯示狀態結果
ZX_ERR_INTERNAL_INTR_RETRY
。vDSO 有處理特殊邏輯
傳回 ZX_ERR_INTERNAL_INTR_RETRY
的系統呼叫只會重新發出
Scall 是原始引數的 syscall:
suspend_self() ZX_ERR_INTERNAL_INTR_RETRY
syscall_dispatch() |
---- syscall ---- | A
vdso |______|
zx_port_wait()
user code
情境 3:在核心中遭到封鎖時暫停
執行緒 A 會在執行緒 B 進入核心並進行封鎖後,發出暫停的問題。
等待通訊埠封包執行緒 A 發現執行緒 B 遭到封鎖,因此
使用 ZX_ERR_INTERNAL_INTR_RETRY
值解除封鎖執行緒 B。從這裡開始
是否符合情境 2 的預期行為呼叫會返回使用者模式,其中
則是由 vDSO 重試:
blocked ZX_ERR_INTERNAL_INTR_RETRY
WaitQueue::BlockEtcPostamble() |
WaitQueue::BlockEtc() |
PortDispatcher::Dequeue() |
sys_port_wait() |
syscall_dispatch() |
---- syscall ---- | A
vdso |______|
zx_port_wait()
user code
情境 4:解除封鎖後,從核心返回前暫停
當執行緒 B 遭到封鎖時,正在等候通訊埠封包、封包送達
正在解除封鎖 (使用 ZX_OK
):
blocked ZX_OK
WaitQueue::BlockEtcPostamble() |
WaitQueue::BlockEtc() |
PortDispatcher::Dequeue() V
sys_port_wait()
syscall_dispatch()
---- syscall ----
vdso
zx_port_wait()
user code
執行緒 A 發出暫停問題時,執行緒 B 現在會展開至使用者模式。 執行緒 A 會設定位元,您會看到執行緒 B 標示為執行中,以便 IPI。類似「僅在 syscall 之前暫停」導致中斷處理常式 執行作業:
interrupt_handler()
---- interrupt ----
PortDispatcher::Dequeue()
sys_port_wait()
syscall_dispatch()
---- syscall ----
vdso
zx_port_wait()
user code
不過,由於處理常式,這次不會檢查待處理信號
中斷的核心內容,而非使用者情境。處理常式完成後
執行緒 B 會繼續放鬆。最後,執行緒 B 會觸及邊緣
即將從 Syscall 返回使用者模式這裡會檢查待處理
信號,可查看 THREAD_SIGNAL_SUSPEND
,並自行暫停:
suspend_self()
syscall_dispatch()
---- syscall ----
vdso
zx_port_wait()
user code
重新啟用後,將返回使用者模式,並顯示
已解除封鎖 (ZX_OK
):
syscall_dispatch() ZX_OK
---- syscall ---- |
vdso V
zx_port_wait()
user code
重點回顧
要點如下:
您無法強制暫停或終止執行緒。您只能要求停用帳戶權限 甚至終止服務
執行緒信號是要求執行緒暫停或終止的機制。
執行緒只應在特定時間點暫停或終止執行 在核心內執行這些動作特別是,執行緒只能暫停或終止 作業會在未保留資源 (例如鎖定) 且即將從 從核心模式切換至使用者模式
為保持回應速度,長時間執行的核心作業必須 定期檢查待處理信號,如果已設定,則傳回。