首頁 > shell教程 閱讀:0更新時間:2020-03-28 02:20:06

關于Linux Shell中進程、信號和捕獲的總結

信號是學習 linux 時必須要熟悉的一部分,沒有了信號 Linux 系統就沒辦法正常的工作。而我們作為 Linux 程序員,也要用到信號來進行程序的運行,沒有了信號,我們的工作將會變得一團糟。這幾天,我把剛剛學的關于信號的知識點總結了一下,分享給大家,希望可以對大家有所幫助。

信號的概念

信號是 linux 系統為了響應某些狀況而產生的事件。進程收到信號后應該采取相應的動作

哪些情況會引發信號

  1. 鍵盤事件   ctrl +c  ctrl +\

  2. 非法內存   如果內存管理出錯,系統就會發送一個信號進行處理

  3. 硬件故障   同樣的,硬件出現故障系統也會產生一個信號

  4. 環境切換   比如說從用戶態切換到其他態,狀態的改變也會發送一個信號,這個信號會告知給系統

怎樣查看信號呢?

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&LTOSTOP) */#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)SIGINTctrl +c終止信號
(3)SIGQUITctrl  + \暫停信號,放入后臺
(4)SIGILL
非法指令
(5)SIGTRAPabort(3)進程異常終止
(7)SIGBUS
(虛實關系建立)       總線錯誤(從寫的位置到物理內存,操作系統沒有將磁盤的開始位置到物理內存之間建立      聯系    mmap(把虛擬內存和磁盤文件的關系映射起來,如果磁盤大小大于 0,就建立這種關系
(9)SIGKILLkill - 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

進程收到信號的三種處理方式

  1. 默認:如果是系統默認的話,那就會終止這個進程

  2. 忽略 :信號來了我們不處理,裝作沒看到    SIGKILL  SIGSTOP 不能忽略

  3. 捕獲并處理 :當信號來了,執行我們自己寫的代碼(捕獲信號這個動作是需要我們完成的)  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%的其他從業者!

相關文章

優秀教程

国产亚洲欧美日韩