每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样如果一个信号选择忽略某个信号,而其他的线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。
进程中的信号是送到单个线程的,如果信号与硬件故障或者计时器超时有关,该信号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。
sigprocmask的行为在多线程的进程中没有定义,线程必须使用pthread_sigmask。
int pthread_sigmask(int how,const sigset_t * newmask, sigset_t * oldmask);//返回值:若成功则返回0,否则返回错误编号
线程可以通过调用sigwait等待一个或多个信号发生:
int sigwait(const sigset_t * set, int * signop);//返回值:若成功则返回0,否则返回错误编号set参数指出了线程等待的信号集,signop指向的整数将作为返回值,表明发送信号的编号(即SIGINT对应2号)。
sigwait函数将阻塞调用它的线程,直到收到它等待的信号发生了,然后sigwait将其从未决队列中取出(因为被阻塞了,所以肯定是未决了),但是有一点需要注意的是:它从未决队列取出之后,并不影响那个被取出的信号原来被阻塞的状态(即原来的信号屏蔽字不会改变的)。
它所做的工作只有两个:
如果多个线程调用sigwait时,且等待的是同一个信号,这时就会出现线程阻塞。当信号递送的时候,只有一个线程可以从sigwait中返回。如果信号被捕获(例如进程通过使用sigaction建立了一个信号处理程序)而且线程正在sigwait调用中等待同一个信号,那么则时候由操作系统实现来决定以何种方式递送信号。在这种情况下,操作系统实现可以让sigwait返回,也可以激活信号处理程序,但不能出现两者皆可的情况。(下面有代码验证,验证结果是:在Ubuntu 10.04系统下,sigwait的优先级高一些)
要把信号发送到进程,可以调用kill;要把信号发送到线程,可以调用pthread_kill:
#include <signal.h> int pthread_kill(pthread_t thread,int signo);//返回值:若成功则返回0,否则返回错误编号。 线程不存在:ESRCH 信号不合法:EINVAL
示例代码:
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> void sig_thread_func(int sig) { printf("sig_thread_func : sig = %d\n", sig); } void sig_func(int sig) { printf("sig_func : sig = %d\n",sig); } void *func1(void *arg) { signal(SIGUSR1, sig_thread_func); //线程1先运行,设置了signal sigset_t set; sigfillset(&set); sigdelset(&set, SIGUSR1); pthread_sigmask(SIG_SETMASK, &set, NULL);//线程1屏蔽了除了SIGUSR1外的所有信号 printf("pthread 1 run\n"); int i; for(i = 0; i < 7; ++i) { printf("1...\n"); sleep(1); } return 0; } void *func2(void *arg) { printf("pthread 2 run\n"); int i; for(i = 0; i < 7; ++i) { printf("2...\n"); sleep(1); } return 0; } int main() { pthread_t tid1, tid2; pthread_create(&tid1, NULL, func1, NULL); pthread_create(&tid2, NULL, func2, NULL); sleep(1); signal(SIGUSR1, sig_func); //覆盖了线程1设置的signal //向线程1发送SIGUSR1,SIGUSR2 sleep(1); pthread_kill(tid1, SIGUSR1);//调动handler sleep(1); pthread_kill(tid1, SIGUSR2);//屏蔽了,无响应 //向线程2发送SIGUSR1,SIGUSR2 sleep(1); pthread_kill(tid2, SIGUSR1);//调用handler sleep(1); //pthread_kill(tid2, SIGUSR2);//会终止进程,是进程! sigset_t set; sigfillset(&set); sigprocmask(SIG_SETMASK, &set, NULL);//进程屏蔽了所有信号 sleep(1); kill(getpid(), SIGUSR1);//调动handler?其实是线程1响应的 pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }运行结果:
huangcheng@ubuntu:~$ ./a.out pthread 2 run 2... pthread 1 run 1... 2... 1... 2... sig_func : sig = 10 1... 2... 1... sig_func : sig = 10 2... 1... 2... 1... 2... sig_func : sig = 10 1...
#include<stdio.h> #include<pthread.h> #include<signal.h> static void sig_alrm(int signo); static void sig_init(int signo); int main() { sigset_t set; int sig; sigemptyset(&set); sigaddset(&set, SIGALRM); pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信号 signal(SIGALRM, sig_alrm); signal(SIGINT, sig_init); sigwait(&set, &sig);//sigwait只是从未决队列中删除该信号,并不改变信号掩码。也就是,当sigwait函数返回,它监听的信号依旧被阻塞。 switch(sig){ case 14: printf("sigwait, receive signal SIGALRM\n"); /*do the job when catch the sigwait*/ break; default: break; } // sigdelset(&set, SIGALRM); // pthread_sigmask(SIG_SETMASK, &set, NULL); for(;;) {} return 0; } static void sig_alrm(int signo) { printf("after sigwait, catch SIGALRM\n"); fflush(stdout); return ; } static void sig_init(int signo) { printf("catch SIGINT\n"); return ; }执行命令:
第一个终端: huangcheng@ubuntu:~$ ./a.out 第二个终端: huangcheng@ubuntu:~$ kill -2 2682 huangcheng@ubuntu:~$ kill -2 2682 huangcheng@ubuntu:~$ kill -14 2682 huangcheng@ubuntu:~$ kill -14 2682 huangcheng@ubuntu:~$ kill -14 2682 huangcheng@ubuntu:~$ kill -14 2682执行结果:
huangcheng@ubuntu:~$ ./a.out catch SIGINT catch SIGINT sigwait, receive signal SIGALRM结果说明:sigwait不会改变屏蔽字,仅仅是从未决队列中删除其等到的信号。
示例代码3:
#include<stdio.h> #include<pthread.h> #include<signal.h> static void sig_alrm(int signo); static void sig_init(int signo); int main() { sigset_t set; int sig; sigemptyset(&set); sigaddset(&set, SIGALRM); pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信号 signal(SIGALRM, sig_alrm); signal(SIGINT, sig_init); sigwait(&set, &sig);//sigwait只是从未决队列中删除该信号,并不改变信号掩码。也就是,当sigwait函数返回,它监听的信号依旧被阻塞。 switch(sig){ case 14: printf("sigwait, receive signal SIGALRM\n"); /*do the job when catch the sigwait*/ break; default: break; } sigdelset(&set, SIGALRM); pthread_sigmask(SIG_SETMASK, &set, NULL); for(;;) {} return 0; } static void sig_alrm(int signo) { printf("after sigwait, catch SIGALRM\n"); fflush(stdout); return ; } static void sig_init(int signo) { printf("catch SIGINT\n"); return ; }运行命令:
第一个终端: huangcheng@ubuntu:~$ ./a.out 第二个终端: huangcheng@ubuntu:~$ kill -2 2704 huangcheng@ubuntu:~$ kill -2 2704 huangcheng@ubuntu:~$ kill -2 2704 huangcheng@ubuntu:~$ kill -14 2704 huangcheng@ubuntu:~$ kill -14 2704 huangcheng@ubuntu:~$ kill -14 2704 huangcheng@ubuntu:~$ kill -14 2704执行结果:
huangcheng@ubuntu:~$ ./a.out catch SIGINT catch SIGINT catch SIGINT sigwait, receive signal SIGALRM after sigwait, catch SIGALRM after sigwait, catch SIGALRM after sigwait, catch SIGALRM