linux 信號與處理


一、linux信號是什么

  1. 基本概念
    信號是事件發生時對進程的通知機制,也就是所謂的軟件中斷。信號和硬件的中斷類似,是軟件層對中斷機制的模擬,在多數情況下是無法預測信號產生的時間,所以軟件層提供了一種處理異步事件的方法。

二、 信號來源

信號的來源分為硬件來源和軟件來源。

  1. 硬件來源:
  • 硬件發生異常,即硬件檢測到錯誤條件並通知內核,隨即再由內核發送相應的信號給相關進程,如除數為0、無效的內存引用等。
  • 用戶按終端鍵,引起終端產生的信號(比如Ctrl + C鍵產生SIGINT)。
  1. 軟件來源:
  • 用戶通過指令殺死,如kill指令。
  • 發生軟件事件, 如程序執行raise, alarm、setitimer、sigqueue等函數。

三、 信號處理

信號通常是發送給對應的進程,當信號到達后,該進程需要做出相應的處理措施,通常進程會視具體信號執行相應的操作,有三種操作方式。

  1. 忽略信號:
      信號到達后、直接忽略,就好像是沒有出該信號,信號對該進程不會產生任何影響。事實上,大多數信號都可以使用這種方式進行處理,但有兩種信號卻決不能被忽略,分別是SIGKILL 和 SIGSTOP。
  2. 捕獲信號:
      當信號到達進程后,執行signal()綁定好的信號處理函數。
  3. 執行系統默認操作:
      進程不對該信號事件作出處理,而是交由系統進行處理,每一種信號都會有其對應的系統默認的處理方式。

四、常見信號

在linux系統中通過kill -l命令可以查看到相應的信號。信號編號是從 1 開始,不存在編號為 0 的信號,事實上 kill()函數對信號編號 0 有着特殊的應用。

注意:括號" ) "前面的數字對應該信號的編號,編號 1~31 所對應的是不可靠信號,編號 34~64 對應的是可靠信號,從圖中可知,可靠信號並沒有一個具體對應的名字,而是使用了 SIGRTMIN+N 或 SIGRTMAXN 的方式來表示。其中32和33空缺。
不可靠信號表

名稱 解釋 默認動作
1 SIGHUP 掛起
2 SIGINT 中斷
3 SIGQUIT 退出
4 SIGILL 非法指令
5 SIGTRAP 斷點或陷阱指令
6 SIGABRT abort發出的信號
7 SIGBUS 非法內存訪問
8 SIGFPE 浮點異常
9 SIGKILL kill信號 不能被忽略、處理和阻塞
10 SIGUSR1 用戶信號1
11 SIGSEGV 無效內存訪問
12 SIGUSR2 用戶信號2
13 SIGPIPE 管道破損,沒有讀端的管道寫數據
14 SIGALRM alarm發出的信號
15 SIGTERM 終止信號
16 SIGSTKFLT 棧溢出
17 SIGCHLD 子進程退出 默認忽略
18 SIGCONT 進程繼續
19 SIGSTOP 進程停止 不能被忽略、處理和阻塞
20 SIGTSTP 進程停止
21 SIGTTIN 進程停止,后台進程從終端讀數據時
22 SIGTTOU 進程停止,后台進程想終端寫數據時
23 SIGURG I/O有緊急數據到達當前進程 默認忽略
24 SIGXCPU 進程的CPU時間片到期
25 SIGXFSZ 文件大小的超出上限
26 SIGVTALRM 虛擬時鍾超時
27 SIGPROF profile時鍾超時
28 SIGWINCH 窗口大小改變 默認忽略
29 SIGIO I/O相關
30 SIGPWR 關機 默認忽略
31 SIGSYS 系統調用異常

