關于Linux Shell中進程、信號和捕獲的總結
信號是學習 linux 時必須要熟悉的一部分,沒有了信號 Linux 系統就沒辦法正常的工作。而我們作為 Linux 程序員,也要用到信號來進行程序的運行,沒有了信號,我們的工作將會變得一團糟。這幾天,我把剛剛學的關于信號的知識點總結了一下,分享給大家,希望可以對大家有所幫助。
信號的概念
信號是 linux 系統為了響應某些狀況而產生的事件。進程收到信號后應該采取相應的動作
哪些情況會引發信號
鍵盤事件 ctrl +c ctrl +\
非法內存 如果內存管理出錯,系統就會發送一個信號進行處理
硬件故障 同樣的,硬件出現故障系統也會產生一個信號
環境切換 比如說從用戶態切換到其他態,狀態的改變也會發送一個信號,這個信號會告知給系統
怎樣查看信號呢?
kill -l
這個命令就可以查看所有的信號啦,現在信號已經增加到 65 個了,但是在這里我要提一下,從 33-64 這些信號一般不會采用,這是為了區分可靠信號和不可靠信號而新增加的 32 個信號。關于可靠信號和不可靠信號,我會在下面加以說明滴。
下面我把最常用到的信號給大家解釋一下
#define SIGHUP 1 /* hangup */#define SIGINT 2 /* interrupt */#define SIGQUIT 3 /* quit */#define SIGILL 4 /* illegal instruction (not reset when caught) */#define SIGTRAP 5 /* trace trap (not reset when caught) */#define SIGABRT 6 /* abort() */#if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE))#define SIGPOLL 7 /* pollable event ([XSR] generated, not supported) */#else /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */#define SIGIOT SIGABRT /* compatibility */#define SIGEMT 7 /* EMT instruction */#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */#define SIGFPE 8 /* floating point exception */#define SIGKILL 9 /* kill (cannot be caught or ignored) */#define SIGBUS 10 /* bus error */#define SIGSEGV 11 /* segmentation violation */#define SIGSYS 12 /* bad argument to system call */#define SIGPIPE 13 /* write on a pipe with no one to read it */#define SIGALRM 14 /* alarm clock */#define SIGTERM 15 /* software termination signal from kill */#define SIGURG 16 /* urgent condition on IO channel */#define SIGSTOP 17 /* sendable stop signal not from tty */#define SIGTSTP 18 /* stop signal from tty */#define SIGCONT 19 /* continue a stopped process */#define SIGCHLD 20 /* to parent on child stop or exit */#define SIGTTIN 21 /* to readers pgrp upon background tty read */#define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */#if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))#define SIGIO 23 /* input/output possible signal */#endif#define SIGXCPU 24 /* exceeded CPU time limit */#define SIGXFSZ 25 /* exceeded file size limit */#define SIGVTALRM 26 /* virtual time alarm */#define SIGPROF 27 /* profiling time alarm */#if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))#define SIGWINCH 28 /* window size changes */#define SIGINFO 29 /* information request */#endif#define SIGUSR1 30 /* user defined signal 1 */#define SIGUSR2 31 /* user defined signal 2 */
信號量值 | 名稱 | 發送方式 | 說明 |
---|---|---|---|
(2) | SIGINT | ctrl +c | 終止信號 |
(3) | SIGQUIT | ctrl + \ | 暫停信號,放入后臺 |
(4) | SIGILL | 非法指令 | |
(5) | SIGTRAP | abort(3) | 進程異常終止 |
(7) | SIGBUS | (虛實關系建立) 總線錯誤(從寫的位置到物理內存,操作系統沒有將磁盤的開始位置到物理內存之間建立 聯系 mmap(把虛擬內存和磁盤文件的關系映射起來,如果磁盤大小大于 0,就建立這種關系 | |
(9) | SIGKILL | kill - 9 pid | 殺死進程 |
(11) | SIGSEGV | 段錯誤 | |
(13) | SIGPIPE | 管道破裂 | |
(14) | SIGALRM | 鬧鐘 | |
(15) | SIGTERM | 缺省終止某個進程,終止掉 | |
(17) | SIGSTOP | 子進程死的時候會給父進程發送這個信號 | |
(19) | SIGCONT | 進程暫停 | |
(23) | SIGURG | 緊急數據 | |
(29) | SIGINFO | 異步 IO |
信號的默認處理方式:
這個就要使用 man 手冊自己去查找啦,有好多呢,記住常用的就可以啦
man 7 signal 在第七頁,所以直接用 man 7
進程收到信號的三種處理方式
默認:如果是系統默認的話,那就會終止這個進程
忽略 :信號來了我們不處理,裝作沒看到 SIGKILL SIGSTOP 不能忽略
捕獲并處理 :當信號來了,執行我們自己寫的代碼(捕獲信號這個動作是需要我們完成的) SIGKILL SIGSTOP 不能捕獲
注冊信號
typedef void (\* sighandler_t) (int);sighandler_t signal (int signum //要注冊的信號,sighandler_t handler); //信號執行函數 1.自己定義函數2.SIG_IGN #define SIG_IGN (sighandler_t(1))把 1 強制轉化為函數指針類型3.SIG_DFL #define SIG_IGN (sighandler_t(0))把 0 強制轉化為函數指針類型 返回錯誤 SIG_ERR#define SIG_ERR
信號是異步事件,當信號到達時,保存當前的執行環境,轉去執行信號處理函數,當信號處理函數完畢,恢復現場,繼續執行
這里我們看一下可靠信號和不可靠信號的區別吧
不可靠信號
Linux 的信號繼承自早期的 Unix 信號,Unix 信號的缺陷 1.信號處理函數執行完畢,信號恢復成默認處理方式(Linux 已經改進) 2.會出現信號丟失,信號不排隊
1-31 都是不可靠的,會出現信號丟失現象
可靠信號
34-64 重新設計的一套信號集合
不會出現信號丟失,支持排隊,信號處理函數執行完畢,不會恢復成缺省處理方式
實時信號 : 就是可靠信號
非實時信號:不可靠信號
發送信號:
kill -信號值 pid
int kill(int pid,int signum)
pid > 0 :發送給 pid 進程
pid = 0 :調用者所在進程組的任一進程
pid = -1: 有權發送的任何一個進程,除了 1
pid < -1 |pid|進程組所有的進程
進程組:進程組中有若干個進程
用管道連接的進程, fork 創建的父子進程都屬于同一個進程組
sleep 返回值 > 0 表示還剩多少秒沒睡就被信號打斷
給自己發信號
raise( int signum )
kill(getpid() ,signum)
給進程組發信號
int killpg(int gid,int signum);
暫停進程,直到被信號打斷
int pause( void )把當前進程變成就緒態,讓出 CPU calling process
SIGALRM
int alarm(int sec)
當 sec 規定的時間到了,觸發 SIGALRM 信號
如果 sec 是 0,表示清除信號
下面是兩個 SIGALRM 信號的使用范例,希望通過這兩個實例可以加深你們對于鬧鐘信號的理解啊
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>void handler(int s) //信號到來,則執行這個函數,輸出超時{ printf("超時\n"); exit(1);}int main(){ char buf[100] = {}; printf("輸入名字"); signal(SIGALRM, handler); //定義一個信號函數,當 SIGALRM 信號發過來時,執行 handler 函數 alarm(5); //設置五秒的時鐘,五秒內如果沒有執行輸入操作就會發送信號 scanf("%s", buf); alarm(0); //如果五秒內執行了操作,那就清空鬧鐘 printf("名字為:%s\n", buf); for (;;) //驗證鬧鐘時間已經清空 { fflush(stdout); printf("."); sleep(1); }}
執行結果
基于鬧鐘原理,我們再來實現一個更加復雜的小型考試系統
代碼如下
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>int count = 0; //初始化int wrong = 0; //初始化void handler(int s) //當鬧鐘信號發過來,執行這個函數,輸出對錯的個數{ printf("time out \n"); printf("right = %d,wrong = %d\n ", count, wrong); exit(1);}int main(){ int i = 0; signal(SIGALRM, handler); //定義信號函數接收信號 alarm(30); //設置鬧鐘時間為 30 秒 srand(getpid()); //用子進程 id 作為隨機種子數 for (i = 0; i < 10; i++) //循環十次,輸出隨機數相加結果 { int left = rand() % 10; int right = rand() % 10; printf("%d + %d=", left, right); int ret; while (getchar() != '\n') ; //清空輸入緩沖區,防止異常輸入使得程序崩潰 scanf("%d", &ret); if (left + right == ret) //判斷對錯 { count++; } else { wrong++; } } printf("做完了\n"); printf("right =%d,wrong = %d \n", count, wrong);}
程序結果如下
從這兩個實例,我們已經可以知道時鐘的簡單用法啦,那么說了這么多,我覺得你們也可以自己上手去做一個以鬧鐘為中心的小程序啦。再另一篇博客里面我會講一講時鐘的用法,希望可以幫助到你們哦。
注:我也是個初學者啦,如果哪里又紕漏之處,歡迎指正啊,也可以私信我,我很喜歡交流的。
beylze編程學院,一個分享編程知識和seo優化知識的網站。跟著beylze一起學習,每天都有進步。
通俗易懂,深入淺出,一篇文章只講一個知識點。
文章不深奧,不需要鉆研,在公交、在地鐵、在廁所都可以閱讀,隨時隨地漲姿勢。
文章不涉及代碼,不燒腦細胞,人人都可以學習。
當你決定關注beylze(公眾號:beylze),你已然超越了90%的其他從業者!