日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Linux信號處理機制

 天空上的極限 2016-08-04

在Linux中,信號是進(jìn)程間通訊的一種方式,它采用的是異步機制。當(dāng)信號發(fā)送到某個進(jìn)程中時,操作系統(tǒng)會中斷該進(jìn)程的正常流程,并進(jìn)入相應(yīng)的信號處理函數(shù)執(zhí)行操作,完成后再回到中斷的地方繼續(xù)執(zhí)行。

需要說明的是,信號只是用于通知進(jìn)程發(fā)生了某個事件,除了信號本身的信息之外,并不具備傳遞用戶數(shù)據(jù)的功能。

1 信號的響應(yīng)動作

每個信號都有自己的響應(yīng)動作,當(dāng)接收到信號時,進(jìn)程會根據(jù)信號的響應(yīng)動作執(zhí)行相應(yīng)的操作,信號的響應(yīng)動作有以下幾種:

  • 中止進(jìn)程(Term)
  • 忽略信號(Ign)
  • 中止進(jìn)程并保存內(nèi)存信息(Core)
  • 停止進(jìn)程(Stop)
  • 繼續(xù)運行進(jìn)程(Cont)

用戶可以通過signalsigaction函數(shù)修改信號的響應(yīng)動作(也就是常說的“注冊信號”,在文章的后面會舉例說明)。另外,在多線程中,各線程的信號響應(yīng)動作都是相同的,不能對某個線程設(shè)置獨立的響應(yīng)動作。

2 信號類型

Linux支持的信號類型可以參考下面給出的列表。

2.1 在POSIX.1-1990標(biāo)準(zhǔn)中的信號列表

信號 動作 說明
SIGHUP 1 Term 終端控制進(jìn)程結(jié)束(終端連接斷開)
SIGINT 2 Term 用戶發(fā)送INTR字符(Ctrl+C)觸發(fā)
SIGQUIT 3 Core 用戶發(fā)送QUIT字符(Ctrl+/)觸發(fā)
SIGILL 4 Core 非法指令(程序錯誤、試圖執(zhí)行數(shù)據(jù)段、棧溢出等)
SIGABRT 6 Core 調(diào)用abort函數(shù)觸發(fā)
SIGFPE 8 Core 算術(shù)運行錯誤(浮點運算錯誤、除數(shù)為零等)
SIGKILL 9 Term 無條件結(jié)束程序(不能被捕獲、阻塞或忽略)
SIGSEGV 11 Core 無效內(nèi)存引用(試圖訪問不屬于自己的內(nèi)存空間、對只讀內(nèi)存空間進(jìn)行寫操作)
SIGPIPE 13 Term 消息管道損壞(FIFO/Socket通信時,管道未打開而進(jìn)行寫操作)
SIGALRM 14 Term 時鐘定時信號
SIGTERM 15 Term 結(jié)束程序(可以被捕獲、阻塞或忽略)
SIGUSR1 30,10,16 Term 用戶保留
SIGUSR2 31,12,17 Term 用戶保留
SIGCHLD 20,17,18 Ign 子進(jìn)程結(jié)束(由父進(jìn)程接收)
SIGCONT 19,18,25 Cont 繼續(xù)執(zhí)行已經(jīng)停止的進(jìn)程(不能被阻塞)
SIGSTOP 17,19,23 Stop 停止進(jìn)程(不能被捕獲、阻塞或忽略)
SIGTSTP 18,20,24 Stop 停止進(jìn)程(可以被捕獲、阻塞或忽略)
SIGTTIN 21,21,26 Stop 后臺程序從終端中讀取數(shù)據(jù)時觸發(fā)
SIGTTOU 22,22,27 Stop 后臺程序向終端中寫數(shù)據(jù)時觸發(fā)

:其中SIGKILLSIGSTOP信號不能被捕獲、阻塞或忽略。

2.2 在SUSv2和POSIX.1-2001標(biāo)準(zhǔn)中的信號列表

信號 動作 說明
SIGTRAP 5 Core Trap指令觸發(fā)(如斷點,在調(diào)試器中使用)
SIGBUS 0,7,10 Core 非法地址(內(nèi)存地址對齊錯誤)
SIGPOLL Term Pollable event (Sys V). Synonym for SIGIO
SIGPROF 27,27,29 Term 性能時鐘信號(包含系統(tǒng)調(diào)用時間和進(jìn)程占用CPU的時間)
SIGSYS 12,31,12 Core 無效的系統(tǒng)調(diào)用(SVr4)
SIGURG 16,23,21 Ign 有緊急數(shù)據(jù)到達(dá)Socket(4.2BSD)
SIGVTALRM 26,26,28 Term 虛擬時鐘信號(進(jìn)程占用CPU的時間)(4.2BSD)
SIGXCPU 24,24,30 Core 超過CPU時間資源限制(4.2BSD)
SIGXFSZ 25,25,31 Core 超過文件大小資源限制(4.2BSD)

