目錄
- 信號的介紹
- 信號的機制
- 信號的編號
- Linux常規信號一覽表
- 信號的產生
- 終端按鍵產生信號
- 硬件異常產生信號
- kill函數/命令產生信號
- 信號的操作函數
- 信號集設定
- sigprocmask函數
- sigpending函數
- 信號的捕捉
- signal函數
- sigaction函數
- 內核實現信號捕捉的過程
- 信號的傳參
- 捕捉函數傳參
信號的機制
A給B發送信號,B收到信號之前執行自己的代碼,收到信號后,不管執行到程序的什么位置,都要暫停運行,去處理信號,處理完畢再繼續執行。與硬件中斷類似——異步模式。但信號是軟件層面上實現的中斷,早期常被稱為“軟中斷”。
信號的特質:由於信號是通過軟件方法實現,其實現手段導致信號有很強的延時性。但對於用戶來說,這個延遲時間非常短,不易察覺。
每個進程收到的所有信號,都是由內核負責發送的,內核處理。
信號的編號
可以使用kill –l命令查看當前系統可使用的信號有哪些。
-
SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
-
SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
-
SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
-
SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
-
SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
-
SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
-
SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
-
SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
-
SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
-
SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
-
SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
-
SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
-
SIGRTMAX-1 64) SIGRTMAX
不存在編號為0的信號。其中1-31號信號稱之為常規信號(也叫普通信號或標准信號),34-64稱之為實時信號,驅動編程與硬件相關。名字上區別不大。而前32個名字各不相同。
這里特別強調了9)SIGKILL 和19) SIGSTOP信號,不允許忽略和捕捉,只能執行默認動作。甚至不能將其設置為阻塞。
Linux常規信號一覽表
1) SIGHUP: 當用戶退出shell時,由該shell啟動的所有進程將收到這個信號,默認動作為終止進程
- SIGINT:當用戶按下了<Ctrl+C>組合鍵時,用戶終端向正在運行中的由該終端啟動的程序發出此信號。默認動
作為終止進程。
- SIGQUIT:當用戶按下<ctrl+>組合鍵時產生該信號,用戶終端向正在運行中的由該終端啟動的程序發出些信
號。默認動作為終止進程。
-
SIGILL:CPU檢測到某進程執行了非法指令。默認動作為終止進程並產生core文件
-
SIGTRAP:該信號由斷點指令或其他 trap指令產生。默認動作為終止里程 並產生core文件。
-
SIGABRT: 調用abort函數時產生該信號。默認動作為終止進程並產生core文件。
-
SIGBUS:非法訪問內存地址,包括內存對齊出錯,默認動作為終止進程並產生core文件。
-
SIGFPE:在發生致命的運算錯誤時發出。不僅包括浮點運算錯誤,還包括溢出及除數為0等所有的算法錯誤。默認動作為終止進程並產生core文件。
-
SIGKILL:無條件終止進程。本信號不能被忽略,處理和阻塞。默認動作為終止進程。它向系統管理員提供了可以殺死任何進程的方法。
-
SIGUSE1:用戶定義 的信號。即程序員可以在程序中定義並使用該信號。默認動作為終止進程。
-
SIGSEGV:指示進程進行了無效內存訪問。默認動作為終止進程並產生core文件。
-
SIGUSR2:另外一個用戶自定義信號,程序員可以在程序中定義並使用該信號。默認動作為終止進程。
-
SIGPIPE:Broken pipe向一個沒有讀端的管道寫數據。默認動作為終止進程。
-
SIGALRM: 定時器超時,超時的時間 由系統調用alarm設置。默認動作為終止進程。
-
SIGTERM:程序結束信號,與SIGKILL不同的是,該信號可以被阻塞和終止。通常用來要示程序正常退出。執行shell命令Kill時,缺省產生這個信號。默認動作為終止進程。
-
SIGSTKFLT:Linux早期版本出現的信號,現仍保留向后兼容。默認動作為終止進程。
-
SIGCHLD:子進程結束時,父進程會收到這個信號。默認動作為忽略這個信號。
-
SIGCONT:如果進程已停止,則使其繼續運行。默認動作為繼續/忽略。
-
SIGSTOP:停止進程的執行。信號不能被忽略,處理和阻塞。默認動作為暫停進程。
-
SIGTSTP:停止終端交互進程的運行。按下<ctrl+z>組合鍵時發出這個信號。默認動作為暫停進程。
-
SIGTTIN:后台進程讀終端控制台。默認動作為暫停進程。
-
SIGTTOU: 該信號類似於SIGTTIN,在后台進程要向終端輸出數據時發生。默認動作為暫停進程。
-
SIGURG:套接字上有緊急數據時,向當前正在運行的進程發出些信號,報告有緊急數據到達。如網絡帶外數據到達,默認動作為忽略該信號。
-
SIGXCPU:進程執行時間超過了分配給該進程的CPU時間 ,系統產生該信號並發送給該進程。默認動作為終止進程。
-
SIGXFSZ:超過文件的最大長度設置。默認動作為終止進程。
-
SIGVTALRM:虛擬時鍾超時時產生該信號。類似於SIGALRM,但是該信號只計算該進程占用CPU的使用時間。默認動作為終止進程。
-
SGIPROF:類似於SIGVTALRM,它不公包括該進程占用CPU時間還包括執行系統調用時間。默認動作為終止進程。
-
SIGWINCH:窗口變化大小時發出。默認動作為忽略該信號。
-
SIGIO:此信號向進程指示發出了一個異步IO事件。默認動作為忽略。
-
SIGPWR:關機。默認動作為終止進程。
-
SIGSYS:無效的系統調用。默認動作為終止進程並產生core文件。
-
SIGRTMIN ~ (64) SIGRTMAX:LINUX的實時信號,它們沒有固定的含義(可以由用戶自定義)。所有的實時信號的默認動作都為終止進程。
信號的產生
終端按鍵產生信號
Ctrl + c → 2) SIGINT(終止/中斷) "INT" ----Interrupt
Ctrl + z → 20) SIGTSTP(暫停/停止) "T" ----Terminal 終端。
Ctrl + \ → 3) SIGQUIT(退出)
硬件異常產生信號
除0操作 → 8) SIGFPE (浮點數例外) "F" -----float 浮點數。
非法訪問內存 → 11) SIGSEGV (段錯誤)
總線錯誤 → 7) SIGBUS
kill函數/命令產生信號
kill命令產生信號:kill -SIGKILL pid
kill函數:給指定進程發送指定信號(不一定殺死)
int kill(pid_t pid, int sig); 成功:0;失敗:-1 (ID非法,信號非法,普通用戶殺init進程等權級問題),設置errno
sig:不推薦直接使用數字,應使用宏名,因為不同操作系統信號編號可能不同,但名稱一致。
pid > 0: 發送信號給指定的進程。
pid = 0: 發送信號給 與 調用kill函數進程屬於同一進程組的所有進程。
pid < 0: 取|pid|發給對應進程組。
pid = -1:發送給進程有權限發送的系統中所有進程。
進程組:每個進程都屬於一個進程組,進程組是一個或多個進程集合,他們相互關聯,共同完成一個實體任務,每個進程組都有一個進程組長,默認進程組ID與進程組長ID相同。
權限保護:super用戶(root)可以發送信號給任意用戶,普通用戶是不能向系統用戶發送信號的。 kill -9 (root用戶的pid) 是不可以的。同樣,普通用戶也不能向其他普通用戶發送信號,終止其進程。 只能向自己創建的進程發送信號。普通用戶基本規則是:發送者實際或有效用戶ID == 接收者實際或有效用戶ID
信號集操作函數
內核通過讀取未決信號集來判斷信號是否應被處理。信號屏蔽字mask可以影響未決信號集。而我們可以在應用程序中自定義set來改變mask。已達到屏蔽指定信號的目的。
信號集設定
sigset_t set; // typedef unsigned long sigset_t;
int sigemptyset(sigset_t *set); 將某個信號集清0 成功:0;失敗:-1
int sigfillset(sigset_t *set); 將某個信號集置1 成功:0;失敗:-1
int sigaddset(sigset_t *set, int signum); 將某個信號加入信號集 成功:0;失敗:-1
int sigdelset(sigset_t *set, int signum); 將某個信號清出信號集 成功:0;失敗:-1
int sigismember(const sigset_t *set, int signum);判斷某個信號是否在信號集中 返回值:在集合:1;不在:0;出錯:-1
sigset_t類型的本質是位圖。但不應該直接使用位操作,而應該使用上述函數,保證跨系統操作有效。
sigprocmask函數
用來屏蔽信號、解除屏蔽也使用該函數。其本質,讀取或修改進程的信號屏蔽字(PCB中)
嚴格注意,屏蔽信號:只是將信號處理延后執行(延至解除屏蔽);而忽略表示將信號丟處理。
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 成功:0;失敗:-1,設置errno
參數:
set:傳入參數,是一個位圖,set中哪位置1,就表示當前進程屏蔽哪個信號。
oldset:傳出參數,保存舊的信號屏蔽集。
how參數取值: 假設當前的信號屏蔽字為mask
-
SIG_BLOCK: 當how設置為此值,set表示需要屏蔽的信號。相當於 mask = mask|set
-
SIG_UNBLOCK: 當how設置為此,set表示需要解除屏蔽的信號。相當於 mask = mask & ~set
-
SIG_SETMASK: 當how設置為此,set表示用於替代原始屏蔽及的新屏蔽集。相當於 mask = set若,調用sigprocmask解除了對當前若干個信號的阻塞,則在sigprocmask返回前,至少將其中一個信號遞達。
sigpending函數
讀取當前進程的未決信號集
int sigpending(sigset_t *set); set傳出參數。 返回值:成功:0;失敗:-1,設置errno
信號捕捉
signal函數
注冊一個信號捕捉函數:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
該函數由ANSI定義,由於歷史原因在不同版本的Unix和不同版本的Linux中可能有不同的行為。因此應該盡量避免使用它,取而代之使用sigaction函數。
void (signal(int signum, void (sighandler_t)(int))) (int);
sigaction函數
修改信號處理動作(通常在Linux用其來注冊一個信號的捕捉函數)
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 成功:0;失敗:-1,設置errno
參數:
act:傳入參數,新的處理方式。
oldact:傳出參數,舊的處理方式。
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:該元素是過時的,不應該使用,POSIX.1標准將不指定該元素。(棄用)
sa_sigaction:當sa_flags被指定為SA_SIGINFO標志時,使用該信號處理程序。(很少使用)
重點掌握:
① sa_handler:指定信號捕捉后的處理函數名(即注冊函數)。也可賦值為SIG_IGN表忽略 或 SIG_DFL表執行默認動作
② sa_mask: 調用信號處理函數時,所要屏蔽的信號集合(信號屏蔽字)。注意:僅在處理函數被調用期間屏蔽生效,是臨時性設置。
③ sa_flags:通常設置為0,表使用默認屬性。
信號捕捉特性
-
進程正常運行時,默認PCB中有一個信號屏蔽字,假定為☆,它決定了進程自動屏蔽哪些信號。當注冊了某個信號捕捉函數,捕捉到該信號以后,要調用該函數。而該函數有可能執行很長時間,在這期間所屏蔽的信號不由☆來指定。而是用sa_mask來指定。調用完信號處理函數,再恢復為☆。
-
XXX信號捕捉函數執行期間,XXX信號自動被屏蔽。
-
阻塞的常規信號不支持排隊,產生多次只記錄一次。(后32個實時信號支持排隊)
內核實現信號捕捉過程:
信號傳參
捕捉函數傳參
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
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_handler而應該使用sa_sigaction。但此時的sa_flags****必須指定為SA_SIGINFO。siginfo_t是一個成員十分豐富的結構體類型,可以攜帶各種與信號相關的數據。