信號
- 1.信號是什么?
- 2.信號的種類
- 3.信號的產生
- 3.1硬體產生
- 3.2軟體產生
- 4.信號的注冊
- 4.1非可靠信號的注冊
- 4.2可靠信號的注冊
- 5.信號的注銷
- 5.1非可靠信號的注銷
- 5.2可靠信號的注銷
- 6.信號阻塞
- 6.1信號是怎樣阻塞的?
- 6.2sigprocmask
- 7.信號未決
- 7.1未決概念
- 7.2sigpending
- 8.信號的處理方式
- 8.1signal函式
- 8.2sigaction函式
- 8.3自定義信號處理的流程
- 9.信號的捕捉
- 9.1信號捕捉的條件
- 9.2信號捕捉流程
- 10.常用信號集操作函式
- 11.SIGCHLD信號
1.信號是什么?
信號其實就是一個軟體中斷,
例:
- 輸入命令,在Shell下啟動一個前臺行程,
- 用戶按下Ctrl-C,鍵盤輸入產生一個硬體中斷,
- 如果CPU當前正在執行這個行程的代碼,則該行程的用戶空間代碼暫停執行, CPU從用戶
態切換到內核態處理硬體中斷,- 終端驅動程式將Ctrl-C解釋成一個SIGINT信號,記在該行程的PCB中(也可以說發送了一
個SIGINT信號給該行程),- 當某個時刻要從內核回傳到該行程的用戶空間代碼繼續執行之前,首先處理PCB中記錄的
信號,發現有一個SIGINT信號待處理,而這個信號的默認處理動作是終止行程,所以直接
終止行程而不再回傳它的用戶空間代碼執行,
在這個例子中,由ctrl+c產生的硬體中斷就是一個信號,Ctrl+C產生的信號只能發送給前臺行程,命令后加&就可放到后臺運行,
Shell可同時運行一個前臺行程和任意多個后臺行程,只有前臺行程才能接受到像CTRL+C這種控制鍵產生的信號,
2.信號的種類
使用命令查看:kill -l
非可靠信號:1~31號信號,信號可能會丟失
可靠信號:34~64號信號,信號不可能丟失

SIGHUP:1號信號,Hangup detected on controlling terminal or death of controlling process(在控制終端上掛起信號,或讓行程結束),ation:term
SIGINT:2號信號,Interrupt from keyboard(鍵盤輸入中斷,ctrl + c ),action:term
SIGQUIT:3號信號,Quit from keyboard(鍵盤輸入退出,ctrl+ | ),action:core,產生core dump檔案
SIGABRT:6號信號,Abort signal from abort(3)(非正常終止,double free),action:core
SIGKILL:9號信號,Kill signal(殺死行程信號),action:term,該信號不能被阻塞、忽略、自定義處理
SIGSEGV:11號信號,Invalid memory reference(無效的記憶體參考,解參考空指標、記憶體越界訪問),action:core
SIGPIPE:13號信號,Broken pipe: write to pipe with no readers(管道中止: 寫入無人讀取的管道,會導致管道破裂),action:term
SIGCHLD:17號信號,Child stopped or terminated(子行程發送給父行程的信號,但該信號為忽略處理的)
SIGSTOP:19號信號,Stop process(停止行程),action:stop
SIGTSTP:20號信號,Stop typed at terminal(終端上發出的停止信號,ctrl + z),action:stop
具體的信號采取的動作和詳細資訊可查看:man 7 signal
3.信號的產生
3.1硬體產生
硬體產生即通過終端按鍵產生的信號:
- ctrl + c:SIGINT(2),發送給前臺行程,& 行程放到后臺運行,fg 把剛剛放到后臺的行程,再放到前臺來運行
- ctrl + z:SIGTSTP(20),一般不用,除非有特定場景
- ctrl + | :SIGQUIT(3),產生core dump檔案
產生core dump檔案的條件:
- 當前OS一定不要限制core dump檔案的大小,ulimit -a
- 磁盤空間要足夠
- 如何產生:
3.1 解參考空指標,收到11號信號,產生core dump檔案
3.2 記憶體訪問越界,程式一旦崩潰,就會收到11號信號,也就會產生core dump檔案
3.3 double free,收到6號信號,并產生core dump,
3.4 free(NULL),不會崩潰
3.2軟體產生
軟體產生即呼叫系統函式向行程發信號
- kill函式
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
引數解釋:
pid:行程號
sig:要發送的信號值
回傳值:成功回傳0,失敗回傳-1,并設定錯誤
- kill命令:
kill -[信號] pid, - abort:
void abort(void);,收到6號信號,誰呼叫該函式,誰就收到信號 - alarm:
unsigned int alarm(unsigned int seconds);,收到14號信號,告訴內核在seconds秒后給行程發送SIGALRM信號,該信號默認處理動作為終止當前行程,
4.信號的注冊
信號注冊又分為可靠信號的注冊和非可靠信號的注冊,
信號注冊實際上是一個位圖和一個sigqueue佇列,

4.1非可靠信號的注冊
當行程收到非可靠信號時:
- 將非可靠信號對應的位元位置為1
- 添加sigqueue節點到sigqueue佇列當中,但是,在添加sigqueue節點的時候,佇列當中已然有了該信號的sigqueue節點,則不添加
4.2可靠信號的注冊
當行程所受到可靠信號時:
- 在sig位圖中更改信號對應的位元位為1
- 不論之前sigqueue佇列中是否存在該信號的sigqueue節點,都再次添加sigqueue節點到sigqueue佇列當中去
5.信號的注銷
5.1非可靠信號的注銷
- 信號對應的位元位從1置為0
- 將該信號的sigqueue節點從sigqueue佇列當中進行出隊操作
5.2可靠信號的注銷
- 將該信號的sigqueue節點從sigqueue佇列當中進行出隊操作
- 需要判斷sigqueue佇列當中是否還有相同的sigqueue節點:
①沒有了:信號位元位從1置為0
②還有:不會更改sig位圖中的位元位
6.信號阻塞
6.1信號是怎樣阻塞的?

