linux內(nèi)核中的信號(hào)機(jī)制--信號(hào)機(jī)制的管理結(jié)構(gòu) Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123(http://blog.csdn.net/ce123)
信號(hào)只是一個(gè)數(shù)字,數(shù)字為0-31表示不同的信號(hào),如下表所示。 編號(hào) | 信號(hào)名 | 默認(rèn)動(dòng)作 | 說(shuō)明 | 1 | SIGHUP | 進(jìn)程終止 | 終端斷開(kāi)連接 | 2 | SIGINT | 進(jìn)程終止 | 用戶在鍵盤(pán)上按下CTRL+C | 3 | SIGQUIT | 進(jìn)程意外結(jié)束(Dump) | 用戶在鍵盤(pán)上按下CTRL+\ | 4 | SIGILL | 進(jìn)程意外結(jié)束(Dump) | 遇到非法指令 | 5 | SIGTRAP | 進(jìn)程意外結(jié)束(Dump) | 遇到斷電,用于調(diào)試 | 6 | SIGABRT/SIGIOT | 進(jìn)程意外結(jié)束(Dump) | | 7 | SIGBUS | 進(jìn)程意外結(jié)束(Dump) | 總線錯(cuò)誤 | 8 | SIGFPE | 進(jìn)程意外結(jié)束(Dump) | 浮點(diǎn)異常 | 9 | SIGKILL | 進(jìn)程終止 | 其他進(jìn)程發(fā)送SIGKILL將導(dǎo)致目標(biāo)進(jìn)程終止 | 10 | SIGUSR1 | 進(jìn)程終止 | 應(yīng)用程序可自定義使用 | 11 | SIGSEGV | 進(jìn)程意外結(jié)束(Dump) | 非法的內(nèi)存訪問(wèn) | 12 | SIGUSR2 | 進(jìn)程終止 | 應(yīng)用程序可自定義使用 | 13 | SIGPIPE | 進(jìn)程終止 | 管道讀取端已經(jīng)關(guān)閉,寫(xiě)入端進(jìn)程會(huì)收到該信號(hào) | 14 | SIGALRM | 進(jìn)程終止 | 定時(shí)器到時(shí) | 15 | SIGTERM | 進(jìn)程終止 | 發(fā)送該信號(hào)使目標(biāo)進(jìn)程終止 | 16 | SIGSTKFLT | 進(jìn)程終止 | 堆線錯(cuò)誤 | 17 | SIGCHLD | 忽略 | 子進(jìn)程退出時(shí)會(huì)向父進(jìn)程發(fā)送該信號(hào) | 18 | SIGCONT | 忽略 | 進(jìn)程繼續(xù)執(zhí)行 | 19 | SIGSTOP | 進(jìn)程暫停 | 發(fā)送該信號(hào)會(huì)使目標(biāo)進(jìn)程進(jìn)入TASK_STOPPED狀態(tài) | 20 | SIGTSTP | 進(jìn)程暫停 | 在終端上按下CTRL+Z | 21 | SIGTTIN | 進(jìn)程暫停 | 后臺(tái)進(jìn)程從控制終端讀取數(shù)據(jù) | 22 | SIGTTOU | 進(jìn)程暫停 | 后臺(tái)進(jìn)程從控制終端讀取數(shù)據(jù) | 23 | SIGURG | 忽略 | socket收到設(shè)置緊急指針標(biāo)志的網(wǎng)絡(luò)數(shù)據(jù)包 | 24 | SIGXCPU | 進(jìn)程意外結(jié)束(Dump) | 進(jìn)程使用CPU已經(jīng)超過(guò)限制 | 25 | SIGXFSZ | 進(jìn)程意外結(jié)束(Dump) | 進(jìn)程使用CPU已經(jīng)超過(guò)限制 | 26 | SIGVTALRM | 進(jìn)程終止 | 進(jìn)程虛擬定時(shí)器到期 | 27 | SIGPROF | 進(jìn)程終止 | 進(jìn)程Profile定時(shí)器到期 | 28 | SIGMNCH | 忽略 | 進(jìn)程終端窗口大小改變 | 29 | SIGIO | 進(jìn)程暫停 | 用于異步IO | 29 | SIGPOLL | 進(jìn)程暫停 | 用于異步IO | 30 | SIGPWR | 進(jìn)程暫停 | 電源失效 | 31 | SIGUNUSED | 進(jìn)程暫停 | 保留未使用 | 注意在上標(biāo)中的默認(rèn)動(dòng)作是指,在沒(méi)有任何程序?yàn)橄鄳?yīng)的信號(hào)設(shè)置信號(hào)處理函數(shù)的情況下,內(nèi)核接收到該信號(hào)的默認(rèn)處理方式,但在實(shí)際中,有可能不是這樣的。另外,在這里,進(jìn)程終止一般是指進(jìn)程通過(guò)do_exit()退出,進(jìn)程意外結(jié)束(Dump)則表示進(jìn)程遇到了一個(gè)異常。默認(rèn)情況下,內(nèi)核會(huì)根據(jù)進(jìn)程當(dāng)時(shí)的內(nèi)存情況,在進(jìn)程的當(dāng)前目錄中生成一個(gè)Core Dump文件,以后用戶可以通過(guò)這個(gè)文件分析進(jìn)程異常的原因。這個(gè)工作主要通過(guò)do_coredump()來(lái)完成。 由于早期只有31個(gè)信號(hào),內(nèi)核僅僅使用一個(gè)32位的變量signal來(lái)表示進(jìn)程接收到的信號(hào),因此如果要向一個(gè)進(jìn)程發(fā)送一個(gè)信號(hào),就把signal的第n位設(shè)置為1,這非常類似中斷請(qǐng)求寄存器SRCPND寄存器,同時(shí),還有一個(gè)blocked的變量,用來(lái)屏蔽信號(hào),這類似中斷屏蔽寄存器INTMSK。這樣做的好處是可以“很快”判斷出一個(gè)進(jìn)程收到了哪些信號(hào),如果采用鏈表或者數(shù)組,則需要掃描整個(gè)隊(duì)列,但這也帶來(lái)了新的問(wèn)題,如果向一個(gè)進(jìn)程發(fā)送了SIGINT信號(hào),在這個(gè)信號(hào)處理之前,再次發(fā)送SIGINT,當(dāng)這個(gè)進(jìn)程開(kāi)始處理信號(hào)時(shí),它只知道收到了SIGINT信號(hào),而無(wú)法判斷出有幾個(gè)SIGINT需要處理。此后加入了信號(hào)隊(duì)列,把收到的信號(hào)保存在這個(gè)隊(duì)列中,就可以很好的解決這個(gè)問(wèn)題了。但是為了兼容的目的,仍能保留了舊的信號(hào)處理方式,因此1-32還是按原有的方式進(jìn)行處理,而33-64則使用新的機(jī)制,為了區(qū)別對(duì)待,編號(hào)為33-64的信號(hào)又稱為實(shí)時(shí)信號(hào)。需要注意的是:這里的“實(shí)時(shí)”和實(shí)時(shí)操作系統(tǒng)中的“實(shí)時(shí)”沒(méi)有任何聯(lián)系,實(shí)時(shí)信號(hào)在處理速度上并不會(huì)比普通信號(hào)快,它們之間的區(qū)別就是:普通信號(hào)會(huì)對(duì)多次的同一個(gè)信號(hào)進(jìn)行“合并”處理,而實(shí)時(shí)信號(hào)會(huì)一一處理。因此我們這里僅討論普通信號(hào)。 信號(hào)機(jī)制的相關(guān)管理結(jié)構(gòu)位于task_struct結(jié)構(gòu)中,其主要結(jié)構(gòu)如下圖所示。 
實(shí)時(shí)信號(hào)引入了信號(hào)隊(duì)列,為了處理上的方便,普通信號(hào)也使用了信號(hào)隊(duì)列,僅僅從數(shù)字上無(wú)法區(qū)分實(shí)時(shí)信號(hào)和普通信號(hào)。由于在linux中,進(jìn)程對(duì)象和線程對(duì)象都是task_struct,因此需要區(qū)別對(duì)待線程的信號(hào)和進(jìn)程的信號(hào)。在上圖中,Private Signal Queue是線程(在linux中稱為輕權(quán)進(jìn)程)信號(hào)隊(duì)列,而Shared Signal Queue是進(jìn)程(在linux中被稱為進(jìn)程組)信號(hào)隊(duì)列。對(duì)于進(jìn)程信號(hào),則由進(jìn)程組中的每一個(gè)線程共享。例如,在上圖中,pending和shared_pending分別是Private Signal Queue和Shared Signal Queue其類型都是sigpending(/include/linux/signal.h),定義如下:
- struct sigpending {
- struct list_head list;
- sigset_t signal;
- };
list用于連接信號(hào)隊(duì)列,signal是一個(gè)位圖,每一位表示一個(gè)對(duì)應(yīng)的信號(hào),用于指示信號(hào)隊(duì)列中有哪些信號(hào)等待處理,其類型為sigset_t(include/asm-arm/signal.h),其定義如下:
- #define _NSIG 64
- #define _NSIG_BPW 32
- #define _NSIG_WORDS (_NSIG / _NSIG_BPW)
-
-
- typedef struct {
- unsigned long sig[_NSIG_WORDS];
- } sigset_t;
sigset_t是一個(gè)數(shù)組,總共有64位,對(duì)應(yīng)64個(gè)信號(hào)位圖(32個(gè)普通信號(hào)和32個(gè)實(shí)時(shí)信號(hào))。在以后的信號(hào)發(fā)送的分析中,我們會(huì)看到,對(duì)于普通信號(hào),只需要把sigset_t中對(duì)應(yīng)的位置1就可以了,而對(duì)于實(shí)時(shí)信號(hào),還需要把相關(guān)信息添加到list的信號(hào)隊(duì)列,信號(hào)隊(duì)列類型為sigqueue(include/linux/signal.h),定義如下:
- /*
- * Real Time signals may be queued.
- */
-
- struct sigqueue {
- struct list_head list;
- spinlock_t *lock;
- int flags;
- siginfo_t info;
- struct user_struct *user;
- };
sigqueue中的list是隊(duì)列鏈表指針,info為這個(gè)信號(hào)的相關(guān)信息,其定義如下(include/asm-generic/siginfo.h):
- typedef struct siginfo {
- int si_signo;
- int si_errno;
- int si_code;
-
- union {
- int _pad[SI_PAD_SIZE];
-
- /* kill() */
- struct {
- pid_t _pid; /* sender's pid */
- __ARCH_SI_UID_T _uid; /* sender's uid */
- } _kill;
-
- /* POSIX.1b timers */
- struct {
- timer_t _tid; /* timer id */
- int _overrun; /* overrun count */
- char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
- sigval_t _sigval; /* same as below */
- int _sys_private; /* not to be passed to user */
- } _timer;
-
- /* POSIX.1b signals */
- struct {
- pid_t _pid; /* sender's pid */
- __ARCH_SI_UID_T _uid; /* sender's uid */
- sigval_t _sigval;
- } _rt;
-
- /* SIGCHLD */
- struct {
- pid_t _pid; /* which child */
- __ARCH_SI_UID_T _uid; /* sender's uid */
- int _status; /* exit code */
- clock_t _utime;
- clock_t _stime;
- } _sigchld;
-
- /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
- struct {
- void __user *_addr; /* faulting insn/memory ref. */
- #ifdef __ARCH_SI_TRAPNO
- int _trapno; /* TRAP # which caused the signal */
- #endif
- } _sigfault;
-
- /* SIGPOLL */
- struct {
- __ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
- int _fd;
- } _sigpoll;
- } _sifields;
- } siginfo_t;
上圖中的sighand保存信號(hào)的處理函數(shù)指針,其作用類似于中斷向量表,類型為sighand_struct(include/linux/sched.h),定義為:
- struct sighand_struct {
- atomic_t count;
- struct k_sigaction action[_NSIG];
- spinlock_t siglock;
- };
_NSIG定義在asm-arm/signal.h中,為64,數(shù)組action,對(duì)應(yīng)64個(gè)信號(hào)處理函數(shù)的相關(guān)信息,烈性為k_sigaction,在arm平臺(tái)上,k_sigaction(include/asm-arm/signal.h)是死噶長(zhǎng)提哦你的一個(gè)包裝,定義如下:
- struct k_sigaction {
- struct sigaction sa;
- };
sigantion(include/asm-arm/signal.h)的定義如下:
- struct sigaction {
- __sighandler_t sa_handler;
- unsigned long sa_flags;
- __sigrestore_t sa_restorer;
- sigset_t sa_mask; /* mask last for extensibility */
- };
sa_handler就是信號(hào)處理函數(shù)指針。另外在task_struct結(jié)構(gòu)中還有一個(gè)blocked可以用來(lái)屏蔽信號(hào)。明白了上面的主要數(shù)據(jù)結(jié)構(gòu)的作用之后,很容易想到信號(hào)的處理主要有以下幾方面。 - 設(shè)置信號(hào)回調(diào)函數(shù):內(nèi)核吧函數(shù)的相關(guān)信息保存到對(duì)應(yīng)的sigantion結(jié)構(gòu)中。
- 信號(hào)的發(fā)送:通過(guò)相關(guān)系統(tǒng)調(diào)用吧一個(gè)指定的信號(hào)發(fā)送到目標(biāo)進(jìn)程,如果該信號(hào)沒(méi)有被屏蔽,就把信號(hào)的相關(guān)信息添加到信號(hào)隊(duì)列中,如果有必要就喚醒目標(biāo)進(jìn)程。
- 信號(hào)響應(yīng):進(jìn)程被喚醒后,根據(jù)信號(hào)隊(duì)列中的信息,調(diào)用信號(hào)回調(diào)函數(shù)。
我們?cè)賮?lái)看一下信號(hào)回調(diào)函數(shù),sa_handler的類型是__sihandler_t(include/asm-generic/singal.h),其定義為: - typedef void __signalfn_t(int);
- typedef __signalfn_t __user *__sighandler_t;
|