问题一:在多线程环境,如果对一个进程发送信号,信号由哪个线程处理?其他线程的工作是否会被信号处理函数中断?
答:进程中的信号是递送到单个线程的。在POSIX.1线程模型中,异步信号被发送到进程以后,进程中当前没有阻塞该信号的某个线程被选中接受信号。在信号处理函数执行过程中,只有接受信号的线程被中断,其他线程不受影响。见代码:
-
#include <signal.h>
-
#include <time.h>
-
#include <stdlib.h>
-
#include <stdio.h>
-
#include <unistd.h>
-
-
typedef void Sigfunc(int);
-
-
sigset_t mask;
-
-
Sigfunc *
-
signal(int signo, Sigfunc *func){
-
struct sigaction act, oact;
-
-
act.sa_handler = func;
-
sigemptyset(&act.sa_mask);
-
act.sa_flags = 0;
-
if (signo == SIGALRM) {
-
#ifdef SA_INTERRUPT
-
act.sa_flags |= SA_INTERRUPT;
-
#endif
-
} else {
-
#ifdef SA_RESTART
-
act.sa_flags |= SA_RESTART;
-
#endif
-
}
-
if (sigaction(signo, &act, &oact) < 0)
-
return(SIG_ERR);
-
return(oact.sa_handler);
-
}
-
-
-
static void sig_int(int signo){
-
printf("\n\tSIGINT REACH\n");
-
}
-
-
void * thr_fn(void *arg){
-
for(;;){
-
printf("thread pid %u\n", pthread_self());
-
sleep(100000);
-
printf("thread(%u) sleep interrupt\n",pthread_self());
-
}
-
}
-
-
int main(void){
-
sigset_t oldmask;
-
int err;
-
void *tret;
-
pthread_t tid;
-
pthread_t tid2;
-
-
sigemptyset(&mask);
-
sigaddset(&mask, SIGINT);
-
-
if(signal(SIGINT,sig_int) == SIG_ERR)
-
perror("signal(SIGINT) error");
-
-
err = pthread_create(&tid, NULL, thr_fn, 0);
-
if(err != 0)
-
perror("Can't create thread");
-
-
err = pthread_create(&tid2, NULL,thr_fn, 0);
-
if(err != 0)
-
perror("Can't create thread");
-
pthread_sigmask(SIG_BLOCK,&mask,&oldmask);
-
sleep(1000000);
-
printf("main thread sleep interrupt\n");
-
sleep(1000000);
-
printf("main thread sleep interrupt\n");
-
return 0;
-
}
在Solaris10上执行的结果如下:
bash-3.2# ./thread_sig
thread pid 3
thread pid 2
^C
SIGINT REACH
thread(2) sleep interrupt
thread pid 2
我们知道,当执行sleep的过程中,一旦接收到信号,sleep函数将被中断。以上代码,由于主线程阻塞了SIGINT信号。因此,thread pid 2 线程被中断。但是,主线程 和 thread pid 3 仍然正常工作。
注: 如果把 pthread_sigmask(SIG_BLOCK,&mask,&oldmask); 写在创建线程的前面,那么所有的线程都将阻塞 SIGINT信号。
问题二:sigwait之前为什么要阻塞所等待的信号?
书上说,“如果信号在sigwait调用的时候没有被阻塞,在完成对sigwait调用
之前会出现一个时间窗,在这个窗口期,某个信号可能在线程完成sigwait调用之前就被递送了”。但是,我在机器上(CentOS5 &
Solaris)测试的结果是——如果不阻塞等待信号,那么信号处理函数将被执行,sigwait继续等待。每次都是如此。如果在sigwait之前阻塞等待信号,则信号处理函数不会被执行,sigwait被唤醒。因为,sigwait函数会自动取消信号集的阻塞状态,只到有新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。见代码:
-
#include <signal.h>
-
#include <time.h>
-
#include <stdlib.h>
-
#include <stdio.h>
-
#include <unistd.h>
-
-
typedef void Sigfunc(int);
-
-
sigset_t mask;
-
-
/* Reliable version of signal(), using POSIX sigaction(). */
-
Sigfunc *
-
signal(int signo, Sigfunc *func)
-
{
-
struct sigaction act, oact;
-
-
act.sa_handler = func;
-
sigemptyset(&act.sa_mask);
-
act.sa_flags = 0;
-
if (signo == SIGALRM) {
-
#ifdef SA_INTERRUPT
-
act.sa_flags |= SA_INTERRUPT;
-
#endif
-
} else {
-
#ifdef SA_RESTART
-
act.sa_flags |= SA_RESTART;
-
#endif
-
}
-
if (sigaction(signo, &act, &oact) < 0)
-
return(SIG_ERR);
-
return(oact.sa_handler);
-
}
-
-
static void sig_int(int signo)
-
{
-
printf("\n\tSIGINT REACH\n");
-
}
-
-
void * thr_fn(void *arg)
-
{
-
int err, signo;
-
for(;;){
-
signo = sigwait(&mask);
-
switch (signo){
-
case SIGINT:
-
printf("\nsigwait wake up\n");
-
break;
-
default:
-
printf("unexpected signal %d\n",signo);
-
exit(1);
-
}
-
}
-
}
-
-
int main(void)
-
{
-
int err;
-
void *tret;
-
pthread_t tid;
-
-
sigset_t oldmask;
-
sigemptyset(&mask);
-
sigaddset(&mask, SIGINT);
-
-
if(signal(SIGINT,sig_int) == SIG_ERR)
-
perror("signal(SIGINT) error");
-
/*
-
if((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
-
perror("SIG_BLOCK error");
-
*/
-
err = pthread_create(&tid, NULL, thr_fn, 0);
-
if(err != 0)
-
perror("Can't create thread");
-
-
err = pthread_join(tid, tret);
-
if (err != 0)
-
perror("Can't join");
-
-
return 0;
-
}
结果如下:
bash-3.2# ./thread_sig
^C
SIGINT REACH
^C
SIGINT REACH
^C
SIGINT REACH
^C
SIGINT REACH
^C
SIGINT REACH
被杀掉
“使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理,为了防止信号中断线程,可以吧信号加到每个线程的信号屏蔽字中,然后安排专用的线程作信号处理。”
问题三:如果sigwait所等待的信号,注册了信号处理函数,那么信号处理函数被执行,还是sigwait被唤醒,还是两者都发生?
答:“如果信号被捕获,而且线程正在sigwait调用中等待同一信号,那么将由操作系统实现来决定以何种方式递送信号。操作系统实现可以让sigwait返回,也可以激活信号处理程序,但是不可能出现两者皆可的情况”。我在CentOS5 和 Solaris10 上测试的结果是,sigwait被唤醒,信号处理函数不会被执行,见代码:
-
#include <signal.h>
-
#include <time.h>
-
#include <stdlib.h>
-
#include <stdio.h>
-
#include <unistd.h>
-
-
typedef void Sigfunc(int);
-
-
sigset_t mask;
-
-
/* Reliable version of signal(), using POSIX sigaction(). */
-
Sigfunc *
-
signal(int signo, Sigfunc *func)
-
{
-
struct sigaction act, oact;
-
-
act.sa_handler = func;
-
sigemptyset(&act.sa_mask);
-
act.sa_flags = 0;
-
if (signo == SIGALRM) {
-
#ifdef SA_INTERRUPT
-
act.sa_flags |= SA_INTERRUPT;
-
#endif
-
} else {
-
#ifdef SA_RESTART
-
act.sa_flags |= SA_RESTART;
-
#endif
-
}
-
if (sigaction(signo, &act, &oact) < 0)
-
return(SIG_ERR);
-
return(oact.sa_handler);
-
}
-
-
static void sig_int(int signo)
-
{
-
printf("\n\tSIGINT REACH\n");
-
}
-
-
void * thr_fn(void *arg)
-
{
-
int err, signo;
-
for(;;){
-
signo = sigwait(&mask);
-
switch (signo){
-
case SIGINT:
-
printf("\nsigwait wake up\n");
-
break;
-
default:
-
printf("unexpected signal %d\n",signo);
-
exit(1);
-
}
-
}
-
}
-
-
int main(void)
-
{
-
int err;
-
void *tret;
-
pthread_t tid;
-
-
sigset_t oldmask;
-
sigemptyset(&mask);
-
sigaddset(&mask, SIGINT);
-
-
if(signal(SIGINT,sig_int) == SIG_ERR)
-
perror("signal(SIGINT) error");
-
-
if((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
-
perror("SIG_BLOCK error");
-
-
err = pthread_create(&tid, NULL, thr_fn, 0);
-
if(err != 0)
-
perror("Can't create thread");
-
-
err = pthread_join(tid, tret);
-
if (err != 0)
-
perror("Can't join");
-
-
return 0;
-
}
结果如下:
bash-3.2# ./thread_sig
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
sigwait wake up
^C
阅读(554) | 评论(0) | 转发(0) |