:在Linux 2.2版本之前,SIGSYS、SIGXCPUSIGXFSZ以及SIGBUS的默認(rèn)響應(yīng)動作為Term,Linux 2.4版本之后這三個信號的默認(rèn)響應(yīng)動作改為Core。

2.3 其它信號

信號 動作 說明
SIGIOT 6 Core IOT捕獲信號(同SIGABRT信號)
SIGEMT 7,-,7 Term 實時硬件發(fā)生錯誤
SIGSTKFLT -,16,- Term 協(xié)同處理器棧錯誤(未使用)
SIGIO 23,29,22 Term 文件描述符準(zhǔn)備就緒(可以開始進(jìn)行輸入/輸出操作)(4.2BSD)
SIGCLD -,-,18 Ign 子進(jìn)程結(jié)束(由父進(jìn)程接收)(同SIGCHLD信號)
SIGPWR 29,30,19 Term 電源錯誤(System V)
SIGINFO 29,-,- 電源錯誤(同SIGPWR信號)
SIGLOST -,-,- Term 文件鎖丟失(未使用)
SIGWINCH 28,28,20 Ign 窗口大小改變時觸發(fā)(4.3BSD, Sun)
SIGUNUSED -,31,- Core 無效的系統(tǒng)調(diào)用(同SIGSYS信號)

注意:列表中有的信號有三個值,這是因為部分信號的值和CPU架構(gòu)有關(guān),這些信號的值在不同架構(gòu)的CPU中是不同的,三個值的排列順序為:1,Alpha/Sparc;2,x86/ARM/Others;3,MIPS。

例如SIGSTOP這個信號,它有三種可能的值,分別是17、19、23,其中第一個值(17)是用在Alpha和Sparc架構(gòu)中,第二個值(19)用在x86、ARM等其它架構(gòu)中,第三個值(23)則是用在MIPS架構(gòu)中的。

3 信號機制

文章的前面提到過,信號是異步的,這就涉及信號何時接收、何時處理的問題。

我們知道,函數(shù)運行在用戶態(tài),當(dāng)遇到系統(tǒng)調(diào)用、中斷或是異常的情況時,程序會進(jìn)入內(nèi)核態(tài)。信號涉及到了這兩種狀態(tài)之間的轉(zhuǎn)換,過程可以先看一下下面的示意圖:

信號處理機制示意圖

接下來圍繞示意圖,將信號分成接收、檢測和處理三個部分,逐一講解每一步的處理流程。

3.1 信號的接收

接收信號的任務(wù)是由內(nèi)核代理的,當(dāng)內(nèi)核接收到信號后,會將其放到對應(yīng)進(jìn)程的信號隊列中,同時向進(jìn)程發(fā)送一個中斷,使其陷入內(nèi)核態(tài)。

注意,此時信號還只是在隊列中,對進(jìn)程來說暫時是不知道有信號到來的。

3.2 信號的檢測

進(jìn)程陷入內(nèi)核態(tài)后,有兩種場景會對信號進(jìn)行檢測:

  • 進(jìn)程從內(nèi)核態(tài)返回到用戶態(tài)前進(jìn)行信號檢測
  • 進(jìn)程在內(nèi)核態(tài)中,從睡眠狀態(tài)被喚醒的時候進(jìn)行信號檢測

當(dāng)發(fā)現(xiàn)有新信號時,便會進(jìn)入下一步,信號的處理。

3.3 信號的處理

信號處理函數(shù)是運行在用戶態(tài)的,調(diào)用處理函數(shù)前,內(nèi)核會將當(dāng)前內(nèi)核棧的內(nèi)容備份拷貝到用戶棧上,并且修改指令寄存器(eip)將其指向信號處理函數(shù)。

接下來進(jìn)程返回到用戶態(tài)中,執(zhí)行相應(yīng)的信號處理函數(shù)。

信號處理函數(shù)執(zhí)行完成后,還需要返回內(nèi)核態(tài),檢查是否還有其它信號未處理。如果所有信號都處理完成,就會將內(nèi)核?;謴?fù)(從用戶棧的備份拷貝回來),同時恢復(fù)指令寄存器(eip)將其指向中斷前的運行位置,最后回到用戶態(tài)繼續(xù)執(zhí)行進(jìn)程。

至此,一個完整的信號處理流程便結(jié)束了,如果同時有多個信號到達(dá),上面的處理流程會在第2步和第3步驟間重復(fù)進(jìn)行。

4 信號的使用

4.1 發(fā)送信號

用于發(fā)送信號的函數(shù)有raise、kill、killpgpthread_kill、tgkillsigqueue,這幾個函數(shù)的含義和用法都大同小異,這里主要介紹一下常用的raisekill函數(shù)。

raise函數(shù):向進(jìn)程本身發(fā)送信號

函數(shù)聲明如下:

