日期:2014-05-16  浏览次数:20714 次

[请教内核高手]内核在何处处理接收到的信号
C/C++ code
#include <signal.h>
#include <stdio.h>

static void alarm_handler(int signo)
{
    printf("3 seconds end.\n");
    alarm(3);
}

int main(void)
{
    // 该程序仅仅做测试,故无错误检查 
    struct sigaction act;
    act.sa_handler = alarm_handler;
    act.sa_flags = 0;
    sigempty(&act.sa_mask);
    
    sigaction(SIGALRM, &act, NULL);
    
    alarm(3);    
    while(1);
    
    return 0;
}

我的主程序在设置了alarm值之后直接进入无系统调用的死循环中,当内核的调度程序检测到该进程alarm<jiffies时,就会向该进程发送 SIGALRM 信号,但不会处理该信号,而且由于是死循环,该进程一直处于 RUNNING 状态(这个我有点不确定),也就没有被唤醒一说。但实际上这个信号却是被处理了,我想问到底在内核哪个部分处理这个信号的?求高手解答。

------解决方案--------------------
[User:root Time:10:04:50 Path:/home/liangdong/c]$ make
gcc -g -I./include -c -o src/main.o src/main.c
gcc -o output src/main.o -lpthread -lm -lz
Makefile done.
[User:root Time:10:04:51 Path:/home/liangdong/c]$ ./output 
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
3 seconds end.
^C

我看到的是假象?
------解决方案--------------------
信号的处理时机是在从内核态切换到用户态时,会执行do_signal()函数来处理信号
你的while(1)死循环并不代表进程一直运行在用户态,系统时钟中断的会在内核态更新当前进程的使用时间相关的变量,因此while(1)也会在内核态和用户态切换,此时就是信号处理的时机
------解决方案--------------------
主要是通过拷贝进程上下文来实现的。
do_signal()调数调用handle_signal(),这些都运行在内核态,而handle_signal()函数会真正地去执行用户态的信号处理函数。这时Linux把保存在内核栈的硬件上下文拷贝到用户态栈(通过setup_frame()),并把进程切换到用户态去执行信号处理函数。当处理程序终止时,sigreturn()再把用户态栈上下文拷贝到内核栈,并恢复用户态栈。
------解决方案--------------------
《深入理解linux内核》“信号”章节书中有讲是如何实现的