信号究竟发给谁

来源:岁月联盟 编辑:猪蛋儿 时间:2012-02-09

信号究竟发给谁

Linux中进程和线程本是一个东西,在内核中都是由task_struct结构标示。
两者都是由do_fork内核函数来启动,只是调用do_fork的参数不同。
如果进程只有一个线程,那么发给该进程的信号显然只能发给这个线程;
信号和信号处理函数是进程资源,那么当进程有多个线程时,信号究竟发给谁呢?
我做了3个实验来探讨这个问题,实验所采用的平台如下:

OS: fedora 11
kernel: 2.6.29.4-167.fc11.i686.PAE
gcc: (GCC) 4.4.0 20090506 (Red Hat 4.4.0-4)

编译命令:gcc -g -W -Wall -Wextra -o mytest main.c -lpthread

实验1
首先用下面的代码作测试
main.c:
========================================
// 2011年 11月 29日 星期二 08:37:06 CST
// author: 李小丹(Li Shao Dan) 字 殊恒(shuheng)
// K.I.S.S
// S.P.O.T

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>

#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>

void *work_thread(void *);

void work();

void handler(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGQUIT, &sa, 0);
    pthread_t tid;
    pthread_create(&tid, 0, work_thread, 0);
    work();
    return 0;
}

void handler(int s)
{
    char buf[32];
    snprintf(buf, sizeof(buf), "/n%ld recv signal", syscall(SYS_gettid));
    psignal(s, buf);
}

void *work_thread(void *p)
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, 0);

    work();
    return (void *)0;
}

void work()
{
    for(;;) {
        pause();
        fprintf(stderr, "I/'m Zuro %ld/n", syscall(SYS_gettid));
    }
}
===========================================================

我在主线程里注册SIGQUIT信号处理函数,在另一个线程里注册SIGINT信号处理函数,
但是当我执行该程序时无论我在终端键入Ctrl-C(发送SIGINT信号)
还是Ctrl-/(发送SIGQUIT信号),信号都是发给主线程!
程序输出如下:
=========================================
^C
2536 recv signal: Interrupt
I'm Zuro 2536
^C
2536 recv signal: Interrupt
I'm Zuro 2536
^/
2536 recv signal: Quit
I'm Zuro 2536
^/
2536 recv signal: Quit
I'm Zuro 2536
==========================================
用kill命令杀死这个进程。

实验2
我把上面的程序修改一下:
main.c:
=============================================
// 2011年 11月 29日 星期二 08:37:06 CST
// author: 李小丹(Li Shao Dan) 字 殊恒(shuheng)
// K.I.S.S
// S.P.O.T

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>

#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>

void *work_thread(void *);

void work();

void handler(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    //sigaction(SIGQUIT, &sa, 0);
    sigaction(SIGSEGV, &sa, 0);
    pthread_t tid;
    pthread_create(&tid, 0, work_thread, 0);
    work();
    return 0;
}

void handler(int s)
{
    char buf[32];
    snprintf(buf, sizeof(buf), "/n%ld recv signal", syscall(SYS_gettid));
    psignal(s, buf);
}

void *work_thread(void *p)
{
    for(;;) {
        fprintf(stderr, "I/'m Zuro %ld/n", syscall(SYS_gettid));
        sleep(2);
        char *p = 0;
        *p = 'a';
    }

    return (void *)0;
}

void work()
{
    for(;;) {
        pause();
        fprintf(stderr, "I/'m Zuro %ld/n", syscall(SYS_gettid));
    }
}

=====================================================================
现在我在主线程里注册SIGSEGV信号处理函数,而在另一个线程里产生一个SIGSEGV信号。
编译执行,等一段时间后Ctrl-C终止该进程。发现这时信号不再发送给主线程,
而是发送给产生SIGSEGV信号的那个线程!

实验3
我再把代码作一下修改:
main.c:
=======================================================
// 2011年 11月 29日 星期二 08:37:06 CST
// author: 李小丹(Li Shao Dan) 字 殊恒(shuheng)
// K.I.S.S
// S.P.O.T

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>

#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>

void *work_thread(void *);

void work();

void handler(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGQUIT, &sa, 0);
    pthread_t tid;
    pthread_create(&tid, 0, work_thread, 0);

    work();
    return 0;
}

void handler(int s)
{
    char buf[32];
    snprintf(buf, sizeof(buf), "/n%ld recv signal", syscall(SYS_gettid));
    psignal(s, buf);
}

void *work_thread(void *p)
{
    /*struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, 0);*/

    for(;;) {
        fprintf(stderr, "I/'m Zuro %ld/n", syscall(SYS_gettid));
        sleep(2);
        raise(SIGQUIT);
    }

    return (void *)0;
}

void work()
{
    for(;;) {
        pause();
        fprintf(stderr, "I/'m Zuro %ld/n", syscall(SYS_gettid));
    }
}
===================================================
这次我在主线程注册SIGQUIT信号处理函数,而在另一个线程
调用raise(SIGQUIT)函数产生这个信号。注意实验1和实验3的
区别:实验1的SIGQUIT信号是由终端shell发送,而实验3的信号
是由进程本身的一个线程产生。实验表明:信号不是发送
给主线程,而是发送给产生SIGQUIT信号的线程。


综上所诉,如果信号是由本进程产生,那么信号发给产生这个信号
的线程;如果信号是由其他进程产生,则信号发送给主线程。


摘自 leeshuheng的专栏

图片内容