#include <signal.h>

int raise(int sig);

函數(shù)功能是向當(dāng)前程序(自身)發(fā)送信號,其中參數(shù)sig為信號值。

kill函數(shù):向指定進(jìn)程發(fā)送信號

函數(shù)聲明如下:

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

函數(shù)功能是向特定的進(jìn)程發(fā)送信號,其中參數(shù)pid為進(jìn)程號,sig為信號值。

在這里的參數(shù)pid,根據(jù)取值范圍不同,含義也不同,具體說明如下:

  • pid > 0 :向進(jìn)程號為pid的進(jìn)程發(fā)送信號
  • pid = 0 :向當(dāng)前進(jìn)程所在的進(jìn)程組發(fā)送信號
  • pid = -1 :向所有進(jìn)程(除PID=1外)發(fā)送信號(權(quán)限范圍內(nèi))
  • pid < -1 :向進(jìn)程組號為-pid的所有進(jìn)程發(fā)送信號

另外,當(dāng)sig值為零時,實際不發(fā)送任何信號,但函數(shù)返回值依然有效,可以用于檢查進(jìn)程是否存在。

4.2 等待信號被捕獲

等待信號的過程,其實就是將當(dāng)前進(jìn)程(線程)暫停,直到有信號發(fā)到當(dāng)前進(jìn)程(線程)上并被捕獲,函數(shù)有pausesigsuspend。

pause函數(shù):將進(jìn)程(或線程)轉(zhuǎn)入睡眠狀態(tài),直到接收到信號

函數(shù)聲明如下:

#include <unistd.h>

int pause(void);

該函數(shù)調(diào)用后,調(diào)用者(進(jìn)程或線程)會進(jìn)入睡眠(Sleep)狀態(tài),直到捕獲到(任意)信號為止。該函數(shù)的返回值始終為-1,并且調(diào)用結(jié)束后,錯誤代碼(errno)會被置為EINTR。

sigsuspend函數(shù):將進(jìn)程(或線程)轉(zhuǎn)入睡眠狀態(tài),直到接收到特定信號

函數(shù)聲明如下:

#include <signal.h>

int sigsuspend(const sigset_t *mask);

該函數(shù)調(diào)用后,會將進(jìn)程的信號掩碼臨時修改(參數(shù)mask),然后暫停進(jìn)程,直到收到符合條件的信號為止,函數(shù)返回前會將調(diào)用前的信號掩碼恢復(fù)。該函數(shù)的返回值始終為-1,并且調(diào)用結(jié)束后,錯誤代碼(errno)會被置為EINTR。

4.3 修改信號的響應(yīng)動作

用戶可以自己重新定義某個信號的處理方式,即前面提到的修改信號的默認(rèn)響應(yīng)動作,也可以理解為對信號的注冊,可以通過signalsigaction函數(shù)進(jìn)行,這里以signal函數(shù)舉例說明。

首先看一下函數(shù)聲明:

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

第一個參數(shù)signum是信號值,可以從前面的信號列表中查到,第二個參數(shù)handler為處理函數(shù),通過回調(diào)方式在信號觸發(fā)時調(diào)用。

下面為示例代碼:

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

/* 信號處理函數(shù) */
void sig_callback(int signum) {
    switch (signum) {
        case SIGINT:
            /* SIGINT: Ctrl+C 按下時觸發(fā) */
            printf("Get signal SIGINT. \r\n");
            break;
        /* 多個信號可以放到同一個函數(shù)中進(jìn)行 通過信號值來區(qū)分 */
        default:
            /* 其它信號 */
            printf("Unknown signal %d. \r\n", signum);
            break;
    }

    return;
}

/* 主函數(shù) */
int main(int argc, char *argv[]) {
    printf("Register SIGINT(%u) Signal Action. \r\n", SIGINT);

    /* 注冊SIGINT信號的處理函數(shù) */
    signal(SIGINT, sig_callback);

    printf("Waitting for Signal ... \r\n");

    /* 等待信號觸發(fā) */
    pause();

    printf("Process Continue. \r\n");

    return 0;
}

源文件下載:鏈接

例子中,將SIGINT信號(Ctrl+C觸發(fā))的動作接管(打印提示信息),程序運行后,按下Ctrl+C,命令行輸出如下:

./linux_signal_example
Register SIGINT(2) Signal Action. 
Waitting for Signal ... 
^CGet signal SIGINT. 
Process Continue.

進(jìn)程收到SIGINT信號后,觸發(fā)響應(yīng)動作,將提示信息打印出來,然后從暫停的地方繼續(xù)運行。這里需要注意的是,因為我們修改了SIGINT信號的響應(yīng)動作(只打印信息,不做進(jìn)程退出處理),所以我們按下Ctrl+C后,程序并沒有直接退出,而是繼續(xù)運行并將"Process Continue."打印出來,直至程序正常結(jié)束。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多