背景介紹
Linux分為內核態和用戶態,用戶態通過系統調用(syscall)進入內核態執行。
用戶空間的glibc庫將Linux內核系統調用封裝成GNU C Library庫文件(兼容ANSI & POSIX C語言標准),同時提供了其他特性的支持。
應用程序通常不是直接調用Linux內核的系統調用接口,而是通過glibc庫封裝的接口間接調用Linux內核系統調用。
信號量機制
關於Linux信號量機制的原理,建議閱讀《Unix環境高級編程》第10章,本博客只是簡單介紹其原理。
信號量是一種軟中斷,用來實現Linux內核和應用程序之間的異步通信。
每一個信號量由一個4字節整形數據表示。可以通過man 7 signal查看所有信號量描述。其中1~32號信號量是從Unix繼承而來,33~64是Linux內核定義的信號量。
Linux內核為每個信號量設置了默認處理動作,如Term(終止執行)、Ign(忽略)、Core(終止執行並產生coredump)、Stop(停止運行)和Cont(繼續運行),當應用程序接收到某個信號量時,則按照默認處理動作執行。應用程序也可以通過sigaction()或者signal()函數修改默認的處理動作,比如屏蔽或者忽略某個信號量等。
應用程序信號量處理函數通常是鏈接glibc中默認的信號量處理函數,也可以自己編寫和指定信號量處理函數。但是需要注意的是,SIGKILL和SIGSTOP信號量不能被捕獲(caught)、屏蔽(blocked)或者忽略(ignored)。
信號量觸發情況有三種:
1)Linux內核檢測到應用程序異常,發送特定的信號量給應用程序,應用程序捕獲到信號量后,調用信號量處理函數;
2)Linux內核因為內部事件而給應用程序發送特定信號,通知應用程序發生了某個事件,如著名的segmentation fault,應用程序捕獲到信號量后,調用信號量處理函數;
3)Linux內核檢測到外部事件,如Ctrl+C,Ctrl+Z等,發送特定信號給應用程序,應用程序捕獲到信號量后,調用信號量處理函數;
Signal | Value | Action | Comment |
SIGHUP | 1 | Term | Hangup detected on controlling terminal or death of controlling process |
SIGINT | 2 | Term | Interrupt from keyboard |
SIGQUIT | 3 | Core | Quit from keyboard |
SIGILL | 4 | Core | Illegal Instruction |
SIGTRAP | 5 | Core | Trace/breakpoint trap |
SIGABRT | 6 | Core | Abort signal from abort(3) |
SIGIOT | 6 | Core | IOT trap. A synonym for SIGABRT |
SIGEMT | 7 | Term | |
SIGFPE | 8 | Core | Floating point exception |
SIGKILL | 9 | Term | Kill signal, cannot be caught, blocked or ignored. |
SIGBUS | 10,7,10 | Core | Bus error (bad memory access) |
SIGSEGV | 11 | Core | Invalid memory reference |
SIGPIPE | 13 | Term | Broken pipe: write to pipe with no readers |
SIGALRM | 14 | Term | Timer signal from alarm(2) |
SIGTERM | 15 | Term | Termination signal |
SIGUSR1 | 30,10,16 | Term | User-defined signal 1 |
SIGUSR2 | 31,12,17 | Term | User-defined signal 2 |
SIGCHLD | 20,17,18 | Ign | Child stopped or terminated |
SIGCONT | 19,18,25 | Cont | Continue if stopped |
SIGSTOP | 17,19,23 | Stop | Stop process, cannot be caught, blocked or ignored. |
SIGTSTP | 18,20,24 | Stop | Stop typed at terminal |
SIGTTIN | 21,21,26 | Stop | Terminal input for background process |
SIGTTOU | 22,22,27 | Stop | Terminal output for background process |
SIGIO | 23,29,22 | Term | I/O now possible (4.2BSD) |
SIGPOLL | Term | Pollable event (Sys V). Synonym for SIGIO | |
SIGPROF | 27,27,29 | Term | Profiling timer expired |
SIGSYS | 12,31,12 | Core | Bad argument to routine (SVr4) |
SIGURG | 16,23,21 | Ign | Urgent condition on socket (4.2BSD) |
SIGVTALRM | 26,26,28 | Term | Virtual alarm clock (4.2BSD) |
SIGXCPU | 24,24,30 | Core | CPU time limit exceeded (4.2BSD) |
SIGXFSZ | 25,25,31 | Core | File size limit exceeded (4.2BSD) |
SIGSTKFLT | 16 | Term | Stack fault on coprocessor (unused) |
SIGCLD | 18 | Ign | A synonym for SIGCHLD |
SIGPWR | 29,30,19 | Term | Power failure (System V) |
SIGINFO | 29 | A synonym for SIGPWR, on an alpha | |
SIGLOST | 29 | Term | File lock lost (unused), on a sparc |
SIGWINCH | 28,28,20 | Ign | Window resize signal (4.3BSD, Sun) |
SIGUNUSED | 31 | Core | Synonymous with SIGSYS |
信號量進階
關於自定義信號量處理函數、屏蔽指定信號量等操作,參考《System Programing: Signals》
信號量處理函數實質上是應用程序發生異常時的一種修復或者調試機制,因為信號量處理不是正常的函數調用,因此它會復用父函數的棧,如果信號量處理函數中發生了異常,系統是沒有辦法處理的,因此,信號量處理函數必須是安全可靠的。
自定義的信號量處理函數不可以做信號量同步(防止死鎖),但是可以通過call fork起gdb調試器或者寫log文件到磁盤。
應用程序崩潰
通常意義上講,main()函數是應用程序的入口。但是實際上,Linux內核執行C程序時(通過exec函數),在調用main函數之前,先調用一個特殊的啟動例程。可執行程序文件(ELF文件)將此啟動例程指定為程序的起始地址。啟動例程從內核取得命令行參數和環境變量值,為調用main函數做好准備。
有8種方式使進程終止,其中5種為正常終止,他們是
1) 從main返回(return語句)
2) 調用exit
3) 調用_exit或者_Exit
4) 最后一個線程從其啟動例程返回
5) 最后一個線程調用pthread_exit
異常終止有3種方式,他們是
6) 調用abort() ----SIGABRT
7) 接收到一個信號量並終止 ---其他Term/Core類信號量
8) 最后一個線程對取消請求做出響應
由此可見,應用程序崩潰必然是因為內部或者外部的原因,導致內核發送信號量或者glibc主動觸發信號量(abort),當應用程序捕獲到信號量之后,進入異常處理流程。