Linux訊號捕捉之sigaction

嚇人的猿發表於2018-02-27

程式可以通過三種方式來響應和處理一個訊號:

​(1)忽略訊號:即對訊號不做任何處理,但是兩個訊號不能忽略:SIGKILL以及SIGSTOP.

(2)捕捉訊號:當訊號發生時,執行使用者定義的訊號處理函式。

(3)執行預設操作: Linux對每種訊號都規定了預設操作,man 7 signal檢視。

Term Default action is to terminate the process.

Ign Default action is to ignore the signal.

Core Default action is to terminate the process and dump core (see core(5)).

Stop Default action is to stop the process.

Cont Default action is to continue the process if it is currently stopped.


sigaction函式捕捉訊號

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

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 : 早期的捕捉函式
sa_sigaction : 新新增的捕捉函式,可以傳參, 和sa_handler互斥,兩者通過sa_flags選擇採用哪種捕捉函式
sa_mask : 在執行捕捉函式時,設定阻塞其它訊號,sa_mask | 程式阻塞訊號集,退出捕捉函式後,還原回原有的
阻塞訊號集
sa_flags : SA_SIGINFO 或者0用來指定呼叫sa_handler還是sa_sigaction,SA_SIGINFO時為呼叫sa_sigaction,
           SA_RESTART 讓被打斷的系統呼叫重新開始
sa_restorer : 保留,已過時
*/
  • sigaction例子

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sig_handle(int sig)
{
    puts("recv SIGINT");
    sleep(5);
    puts("end");
}

int main(int argc, char** argv)
{
    struct sigaction act;
    act.sa_handler = sig_handle;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGQUIT); //當進入訊號處理函式的時候,遮蔽掉SIGQUIT的遞達

    sigaction(SIGINT, &act, NULL);
    while(1)
        sleep(1);
    return 0;
}

read函式的EINTR錯誤

    訊號能中斷一些系統呼叫,返回-1並且設定errno為EINTR,例如read write sleep等,如果想要恢復這些函式繼續執行可以加上SA_RESTART標誌。

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
void sig_handle(int sig)
{
    printf("SIGINT\n");
}

int main(int argc, char** argv)
{
    char buf[10];
    struct sigaction act;
    act.sa_handler = sig_handle;
    act.sa_flags = 0;
    //act.sa_flags = SA_RESTART;  //先試一試sa_flags為0時,然後再試一試SA_RESTART的情況
    sigemptyset(&act.sa_mask);

    sigaction(SIGINT, &act, NULL);

    puts("read stdio:");
    int ret = read(STDIN_FILENO,  buf, 9);
    if(ret == -1)
    {
        if(errno == EINTR)
            perror("read:");
    }
    else
    {
        buf[ret] = '\0';
        printf("read %d : %s", ret,  buf);
    }
    return 0;
}

​ 以上的執行結果的是,act.sa_flags = 0時一旦傳送了一個訊號,read將被打斷,返回-1,並且設定errno為EINTR。如果想打斷後繼續執行read,可以設定一下act.sa_flags = SA_RESTART。或者也可以使用signal函式來處理訊號,signal相當於預設設定了SA_RESTART標誌。

相關文章