linux信號機制基礎

通過本文你會了解到:
1. 信號機制簡介
2. 信號處理函數( signal/sigaction/kill)

信號機制簡介
信號機制就是進程間相互傳遞消息的機制,又稱做軟中斷信號。就像電話一樣,你不能確定什麼時候有人打電話,隨時隨地發生。linux系統中處理信號有一下幾種方式:

  • Term - 終止進程
  • Ign - 忽略該信號
  • Core - 終止該進程並保存內存信息
  • Stop - 停止進程
  • Cont - 恢復已停止的進程

除了上述的5種處理,用戶還可以自定義處理函數。

信號處理函數
那麼在linux中應用什麼函數來實現對信號進行處理呢,接下來我們一起來學習幾個常用函數:

signal函數 – 爲信號設置處理方法 – 早期linux版本使用,知道怎麼用就可以了,在編程時還是不要用這麼老的函數了。

#include <signal.h>

typedef void (*sighandler_t)(int);
sighandler_t signalint signum, sighandler_t handler);

signum - 信號值
handler - 可以爲自定義的信號處理函數,也可以是SIG_IGN(忽略該信號)和SIG_DEL(信號默認處理)。

下面以大家最熟悉的ctrl-c(終止進程)爲例,根據handler取值不同給出實例(終於可以上代碼了,竊喜):

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

void signal_handler(int signo)
{
    switch(signo) {
        case SIGINT:
            printf("handle SIGINT by user\n");
            break;
        default:
            break;
    }

}

int main(int argc, char **argv)
{
    printf("ctr-c will be handled by user, enter to continue..\n");
    signal(SIGINT, signal_handler);
    getchar();

    printf("ctr-c will be ignore, enter to continue..\n");
    signal(SIGINT, SIG_IGN);
    getchar();

    printf("ctr-c will handle by default, enter to continue..\n");
    signal(SIGINT, SIG_DFL);
    getchar();

    printf("thanks, bye!\n");

    return 0;
}

運行測試:

$ ./signal
ctr-c will be handled by user, enter to continue..
^Chandle SIGINT by user // 信號被自定義函數獲取
^Chandle SIGINT by user // 信號被自定義函數獲取

ctr-c will be ignore, enter to continue..
^C^C // 信號被忽略
ctr-c will handle by default, enter to continue..
^C // 默認操作,終止進程

sigaction函數 - 爲信號設置處理方法

#include <signal.h>

int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

signum - 信號值
act - sigaction結構體,指定當前信號的處理信息
oldact - sigaction結構體,返回信號之前的處理信息
要了解後兩個參數的作用必須要瞭解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函數中的處理函數作用相同,可以爲用戶自定義的處理函數,也可以爲SIG_IGNSIG_DFL
sa_sigaction - 信號的另一個處理函數,當sa_flags含有SA_SIGINFO時,應用此處理函數。此函數可以提供信號的更過信息。
sa_mask - 指定在信號處理函數運行期間需要屏蔽的信號。屏蔽的信號會被阻塞直到處理函數結束。
sa_flags - 指定信號的處理行爲,可以爲0SA_NOCLDSTOP/A_NOCLDSTOP等,具體參考man 7 signal
sa_restorer - 暫爲使用,保留。

sigaction的使用也時比較複雜的,後續會結合具體實例做分析,先給一個簡單的例子,完成與signal實例同樣的功能:

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

void signal_handler(int signo)
{
    switch(signo) {
        case SIGINT:
            printf("handle SIGINT by user\n");
            break;
        default:
            break;
    }

}

int main(int argc, char **argv)
{
    struct sigaction act;

    printf("ctr-c will be handled by user, enter to continue..\n");
    act.sa_flags = 0;
    act.sa_handler = signal_handler;
    sigaction(SIGINT, &act, NULL);
    getchar();

    printf("ctr-c will be ignore, enter to continue..\n");
    act.sa_flags = 0;
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);
    getchar();

    printf("ctr-c will handle by default, enter to continue..\n");
    act.sa_flags = 0;
    act.sa_handler = SIG_DFL;
    signal(SIGINT, SIG_DFL);
    getchar();

    printf("thanks, bye!\n");

    return 0;
}

運行測試:

$ ./sigaction 
ctr-c will be handled by user, enter to continue..
^Chandle SIGINT by user // 信號被自定義函數獲取,此處與signal行爲不同,getchar函數被中斷
ctr-c will be ignore, enter to continue..
^C^C^C // 信號被忽略
ctr-c will handle by default, enter to continue..
^C // 默認處理,終端程序

信號發送函數

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

int kill(pid_t pid, int sig);

pid:可能選擇有以下四種
1. pid大於零時,將信號發送給進程ID爲pid的進程。
2. pid等於零時,信號將送往所有與調用kill()的那個進程屬同一個使用組的進程。
3. pid等於-1時,信號將送往所有調用進程有權給其發送信號的進程,除了進程1(init)。
4. pid小於-1時,信號將送往以-pid爲組標識的進程。

sig:信號值,若爲0則不發送任何信號。

kill函數的使用實例:

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

void signal_handler(int signo)
{
    switch(signo) {
        case SIGUSR1:
            printf("get signal -- SIGUSR1\n");
            break;
        default:
            break;
    }

}

int main(int argc, char **argv)
{
    pid_t pid;
    struct sigaction act;

    act.sa_flags = 0;
    act.sa_handler = signal_handler;
    sigaction(SIGUSR1, &act, NULL);

    pid = getpid();

    printf("send SIGUSR1 to curent process\n");
    kill(pid, SIGUSR1);

    getchar();

    printf("thanks, bye!\n");

    return 0;
}

運行測試:

$ ./kill 
send SIGUSR1 to curent process
get signal -- SIGUSR1

thanks, bye!

本文代碼實例github地址:
https://github.com/zsirkg/myWorks/tree/master/signal

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章