代表行程接收到 signal 後會產生什麼行為,並且每個行程有各自的 signal disposition ,基本的預設行為有下列幾種
行程可以透過 sigaction(2) 改變 signal disposition ,有以下幾種行為可以選擇
如果利用 signal handler 來處理該 signal ,預設是將該 signal handler 指向的函式的函式資訊框 (function frame,簡稱 fp) 加在原本行程的堆疊當中,也可用 signalstack(2) 使該函式資訊框使用其他的堆疊。
Linux 核心在每次轉移回使用者模式時,都會檢查是否有正在等待的 signal 並檢查是否有對應的 signal handler,如果有的話會進行以下動作
sigprocmask()
來建立在 act->sa_mask
當中的 signal 都會被加到該執行緒的 signal mask 當中。sigreturn()
,恢復執行緒的狀態回到呼叫 signal handler 之前,然後 Linux 核心把控制權轉回給使用者模式,繼續執行 signal handler 被呼叫之前的指令。如果 signal handler 因為 siglongjmp()
或使用 execve()
啟動新的程式而沒有回傳,則不會回傳 signal trampoline 也不會呼叫 sigreturn()
,此時這是開發者的責任要去恢復 signal mask 的狀態。
以 Linux 核心的角度來看,它不知道 signal handler 跟其他使用者層級的程式碼之間的差別,自然也不知道目前執行的是誰,所有 signal handler 需要的相關資訊都儲存在使用者層級的暫存器和堆疊中,所以巢狀呼叫的 signal handler 數量上限是被使用者層級的堆疊大小所限制。
sigaction() 系統呼叫可以改變行程接收到特定 signal 時的行為。
signum
是用來指定該 signal 的數字(不能指定 SIGKILL
和 SIGSTOP
)。
struct sigaction
定義如下
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
特別注意到 sa_restorer
並非是給使用者使用的,詳見 sigreturn(2) 。
如上節所說的, sa_handler
是行程接收到 signum
這個 signal 時會採取的行為
SIG_DFL
代表採取預設行為SIG_IGN
代表忽略該 signalsa_flags
中設定 SA_SIGINFO
,則 sa_sigaction
就指向處理 signum
的 signal-hadling functionsignal handler 的函式定義如下
void handler(int sig, siginfo_t *info, void *ucontext);
以下解說參數
sig
: 觸發此 handler function 的 signal number (和 signum
相同)info
: 指向 siginfo_t
的指標,該結構體包含 signal 的詳細資訊。詳見 sigaction(2) 。ucontext
: a pointer to a ucontext_t
structure, cast to void *
。此指標指向的結構體包含了 Linux 核心 存在 userspace stack 中的 signal context information 。通常此參數不會被使用到,詳見 getcontext(3) 。sa_mask
則代表在 signal handler 執行期間會被阻塞著的 signals ,並且觸發 signal handler 的 signal 本身也會被阻塞。
sa_flags
代表改變該 signal 行為的標籤集合,有數種標籤詳見 sigaction(2)
在 泛 Sytem V 環境當中,我們可以透過以下幾種結構體和函式來控制行程當中不同執行緒的 user-level context switch 。
mcontext_t
: machine-dependent 且不開放給使用者操作。ucontext_t
: 定義如下typedef struct ucontext_t {
struct ucontext_t *uc_link;
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
...
} ucontext_t;
uc_link
: 指向在目前 context 被終止後會接續執行的 context 。uc_sigmask
: 被此 context 阻塞的 signals 集合。uc_stack
: 此 context 使用的堆疊空間。uc_mcontext
: 被儲存的 context 的 machine-specific 表示法,包含 calling thread's machine registers 。getcontext()
會將 ucp
指向的結構體初始化並指向目前執行的 context 。
setcontext()
會繼續執行 ucp
指向的 user context ,此 context 有三種方式獲取
getcontext()
makecontext()
最早實現 user-level context switch 此機制的方式是透過 setjmp()
/longjmp()
,不過這樣的方式沒有定義如何處理 signal context ,之後 sigsetjmp()
/siglongjmp()
讓我們可以處理 signal context 。當 signal 觸發某個 signal handler 時,目前的 user context 會被暫存而 Linux 核心 會為 signal handler 建立一個新的 user context 。我們不該使用 longjmp()
函式來離開 signal handler ,這樣的行為是未定義的,取而代之應該使用 siglongjmp()
或 setcontext()
。
注意到除非我們自己設置額外儲存裝置進行記錄,不然我們無從得知 getcontext()
的回傳行為是從首次呼叫 getcontext()
還是透過 setcontext()
來回傳。因為 register 會被重新恢復,所以利用 register variable 來記錄是無效的。