Linux編程基礎——訊號

來源:互聯網
上載者:User
基本概念

訊號在Linux中是一個比較常見的概念,例如我們按Ctrl+C中斷前台進程,通過Kill命令結束進程都是通過訊號實現的。下面就以Ctrl+C為例簡單的說明訊號的處理流程:

  1. 使用者按下Ctrl-C,這個鍵盤輸入產生一個硬體中斷。
  2. 該進程的使用者空間代碼暫停執行,CPU從使用者態切換到核心態處理硬體中斷。
  3. 終端驅動程式將Ctrl-C解釋成一個SIGINT訊號,記在該進程的PCB中(也可以說發送了一個SIGINT訊號給該進程)。
  4. 當核心返回到該進程的使用者空間代碼繼續執行之前,首先處理PCB中記錄的訊號,發現有一個SIGINT訊號待處理,而這個訊號的預設處理動作是終止進程,所以直接終止進程而不再返回它的使用者空間代碼執行。

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

每個訊號都有一個編號和一個宏定義名稱,這些宏定義可以在signal.h中找到,可以通過man signal(7)查看詳細說明:

    Signal Value Action Comment
    -------------------------------------------------------------------------
    SIGHUP 1 Term Hangup detected on controlling terminal
                                 or death of controlling process
    SIGINT 2 Term Interrupt from keyboard
    SIGQUIT 3 Core Quit from keyboard
    SIGILL 4 Core Illegal Instruction
    ...

產生訊號的條件主要有:

  • 使用者在終端按下某些鍵時,終端驅動程式會發送訊號給前台進程。
    例如常見的Ctrl-C產生SIGINT訊號,Ctrl-\產生SIGQUIT訊號,Ctrl-Z產生SIGTSTP訊號。
  • 硬體異常產生訊號,這些條件由硬體檢測到並通知核心,然後核心向當前進程發送適當的訊號。例如當前進程執行了除以0的指令,CPU的運算單元會產生異常,核心將這個異常解釋為SIGFPE訊號發送給進程。
  • 一個進程調用kill函數可以發送訊號給另一個進程。
  • 當核心檢測到某種軟體條件發生時也可以通過訊號通知進程,例如鬧鐘逾時產生SIGALRM訊號,向讀端已關閉的管道寫資料時產生SIGPIPE訊號。
捕捉訊號

如果不想按預設動作處理訊號,使用者程式可以調用sigaction函數接管該訊號的處理流程。由於訊號處理函數的代碼是在使用者空間的,處理過程比較複雜,舉例如下:

  1. 使用者程式註冊了SIGQUIT訊號的處理函數sighandler。
  2. 當前正在執行main函數,這時發生中斷或異常切換到核心態。
  3. 在中斷處理完畢後要返回使用者態的main函數之前檢查到有訊號SIGQUIT遞達。
  4. 核心決定返回使用者態後不是恢複main函數的上下文繼續執行,而是執行sighandler函數,sighandler和main函數使用不同的堆棧空間,它們之間不存在調用和被調用的關係,是兩個獨立的控制流程程。
  5. sighandler函數返回後自動執行特殊的系統調用sigreturn再次進入核心態。
  6. 如果沒有新的訊號要遞達,這次再返回使用者態就是恢複main函數的上下文繼續執行了。

sigaction函數可以讀取和修改與指定訊號相關聯的處理動作,它的聲明如下:

    #include
<signal.h>
    int sigaction(int signum, const
struct sigaction *act, struct sigaction *oldact);

其中關鍵就是第二個參數act,他是一個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_handler這個參數,它通常可以有如下幾種賦值:

  • 常數SIG_IGN表示忽略訊號
  • 常數SIG_DFL表示執行系統預設動作,一般用於恢複訊號處理
  • 賦值為一個函數指標表示用自訂函數捕捉訊號

另外,如果在調用訊號處理函數時,除了當前訊號被自動屏蔽之外,還希望自動屏蔽另外一些訊號,則用sa_mask欄位說明這些需要額外屏蔽的訊號,當訊號處理函數返回時自動回復原來的訊號屏蔽字。

下面就以一個簡單的例子示範下如何?捕捉訊號的過程,該函數的功能比較簡單,就是在Ctrl+C的時候並不直接退出,而是先輸出一條華麗的分割線後才退出。

    #include
<stdio.h>
    #include
<signal.h>
    #include
<unistd.h>
    #include
<stdlib.h>

    void show_and_exit(int sig)
    {
        printf("\n----------------------------\n");
        exit(0);
    }

    int main(void)
    {
        struct sigaction act = {0}, oldact = {0};

        act.sa_handler = show_and_exit;
        //act.sa_flags = SA_RESETHAND | SA_NODEFER;

        //sigaddset(&act.sa_mask, SIGQUIT);

        sigaction(SIGINT, &act, &oldact);

        int count = 0;
        while(1)
        {
            sleep(1);
            printf("sleeping %d\n", count++);
        }
    }

執行該函數結果如下:

    tianfang > run
    sleeping 0
    sleeping 1
    sleeping 2
    ^C
    ----------------------------
    tianfang >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.