信号究竟发给谁
信号究竟发给谁
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的专栏