UNIX再学习 -- 信号
終于講到信號部分,很多比較重要的應用程序都需處理信號。第 9 章需要先了解信號機制再看,所以先跳過不講。現在開始詳解信號。
一、信號概念
信號是提供異步事件處理機制的軟件中斷。
這些異步事件可能來自硬件設備,如用戶同時按下 Ctrl+ C 鍵,也可能來自系統內核,如試圖訪問尚未映射的虛擬內存,又或者來自用戶進程,如嘗試計算整數除 0 的表達式。進程之間可以相互發送信號,這使信號稱為一種進程間通信的基本手段。
信號的異步特性不僅表現為它的產生是異步的,對它的處理同樣也是異步的。程序的設計者不可能也不需要精確地預見什么時候觸發什么信號,也同樣無法預見該信號究竟在什么時候會被處理。一切都在內核的操控下,異步地運行。信號是軟件層面對中斷機制的一種模擬。
這里簡單提一句什么是中斷?
中斷就是指暫時停止當前程序的執行轉而去執行新的程序或者處理出現的意外情況。
中斷分為:軟件中斷 / ctrl+c 和 硬件中斷 / 電源拔出。
二、信號處理
信號有一個非常明確的生命周期。
首先,信號被生成,并發送至系統內核。
其次,系統內核存儲信號,直到可以處理它。
最后,一旦有空閑,內核即按以下三種方式之一處理信號。
(1)忽略信號:什么也不做。大多數信號都可使用這種方式進行處理,但 SIGKILL 和 SIGTOP 信號決不能被忽略。其原因是:它們向內核和超級用戶提供了使進程終止或停止的可靠方法。另外,如果忽略某些由硬件異常產生的信號(如非法內存引用或除以 0),則進程的運行行為是未定義的。
(2)捕獲信號:內核暫停收到信號的進程正在執行的代碼,跳轉到事先注冊的信號處理函數,執行該函數并返回,跳轉到捕獲信號的地方繼續執行。SIGKILL 和 SIGTOP 信號不能捕獲。
(3)默認操作:不同信號有不同的默認操作,對大多數信號的系統默認動作是終止該進程,但也有一些信號的默認操作是視而不見的,即忽略。
三、信號名稱與編號
每個信號都有唯一的名稱和編號。信號的名稱是以 SIG 開頭的文本,如 SIGHUP、SIGINT 等。信號的編號是從 1 開始連續增加的整數,如 1、2、3 等。信號的名稱與編號之間的對應關系,依賴于具體實現。
在 /usr/include/i386-linux-gnu/bits/signum.h?文件中通過宏定義,把每個信號的名稱和編號建立了一一映射。
/* Signals. */ #define SIGHUP 1 /* Hangup (POSIX). */ #define SIGINT 2 /* Interrupt (ANSI). */ #define SIGQUIT 3 /* Quit (POSIX). */ #define SIGILL 4 /* Illegal instruction (ANSI). */ #define SIGTRAP 5 /* Trace trap (POSIX). */ #define SIGABRT 6 /* Abort (ANSI). */ #define SIGIOT 6 /* IOT trap (4.2 BSD). */ #define SIGBUS 7 /* BUS error (4.2 BSD). */ #define SIGFPE 8 /* Floating-point exception (ANSI). */ #define SIGKILL 9 /* Kill, unblockable (POSIX). */ #define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ #define SIGSEGV 11 /* Segmentation violation (ANSI). */ #define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ #define SIGPIPE 13 /* Broken pipe (POSIX). */ #define SIGALRM 14 /* Alarm clock (POSIX). */ #define SIGTERM 15 /* Termination (ANSI). */ #define SIGSTKFLT 16 /* Stack fault. */ #define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ #define SIGCHLD 17 /* Child status has changed (POSIX). */ #define SIGCONT 18 /* Continue (POSIX). */ #define SIGSTOP 19 /* Stop, unblockable (POSIX). */ #define SIGTSTP 20 /* Keyboard stop (POSIX). */ #define SIGTTIN 21 /* Background read from tty (POSIX). */ #define SIGTTOU 22 /* Background write to tty (POSIX). */ #define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ #define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ #define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ #define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ #define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ #define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ #define SIGPOLL SIGIO /* Pollable event occurred (System V). */ #define SIGIO 29 /* I/O now possible (4.2 BSD). */ #define SIGPWR 30 /* Power failure restart (System V). */ #define SIGSYS 31 /* Bad system call. */ #define SIGUNUSED 31 當然,我們之前講 kill 指令時,參看:UNIX再學習 -- ps、top、kill 指令? 講到,執行 kill -l 命令,可以查看系統支持的信號列表。
# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX 列表中,編號為1 ~ 31的信號為傳統UNIX支持的信號,是不可靠信號(非實時的),編號為32 ~ 63的信號是后來擴充的,稱做可靠信號(實時信號)。不可靠信號和可靠信號的區別在于前者不支持排隊,可能會造成信號丟失,而后者不會。 總共 62 個信號,注意沒有 32 和 33 信號。 也不存在編號為 0 的信號,POSIX.1 將此信號編號值稱為空信號, 這部分后面會詳解講到的。
四、逐一說明這些信號
上面提到默認操作對大多數信號的系統默認動作是終止該進程,但也有一些信號的默認操作是視而不見的,即忽略。
下面給出對每一種信號的系統默認動作:
上圖列出了所有信號的名字,說明了哪些系統支持此信號以及對于這些信號的系統默認動作。在 SUS 列中,“.”表示此種信號定義為基本 POSIX.1 規范部分,“XSI”表示該信號定義在 XSI 擴展部分。
在系統默認動作列,“終止+core”表示在進程當前工作目錄的 core 文件中復制了該進程的內存映像(該文件名為 core,由此可以看出這種功能很久之前就是 UNIX 的一部分)。大多數 UNIX 系統調試程序都使用 core 文件檢查進程終止時的狀態。
其中在 Linux 3.2.0 中,core 文件名通過 /proc/sys/kernel/core_pattern 進行配置。
/proc/sys/kernel# cat core_pattern |/usr/share/apport/apport %p %s %c
這里出現了 core 文件 是什么東東?
參看:Core文件作用、設置及用法
1、core文件簡介core 文件起始就是內存的映像,當程序崩潰時,存儲內存的相應信息,主要用于對程序進行調試。當程序崩潰時便會產生 core 文件,其實準確的應該說 core dump 文件,默認生成位置與可執行程序位于同一目錄下,文件名 core.***,其中 *** 是某一數字。
2、開啟或關閉 core 文件的生成
(1)關閉或阻止 core 文件生成:
# ulimit -c 0 (2)打開 core 文件生成:
# ulimit -c unlimited (3)檢查 core 文件的選項是否打開:
# ulimit -a core file size (blocks, -c) unlimited data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 7892 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 7892 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited 其中 ulimit 函數我們之前有講過,參看:UNIX再學習 -- 文件描述符
以上配置只對當前會話起作用,下次重新登錄后,還得重新配置。要想配置永久生效,得在?/etc/profile 或者 /etc/security/limits.conf 文件中進行配置,將 0 改為 ulimited。
#gedit /etc/security/limits.conf #<domain> <type> <item> <value> # #* soft core unlimited 或者在 /etc/profile 中作如下配置:
#vi /etc/profile ulimit -S -c unlimited >/dev/null 2>&1 或者想配置只針對某一用戶有效,則修改此用戶的 ~/.bashrc 或者~/.bash_profile文件:
ulimit -c unlimited ulimit -c 0 是禁止產生core文件,而?ulimit -c 1024 則限制產生的 core 文件的大小不能超過 1024kb。
3. 設置Core Dump的核心轉儲文件目錄和命名規則
/proc/sys/kernel/core_uses_pid 可以控制產生的 core 文件的文件名中是否添加 pid 作為擴展,如果添加則文件內容為1,否則為 0
/proc/sys/kernel/core_pattern 可以設置格式化的 core 文件保存位置或文件名,比如原來文件內容是 core-%e
可以這樣修改:
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
將會控制所產生的 core 文件會存放到 /corefile 目錄下,產生的文件名為 core-命令名-pid-時間戳
以下是參數列表:
4. 使用core文件
在core文件所在目錄下鍵入:gdb -c core
它會啟動 GNU 的調試器,來調試 core 文件,并且會顯示生成此 core 文件的程序名,中止此程序的信號等等。
如果你已經知道是由什么程序生成此 core 文件的,比如 MyServer 崩潰了生成 core.12345,那么用此指令調試:
gdb -c core MyServer
gdb使用,參看:C語言再學習 -- GCC編譯過程
5. 一個小方法來測試產生core文件
直接輸入指令:
kill -s SIGSEGV $$
6. 為何有時程序Down了,卻沒生成 Core文件。
Linux下,有一些設置,標明了resources available to the shell and to processes。 可以使用
#ulimit -a 來看這些設置。
從這里可以看出,如果 -c 是顯示:core file size ? ? ? ? ?(blocks, -c)?
如果這個值為 0,則無法生成core文件。所以可以使用:
#ulimit -c 1024 或者 #ulimit -c unlimited 來使能 core文件。
如果程序出錯時生成 core 文件,則會顯示 Segmentation fault (core dumped)。
7. Core Dump的核心轉儲文件目錄和命名規則:
/proc/sys/kernel/core_uses_pid 可以控制產生的 core 文件的文件名中是否添加 pid 作為擴展,如果添加則文件內容為1,否則為 0。
看到這里是不是有點蒙,core 文件其實我們之前有接觸過的,比上面講的更清楚。
參看:C語言再學習 -- 段錯誤(核心已轉儲)
接著講信號,在下列條件下不產生 core 文件:
(1)進程是設置用戶 ID 的,而且當前用戶并非程序文件的所有者。
(2)進程是設置組 ID 的,而且當前用戶并非該程序文件的組所有者。
(3)用戶沒有寫當前工作目錄的權限。
(4)文件已存在,而且用戶對該文件設有寫權限。
(5)文件太大。其中 RLIMIT_CORE ?表示 core 文件的最大字節數,若其值為 0 則阻止創建 core 文件。
下面較詳細地逐一說明這些信號:
參看:linux中的signal機制
1) SIGHUP
本信號在用戶終端連接(正?;蚍钦?結束時發出, 通常是在終端的控制進程結束時, 通知同一 session 內的各個作業, 這時它們與控制終端不再關聯。
登錄Linux時,系統會分配給登錄用戶一個終端(Session)。在這個終端運行的所有程序,包括前臺進程組和后臺進程組,一般都屬于這個Session。當用戶退出 Linux 登錄時,前臺進程組和后臺有對終端輸出的進程將會收到 SIGHUP信號。這個信號的默認操作為終止進程,因此前臺進程組和后臺有終端輸出的進程就會中止。不過可以捕獲這個信 號,比如 wget 能捕獲 SIGHUP 信號,并忽略它,這樣就算退出了 Linux 登錄,wget 也能繼續下載。
此外,對于與終端脫離關系的守護進程,這個信號用于通知它重新讀取配置文件。
2) SIGINT (常用)
程序終止(interrupt)信號, 在用戶鍵入INTR字符 (通常是Ctrl-C) 時發出,用于通知前臺進程組終止進程。
3) SIGQUIT (常用)
和SIGINT類似, 但由QUIT字符 (通常是Ctrl-\)?來控制.。進程在因收到 SIGQUIT 退出時會產生 core 文件, 在這個意義上類似于一個程序錯誤信號。
4) SIGILL
執行了非法指令. 通常是因為可執行文件本身出現錯誤, 或者試圖執行數據段. 堆棧溢出時也有可能產生這個信號。
5) SIGTRAP
由斷點指令或其它trap指令產生. 由debugger使用。
6) SIGABRT
調用 abort 函數生成的信號。進程異常終止。
7) SIGBUS
非法地址, 包括內存地址對齊(alignment)出錯。比如訪問一個四個字長的整數, 但其地址不是4的倍數。它與 SIGSEGV 的區別在于后者是由于對合法存儲地址的非法訪問觸發的 (如訪問不屬于自己存儲空間或只讀存儲空間)。
8) SIGFPE
在發生致命的算術運算錯誤時發出. 不僅包括浮點運算錯誤, 還包括溢出及除數為0等其它所有的算術的錯誤。
9) SIGKILL (常用)
用來立即結束程序的運行. 本信號不能被阻塞、處理和忽略。如果管理員發現某個進程終止不了,可嘗試發送這個信號。
10) SIGUSR1
這是一個用戶定義的信號,可用于應用程序。
11) SIGSEGV
試圖訪問未分配給自己的內存, 或試圖往沒有寫權限的內存地址寫數據.指示進程進程了一次無效的內存引用。
12) SIGUSR2
這是另一個用戶定義的信號,與 SIGUSR1 相似,可用于應用程序。
13) SIGPIPE
管道破裂。這個信號通常在進程間通信產生,比如采用FIFO(管道)通信的兩個進程,讀管道沒打開或者意外終止就往管道寫,寫進程會收到 SIGPIPE 信號。此外用 Socket 通信的兩個進程,寫進程在寫 Socket 的時候,讀進程已經終止。
14) SIGALRM
時鐘定時信號, 計算的是實際的時間或時鐘時間. alarm函數使用該信號.
15) SIGTERM
程序結束(terminate)信號, 與 SIGKILL 不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出,shell命令kill缺省產生這個信號。如果進程終止不了,我們才會嘗試SIGKILL。
17) SIGCHLD
子進程結束時, 父進程會收到這個信號。
如果父進程沒有處理這個信號,也沒有等待(wait)子進程,子進程雖然終止,但是還會在內核進程表中占有表項,這時的子進程稱為僵尸進程。這種情況我們應該避免(父進程或者忽略SIGCHILD信號,或者捕捉它,或者wait它派生的子進程,或者父進程先終止,這時子進程的終止自動由init進程來接管)。
18) SIGCONT
讓一個停止(stopped)的進程繼續執行. 本信號不能被阻塞. 可以用一個handler來讓程序在由stopped狀態變為繼續執行時完成特定的工作. 例如, 重新顯示提示符
19) SIGSTOP
停止(stopped)進程的執行. 注意它和 terminate 以及 interrupt 的區別:該進程還未結束, 只是暫停執行. 本信號不能被阻塞, 處理或忽略.
20) SIGTSTP
停止進程的運行, 但該信號可以被處理和忽略. 用戶鍵入 SUSP 字符時(通常是Ctrl-Z)發出這個信號
21) SIGTTIN
當后臺作業要從用戶終端讀數據時, 該作業中的所有進程會收到 SIGTTIN 信號. 缺省時這些進程會停止執行.
22) SIGTTOU
類似于 SIGTTIN, 但在寫終端(或修改終端模式)時收到.
23) SIGURG
有"緊急"數據或 out-of-band 數據到達 socket 時產生.
24) SIGXCPU
超過CPU時間資源限制. 這個限制可以由 getrlimit/setrlimit 來讀取/改變。
25) SIGXFSZ
當進程企圖擴大文件以至于超過文件大小資源限制。
26) SIGVTALRM
虛擬時鐘信號. 類似于 SIGALRM, 但是計算的是該進程占用的CPU時間.
27) SIGPROF
類似于 SIGALRM/SIGVTALRM, 但包括該進程用的CPU時間以及系統調用的時間.
28) SIGWINCH
窗口大小改變時發出.
29) SIGIO
文件描述符準備就緒, 可以開始進行輸入/輸出操作.
30) SIGPWR
Power failure
31) SIGSYS
非法的系統調用。
在以上列出的信號中,程序不可捕獲、阻塞或忽略的信號有:SIGKILL,SIGSTOP
不能恢復至默認動作的信號有:SIGILL,SIGTRAP
默認會導致進程流產的信號有:
SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默認會導致進程退出的信號有:
SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默認會導致進程停止的信號有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默認進程忽略的信號有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在進程掛起時是繼續,否則是忽略,不能被阻塞。
這里順便提一句: ctrl-c?是發送 SIGINT 信號,終止一個進程;進程無法再重續。
ctrl-z?是發送 SIGSTOP 信號,掛起一個進程;進程從前臺轉入后臺并暫停,可以用?bg?使其后臺繼續運行,fg?使其轉入前臺運行。
ctrl-d?不是發送信號,而是表示一個特殊的二進制值,表示 EOF,通常是表示輸入終止,通常進程接收到終止符可以完成運行并退出。
參看:C語言再學習 -- EOF、feof函數、ferror函數
五、產生信號的條件
1、bash按下時,終端會發送信號給前臺,Ctrl-C 產生 SIGINT 信號,Ctrl-\產生SIGQUIT信號,Ctrl-Z產生SIGTSTP?信號,上述信號使得進程停止。 2、硬件異常信號,條件由硬件檢測到并通知內核,然后內核向當前進程發送信號。例如執行了除以 0 的指令,CPU 的運算單元會產生異常,內核將這個異常解釋為 SIGFPE 信號發送給進程。再比如當前進程訪問了非法內存地址,MMU 會產生異常,內核將這個異常解釋為 SIGSEGV 信號發送給進程。3、一個進程調用 kill(2) 函數可以發送信號給另一個進程。
4、可以用 kill(1)命令發送信號給某個進程,kill(1)命令也是調用kill(2)函數實現的,如果不明確指定信號則發送 SIGTERM 信號,該信號的默認處理動作是終止進程。
5、當內核檢測到某種軟件條件發生時也可以通過信號通知進程,例如鬧鐘超時產生SIGALRM信號,向讀端已關閉的管道寫數據時產生SIGPIPE信號。
六、signal 函數
#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);1、函數功能
設置對指定信號的指定處理方式2、返回值
成功返回原信號處理方式,失敗返回?SIG_ERR。3、參數解析
signum:信號編號handler:信號處理方式,可取以下值? ??SIG_IGN ? ?忽略信號? ??SIG_DFL ? ?默認操作? ??信號處理函數指針?? ?捕獲信號查看系統頭文件?/usr/include/i386-linux-gnu/bits/signum.h,則可以找到下列形式的聲明:/* Fake signal functions. */ #define SIG_ERR ((__sighandler_t) -1) /* Error return. */ #define SIG_DFL ((__sighandler_t) 0) /* Default action. */ #define SIG_IGN ((__sighandler_t) 1) /* Ignore signal. */這些常量可用于表示“指向函數的指針,該函數要求一個整型參數,而且無返回值”。signal 的第二個參數及其返回值就可用它們表示。4、示例說明
//示例一 #include <stdio.h> #include <signal.h>int main (void) {signal (SIGINT, SIG_IGN);while (1);return 0; } ctrl + c 將無法結束//示例二 #include <stdio.h> #include <signal.h>int main (void) {signal (SIGINT, SIG_DFL);while (1);return 0; } ctrl + c 可結束//示例三 #include <stdio.h> #include <signal.h>//自定義信號處理函數 void fa(int signo) {printf("捕獲到了信號%d\n",signo); }int main(void) {signal(2,fa);while(1);return 0; }ctrl + c 可打印 捕獲到了信號2//示例四 #include <stdio.h> #include <signal.h> #include <stdlib.h>int main (void) { //if (signal (SIGKILL, SIG_IGN) == SIG_ERR)//if (signal (SIGKILL, SIG_DFL) == SIG_ERR)//if (signal (SIGSTOP, SIG_DFL) == SIG_ERR)if (signal (SIGSTOP, SIG_IGN) == SIG_ERR)perror ("signal"), exit (1); while (1); return 0; } 輸出結果: signal: Invalid argument5、示例解析
示例一:使用?SIG_IGN?為忽略信號,所以使用?ctrl+c?終止進程失效示例二:使用?SIG_DEL?為默認操作,所以有無?signal (SIGINT, SIG_DFL);?都可以使用ctrl+c 終止進程的。示例三:使用自定義信號處理函數,注意該函數無返回值,參數為整型。SIGINT 可用信號編號 2 代替。如果想結束,可用 ctrl+\ 即 SIGQUIT 退出?;虼蛴∵M程號使用 kill -9 PID 殺死進程。?示例四:說明 SIGKILL (9) 和 SIGSTOP (19) 信號即不能被忽略,也不能被捕獲,只能按缺省方式終止或停止接收到信號的進程。如果返回失敗則返回 SIG_ERR。
七、信號的分類
可以從兩個不同的分類角度對信號進行分類:(1)可靠性方面,分為可靠信號與不可靠信號(2)與時間的關系上,分為實時信號與非實時信號。上面也有提到了,編號為1 ~ 31的信號為傳統UNIX支持的信號,是不可靠信號(非實時的),編號為32 ~ 63的信號是后來擴充的,稱做可靠信號(實時信號)。參看:Linux環境進程間通信(二): 信號(上)
1、不可靠信號
Linux信號機制基本上是從 UNIX 系統中繼承過來的。早期 UNIX 系統中的信號機制比較簡單和原始,后來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做“不可靠信號”,信號小于 SIGRTMN 的信號都是不可靠信號。這就是“不可靠信號”的來源。它的主要問題是: 進程每次處理信號后,就將對信號的響應設為默認動作。在某些情況下,就導致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號處理函數結尾再一次調用 signal ( ),重新安裝該信號。 信號可能丟失,后面將對此詳細闡述。因此,早期 UNIX 下的不可靠信號主要指的是進程可能對信號做出錯誤的反應以及信號可能丟失。 Linux支持不可靠信號,但是對不可靠信號機制做了改進,在調用完信號處理函數后,不必重新調用該信號的安裝函數(信號安裝函數時在可靠機制上的實現)。因此,Linux 下的不可靠信號問題主要是指的是信號可能丟失。2、可靠信號
隨著時間的發展,實踐證明了有必要對信號的原始機制加以改進和擴充。所以,后來出現的各種 UNIX 版本分別在這方面進行了研究,力圖實現“可靠信號”。由于原來定義的信號已有許多應用,不好再做改動,最終只好又新增加了一些信號,并在一開始就把它們定義為可靠信號,這些信號支持排隊,不會丟失。同時,信號的發送和安裝也出現了新版本:信號發送函數 sigqueue() 及信號安裝函數 sigaction() 。POSIX.4 對可靠信號機制做了標準化。但是,POSIX 只對可靠信號機制應具有的功能以及信號機制的對外接口做了標準化,對信號機制的實現沒有作具體的規定。信號值位于 SIGRTMIN 和 SIGRTMAX 之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。Linux在支持新版本的信號安裝函數 sigation()以及信號發送函數 sigqueue ( ) 的同時,仍然支持早期的 signal()信號安裝函數,支持信號發送函數 kill()。
注:不要有這樣的誤解:由 sigqueue() 發送、sigaction 安裝的信號就是可靠的。事實上,可靠信號是指后來添加的新信號(信號值位于SIGRTMIN及SIGRTMAX之間);不可靠信號是信號值小于SIGRTMIN的信號。信號的可靠與不可靠只與信號值有關,與信號的發送及安裝函數無關。目前linux中的 signal() 是通過 sigation() 函數實現的,因此,即使通過 signal()安裝的信號,在信號處理函數的結尾也不必再調用一次信號安裝函數。同時,由 signal() 安裝的實時信號支持排隊,同樣不會丟失。
對于目前 linux 的兩個信號安裝函數: signal() 及 sigaction() 來說,它們都不能把 SIGRTMIN 以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對 SIGRTMIN 以后的信號都支持排隊。這兩個函數的最大區別在于,經過 sigaction 安裝的信號都能傳遞信息給信號處理函數(對所有信號這一點都成立),而經過signal 安裝的信號卻不能向信號處理函數傳遞信息。對于信號發送函數來說也是一樣的。
3、實時信號與非實時信號
當多個相同的信號被發送到正在處理該信號的進程時,那些還來不及處理的信號會按先后順序排成隊列。等進程處理完手里的信號以后,再依次處理隊列中的信號。整個過程中,所有發送給進程的信號一個也不會丟,都能得到處理。這樣的信號就叫做可靠信號。實時信號都是可靠信號,都支持排隊。 反之,不可靠信號不支持排隊,可能丟失,在多個相同的信號里進程可能只收到一個。非實時信號都是不可靠信號。總結
以上是生活随笔為你收集整理的UNIX再学习 -- 信号的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 锁、事务和同步
- 下一篇: 高性能RPC框架—-----------