- 信號的阻塞,并不會干擾信號的注冊,信號能注冊,但不能被立即處理,
- 將block位圖中對應的信號位元位置為1,表示阻塞該信號
- 行程收到該信號,還是一如既往的注冊
- 當行程進入到內核空間,準備回傳用戶空間的時候,呼叫do_signal函式,就不會立即去處理該信號了
- 當該信號不被阻塞后,就可以進行處理了
6.2sigprocmask
函式原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
引數解釋:
- how,該做什么樣的操作
SIG_BLOCK:設定信號為阻塞
SIG_UNBLOCK:解除信號阻塞
SIG_SETMASK:替換阻塞位圖- set:用來設定阻塞位圖
SIG_BLOCK:設定某個信號為阻塞,block(new) = block(old) | set
SIG_UNBLOCK:解除某個信號阻塞,block(new)= block(old) & (~set)
SIG_SETMASK:替換阻塞位圖,block(new)= set- oldset:原來的阻塞位圖
例:下述例子,信號全部被阻塞,采用kill -9,將該行程結束掉

7.信號未決
7.1未決概念
實際執行信號的處理動作稱為信號遞達(Delivery),信號從產生到遞達之間的狀態,稱為信號未決(Pending),
行程可以選擇阻塞(Block)某個信號,被阻塞的信號產生時將保持在未決狀態,直到行程解除對此信號的阻塞,才執行遞達的動作,注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是、在遞達之后可選的一種處理動作,
7.2sigpending
函式原型:int sigpending(sigset_t *set);
讀取當前行程的未決信號集,通過set引數傳出,呼叫成功回傳0,出錯回傳-1.
例:

8.信號的處理方式

每個信號都有兩個標志位分別表示阻塞和未決,還有一個函式指標表示處理動作,
在上述例子中:
- SIGHUP信號未阻塞也未產生過,當它遞達時執行默認處理動作,
- SIGINT信號產生過,但正在被阻塞,所以暫時不能遞達,雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為行程仍有機會改變處理動作之后再解除阻塞,
- SIGQUIT信號未產生過,一旦產生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函式sighandler,
8.1signal函式
該函式可以更改信號的處理動作,
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
引數解釋:
- signum:更改的信號值
- handler:函式指標,要更改的動作是什么
實際上,該函式內部也呼叫了sigaction函式,
8.2sigaction函式
讀取和修改與指定信號相關聯的處理動作,
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
引數解釋:
- signum:待更改的信號值
struct sigaction結構體:
void (*sa_handler)(int);//函式指標,保存了內核對信號的處理方式
void (*sa_sigaction)(int, siginfo_t *, void *);//
sigset_t sa_mask;//保存的是當行程在處理信號的時候,收到的信號
int sa_flags;//SA_SIGINFO,OS在處理信號的時候,呼叫的就是sa_sigaction函式指標當中
//保存的值0,在處理信號的時候,呼叫sa_handler保存的函式
void (*sa_restorer)(void);
例:

8.3自定義信號處理的流程

task_struct結構體中有一個struct sighand_struct結構體,struct sighand_struct結構體有一個struct k_sigaction action[_NSIG]結構體陣列,- 該陣列中,其中的
_sighandler_t sa_handler保存的是信號的處理方式,通過改變其指向,可以實作我們對自定義信號的處理,
9.信號的捕捉
9.1信號捕捉的條件
如果信號的處理動作是用戶自定義函式,在信號遞達時就呼叫這個函式,這就稱為信號捕捉,
9.2信號捕捉流程

內核態回傳用戶態會呼叫do_signal函式,兩種情況:
- 無信號:sys_return函式,回傳用戶態
- 有信號:先處理信號,信號回傳,再呼叫do_signal函式
例:
- 程式注冊了SIGQUIT信號的處理函式sighandler,
- 當前正在執行main函式,這時發生中斷或例外切換到內核態,
- 在中斷處理完畢后要回傳用戶態的main函式之前檢查到有信號SIGQUIT遞達,
- 內核決定回傳用戶態后不是恢復main函式的背景關系繼續執行,而是執行sighandler函式, sighandler和main函式使用不同的堆疊空間,它們之間不存在呼叫和被呼叫的關系,是兩個獨立的控制流程,
- sighandler函式回傳后自動執行特殊的系統呼叫sigreturn再次進入內核態,
- 如果沒有新的信號要遞達,這次再回傳用戶態就是恢復main函式的背景關系繼續執行了,
10.常用信號集操作函式
int sigemptyset(sigset_t *set);://將位元位圖全置為0
int sigfillset(sigset_t *set);//將位元位圖全置為1
int sigaddset(sigset_t *set, int signum);//將該set位圖,多少號信號置為1
int sigdelset(sigset_t *set, int signum);//將該set位圖,多少號信號置為0
int sigismember(const sigset_t *set, int signum);//信號signum是否是set位圖中的信號
11.SIGCHLD信號
該信號是子行程在結束是發送給父行程的信號,但是該信號的處理方式是默認處理的,
父行程對子行程發送過來的SIGCHLD信號進行了忽略處理,就會導致子行程成為僵尸行程,
可以自定義該信號的處理方式:

指令查看后臺:ps aux | grep ./fork

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/225454.html
標籤:其他