五、信號處理

  1. signal()
    "signal.h"信號處理庫提供了signal函數,用來捕獲突發事件。以下是 signal() 函數的語法ads。
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • signum:可使用信號名(宏)或信號的數字編號,建議使用信號名。
  • handler:參數 handler 既可以設置為用戶自定義的函數,也可以設置為 SIG_IGN 或 SIG_DFL,SIG_IGN 表示此進程需要忽略該信號,SIG_DFL 則表示設置為系統默認操作。
  1. raise()
    有時進程需要向自身發送信號,raise()函數可用於實現這一要求.
int raise(int sig);
  • sig:需要發送的信號。
  1. sigaction()
    除了signal()之外,sigaction()系統調用是設置信號處理方式的另一選擇,雖然 signal()函數簡單好用,而 sigaction()更為復雜,但作為回報,sigaction()也更具靈活性以及移植性。
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum:需要設置的信號,除了 SIGKILL 信號和 SIGSTOP 信號之外的任何信號。
  • act:參數 act 不為 NULL,則表示需要為信號設置新的處理方式;如果參數 act 為 NULL,則表示無需改變信號當前的處理方式
  • oldact:參數oldact 不為 NULL,則會將信號之前的處理方式等信息通過參數 oldact 返回出來;如果無意獲取此類信息,那么可將該參數設置為 NULL。
  • 返回值:成功返回 0;失敗將返回-1,並設置 errno。
    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_handler:指定信號處理函數,與 signal()函數的 handler 參數相同。
  • sa_sigaction:也用於指定信號處理函數,這是一個替代的信號處理函數。
  • sa_mask:參數 sa_mask 定義了一組信號。
  • sa_restorer:該成員已過時,不要再使用了。
  • sa_flags:參數 sa_flags 指定了一組標志,這些標志用於控制信號的處理過程。
  1. kill()
    kill()系統調用可將信號發送給指定的進程或進程組中的每一個進程。
int kill(pid_t pid, int sig);
  • pid:參數 pid 為正數的情況下,用於指定接收此信號的進程 pid。
  • sig:參數 sig 指定需要發送的信號,也可設置為 0,如果參數 sig 設置為 0 則表示不發送信號,但任執行錯誤檢查,這通常可用於檢查參數 pid 指定的進程是否存在。
  • 返回值:成功返回 0;失敗將返回-1,並設置 errno。
  1. alarm()
    使用 alarm()函數可以設置一個定時器(鬧鍾),當定時器定時時間到時,內核會向進程發送 SIGALRM信號。
unsigned int alarm(unsigned int seconds);
  • seconds:設置定時時間,以秒為單位;如果參數 seconds 等於 0,則表示取消之前設置的 alarm 鬧鍾。
  • 返回值:如果在調用 alarm()時,之前已經為該進程設置了 alarm 鬧鍾還沒有超時,則該鬧鍾的剩余值作為本次 alarm()函數調用的返回值,之前設置的鬧鍾則被新的替代;否則返回 0。
  1. pause()
    pause()系統調用可以使得進程暫停運行、進入休眠狀態,直到進程捕獲到一個信號為止,只有執行了信號處理函數並從其返回時,pause()才返回,在這種情況下,pause()返回-1,並且將 errno 設置為EINTR。
int pause(void);
  1. 使用案例
    demo1
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

void signal_handler(int signum)
{
    printf("Interrupt signal (%d) received. \n", signum);
    switch(signum){
        case SIGINT:
            printf("ctrl + c \n");
            exit(signum);
            break;
        case SIGQUIT:
            printf("ctrl + \\ \n");
            exit(signum);

    }
}

int main(int argc, char *argv[]) {
    int i = 0;
    
    //注冊信號與信號處理程序
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);

    while(1)
    {
        printf("Going to sleep....\n");
        if (i > 5){
            raise( SIGINT);
        }
        sleep(1);
        i++;
    }

    return 0;
}

參考文獻

Linux信號(signal)機制:http://gityuan.com/2015/12/20/signal/
linux 信號及處理過程詳解:https://blog.csdn.net/u010765526/article/details/80085895
linux kill信號詳解:https://www.cnblogs.com/gcb-1991/p/6922694.html
《正點原子應用編程指南》


免責聲明!

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



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