信號究竟發給誰
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信號的線程。
綜上所訴,如果信號是由本進程產生,那麼信號發給產生這個信號
的線程;如果信號是由其他進程產生,則信號發送給主線程。