欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

信号究竟发给谁

程序员文章站 2022-06-01 20:00:29
信号究竟发给谁 linux中进程和线程本是一个东西,在内核中都是由task_struct结构标示。 两者都是由do_fork内核函数来启动,只是调用do_fork的参数不同。...

信号究竟发给谁

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的专栏