Linux系統編程——信號


目錄

  • 信號的介紹
    • 信號的機制
    • 信號的編號
    • Linux常規信號一覽表
  • 信號的產生
    • 終端按鍵產生信號
    • 硬件異常產生信號
    • kill函數/命令產生信號
  • 信號的操作函數
    • 信號集設定
    • sigprocmask函數
    • sigpending函數
  • 信號的捕捉
    • signal函數
    • sigaction函數
    • 內核實現信號捕捉的過程
  • 信號的傳參
    • 捕捉函數傳參

信號的機制

​ A給B發送信號,B收到信號之前執行自己的代碼,收到信號后,不管執行到程序的什么位置,都要暫停運行,去處理信號,處理完畢再繼續執行。與硬件中斷類似——異步模式。但信號是軟件層面上實現的中斷,早期常被稱為“軟中斷”。

信號的特質:由於信號是通過軟件方法實現,其實現手段導致信號有很強的延時性。但對於用戶來說,這個延遲時間非常短,不易察覺。

每個進程收到的所有信號,都是由內核負責發送的,內核處理。

信號的編號

可以使用kill –l命令查看當前系統可使用的信號有哪些。

  1. SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP

  2. SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1

  3. SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM

  4. SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

  5. SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

  6. SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR

  7. SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3

  8. SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8

  9. SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13

  10. SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12

  11. SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7

  12. SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2

  13. SIGRTMAX-1 64) SIGRTMAX

不存在編號為0的信號。其中1-31號信號稱之為常規信號(也叫普通信號或標准信號),34-64稱之為實時信號,驅動編程與硬件相關。名字上區別不大。而前32個名字各不相同。

這里特別強調了9)SIGKILL 和19) SIGSTOP信號,不允許忽略和捕捉,只能執行默認動作。甚至不能將其設置為阻塞。

Linux常規信號一覽表

​ 1) SIGHUP: 當用戶退出shell時,由該shell啟動的所有進程將收到這個信號,默認動作為終止進程

  1. SIGINT:當用戶按下了<Ctrl+C>組合鍵時,用戶終端向正在運行中的由該終端啟動的程序發出此信號。默認動

作為終止進程。

  1. SIGQUIT:當用戶按下<ctrl+>組合鍵時產生該信號,用戶終端向正在運行中的由該終端啟動的程序發出些信

號。默認動作為終止進程。

  1. SIGILL:CPU檢測到某進程執行了非法指令。默認動作為終止進程並產生core文件

  2. SIGTRAP:該信號由斷點指令或其他 trap指令產生。默認動作為終止里程 並產生core文件。

  3. SIGABRT: 調用abort函數時產生該信號。默認動作為終止進程並產生core文件。

  4. SIGBUS:非法訪問內存地址,包括內存對齊出錯,默認動作為終止進程並產生core文件。

  5. SIGFPE:在發生致命的運算錯誤時發出。不僅包括浮點運算錯誤,還包括溢出及除數為0等所有的算法錯誤。默認動作為終止進程並產生core文件。

  6. SIGKILL:無條件終止進程。本信號不能被忽略,處理和阻塞。默認動作為終止進程。它向系統管理員提供了可以殺死任何進程的方法。

  7. SIGUSE1:用戶定義 的信號。即程序員可以在程序中定義並使用該信號。默認動作為終止進程。

  8. SIGSEGV:指示進程進行了無效內存訪問。默認動作為終止進程並產生core文件。

  9. SIGUSR2:另外一個用戶自定義信號,程序員可以在程序中定義並使用該信號。默認動作為終止進程。

  10. SIGPIPE:Broken pipe向一個沒有讀端的管道寫數據。默認動作為終止進程。

  11. SIGALRM: 定時器超時,超時的時間 由系統調用alarm設置。默認動作為終止進程。

  12. SIGTERM:程序結束信號,與SIGKILL不同的是,該信號可以被阻塞和終止。通常用來要示程序正常退出。執行shell命令Kill時,缺省產生這個信號。默認動作為終止進程。

  13. SIGSTKFLT:Linux早期版本出現的信號,現仍保留向后兼容。默認動作為終止進程。

  14. SIGCHLD:子進程結束時,父進程會收到這個信號。默認動作為忽略這個信號。

  15. SIGCONT:如果進程已停止,則使其繼續運行。默認動作為繼續/忽略。

  16. SIGSTOP:停止進程的執行。信號不能被忽略,處理和阻塞。默認動作為暫停進程。

  17. SIGTSTP:停止終端交互進程的運行。按下<ctrl+z>組合鍵時發出這個信號。默認動作為暫停進程。

  18. SIGTTIN:后台進程讀終端控制台。默認動作為暫停進程。

  19. SIGTTOU: 該信號類似於SIGTTIN,在后台進程要向終端輸出數據時發生。默認動作為暫停進程。

  20. SIGURG:套接字上有緊急數據時,向當前正在運行的進程發出些信號,報告有緊急數據到達。如網絡帶外數據到達,默認動作為忽略該信號。

  21. SIGXCPU:進程執行時間超過了分配給該進程的CPU時間 ,系統產生該信號並發送給該進程。默認動作為終止進程。

  22. SIGXFSZ:超過文件的最大長度設置。默認動作為終止進程。

  23. SIGVTALRM:虛擬時鍾超時時產生該信號。類似於SIGALRM,但是該信號只計算該進程占用CPU的使用時間。默認動作為終止進程。

  24. SGIPROF:類似於SIGVTALRM,它不公包括該進程占用CPU時間還包括執行系統調用時間。默認動作為終止進程。

  25. SIGWINCH:窗口變化大小時發出。默認動作為忽略該信號。

  26. SIGIO:此信號向進程指示發出了一個異步IO事件。默認動作為忽略。

  27. SIGPWR:關機。默認動作為終止進程。

  28. SIGSYS:無效的系統調用。默認動作為終止進程並產生core文件。

  29. 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

  1. SIG_BLOCK: 當how設置為此值,set表示需要屏蔽的信號。相當於 mask = mask|set

  2. SIG_UNBLOCK: 當how設置為此,set表示需要解除屏蔽的信號。相當於 mask = mask & ~set

  3. 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,表使用默認屬性。

信號捕捉特性

  1. 進程正常運行時,默認PCB中有一個信號屏蔽字,假定為☆,它決定了進程自動屏蔽哪些信號。當注冊了某個信號捕捉函數,捕捉到該信號以后,要調用該函數。而該函數有可能執行很長時間,在這期間所屏蔽的信號不由☆來指定。而是用sa_mask來指定。調用完信號處理函數,再恢復為☆。

  2. XXX信號捕捉函數執行期間,XXX信號自動被屏蔽。

  3. 阻塞的常規信號不支持排隊,產生多次只記錄一次。(后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是一個成員十分豐富的結構體類型,可以攜帶各種與信號相關的數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM