<Linux内核学习>内核信号

环境:Linux 0.11 / Linux 3.4.2

参考书籍:Linux内核完全剖析基于0.11内核-赵炯

一、Linux信号是什么,都有什么信号

在系统中,信号是一种"软件中断"处理机制,为异步事件的处理提供了一种有效的方法。例如:① 如果一个进程设置的报警时钟到期后,系统就会向当前进程发送SIGALRM信号。② 进程可以通过kill函数向另一个进程发送终止信号,如在shell终端上输入kill -9 pid,向pid进程发送进程终止信号。

在Linux 0.11内核版本中定义了一共22中信号,具体可在文件/sys/signal.h中查阅,在内核代码中使用一个无符号长整数(32位比特)表示各种不同的信号。因此在当前版本内核中最多定义32个信号。

//定义信号量总共22个
#define SIGHUP         1
#define SIGINT         2
#define SIGQUIT         3
#define SIGILL         4
#define SIGTRAP         5
#define SIGABRT         6
#define SIGIOT         6
#define SIGUNUSED     7
#define SIGFPE         8
#define SIGKILL         9
#define SIGUSR1        10
#define SIGSEGV        11
#define SIGUSR2        12
#define SIGPIPE        13
#define SIGALRM        14
#define SIGTERM        15
#define SIGSTKFLT    16
#define SIGCHLD        17
#define SIGCONT        18
#define SIGSTOP        19
#define SIGTSTP        20
#define SIGTTIN        21
#define SIGTTOU        22

二、进程收到信号的处理方式

对于一个进程在接收到信号后有三种处理方式:

① 忽略该信号,但是SIGKILL和SIGSTOP信号忽略不了。

② 捕获信号,执行默认信号处理函数。

③ 捕获信号,执行自定义信号处理函数。

进程修改原信号处理句柄的两种方式

① signal()

函数原型如下:

void (*signal(int _sig, void (*_func)(int)))(int);

signal函数有两个参数:

  1. _sig为要捕获的信号

  1. 另外一个参数_func是新的信号处理句柄,参数_func即可以是用户自定义的信号的信号处理句柄,也可以是内核定义的函数指针SIG_IGN或SIG_DFL,SIG_IGN代表忽略该信号,SIG_DFL代表接收到信号后执行默认的操作。

注意:新的信号处理句柄在执行完一次以后,会恢复到默认的SIG_DFL,因此要想重复捕获该信号执行自定义处理句柄,代码如下:

void handler(int sign)
{
    signal(SIGINT, handler);
    ........ //处理函数代码
}

main()
{
    signal(SIGINT, handler);
}

② sigaction

signal函数具有一定的不可靠性质,因为在自定义信号处理句柄中,在重新设置自己的句柄之前,该信号的处理句柄为默认操作,如果此时再来这个信号,就会造成信号的丢失。

因此通常采用可靠的sigaction()函数执行此机制,函数原型如下:

int sigaction(int sig, struct sigaction *act, struct sigaction *oldact);

//结构体定义
struct sigaction {
    void (*sa_handler)(int);//信号处理函数
    sigset_t sa_mask;//是否屏蔽
    int sa_flags;
    void (*sa_restorer)(void);
};

a. sig为要捕获的信号

b. 函数根据结构体指针act的内容指定信号的行为

c. 函数会将sig信号原来的行为保存到okdact指针中

d.结构体内容sa_mask是一个需要加入到当前进程的信号屏蔽图集,作用是在执行自定义信号句柄sa_handler的中途阻塞当前信号的再次出现,并在信号句柄返回时会恢复到进程原来的信号屏蔽图集。

注意:在阻塞解除前的多个同一信号,只会调用一次任务句柄。

e.结构体内容sa_flags用于指定其他的一些处理事项,选项如下:

/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
#define SA_NOCLDSTOP    1
#define SA_NOMASK    0x40000000
#define SA_ONESHOT    0x80000000

三、信号预处理函数do_signal()

do_signal函数是在发生内核调用系统调用(0x80)中断或者时钟中断时将信号处函数句柄插入到用户堆栈中。具体流程如下图所示:

信号处理流程如下:

在系统调用中断或时钟中断中调用

->call _sys_call_table(,%eax,4)(在系统调用表中找到sys_signal函数)

->sys_signal()(实现信号的预处理)

->do_signal() (将信号处函数句柄插入到用户堆栈中,使系统调用或时钟中断返回时可以直接执行信号处理句柄)


sys_signal函数如下:

//进行一些信号的预处理设置
int sys_signal(int signum, long handler, long restorer)
{
    struct sigaction tmp;//设置一个信号结构体

    if (signum<1 || signum>32 || signum==SIGKILL)//检索信号范围在1-32之间且不是终止信号
        return -1;
        //指定信号处理句柄
    tmp.sa_handler = (void (*)(int)) handler;
    //设置屏蔽码
    tmp.sa_mask = 0;
    //设置信号的状态为只可执行一次就恢复到默认值 
    tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
    //保存恢复处理程序指针
    tmp.sa_restorer = (void (*)(void)) restorer;
    //更新当前标识指针的信号信息
    handler = (long) current->sigaction[signum-1].sa_handler;
    current->sigaction[signum-1] = tmp;
    return handler;
}

do_signal函数具体分析可在pdf P304找到

猜你喜欢

转载自blog.csdn.net/qq_42174306/article/details/128778183