从信号到线程,然后再到线程中的信号,再然后就是守护进程中关于信号的一些个实例,一堆系统调用,有点迷茫,貌似又有一线线索在串起这些个知识点,嗯,就先把这些知识点写出来,再总结吧。
1、进程的信号屏蔽字:规定了进程当前要阻塞的信号集。
sigprocmask调用就是设置当前进程的信号屏蔽字的,但要注意,该调用对基于线程的信号处理是无效的(后面将提及,pthread_sigmask调用才是针对线程的信号屏蔽字的设置)。
2、如果有信号被阻塞,也就是未决状态的信号,那么可以调用sigpending取得当前所有的未决信号集,多次发生的信号会被认为是发生了一次,并写入sigpending传入的参数指针指向的内存中。注,这时这些信号仍旧是未决状态,sigpending所做的就是查询一下当前有哪些信号被block掉了。
3、有什么办法可以临时解除某些信号的屏蔽,并等待这些信号的发生,在这些信号发生后,调用进程停止等待,继续执行,并恢复原先的信号屏蔽设置呢?有的,就是sigsuspend,该调用临时设置参数指定屏蔽字为当前进程的信号屏蔽字,在捕捉到一个信号或者发生了一个会终止该进程的信号之前,该进程被挂起,如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且将该进程的信号屏蔽字设置为调用sigsuspend之前的值。
下面一个例子:
void sig_usr(int signo) { // do some things about signal } int main(int argc, char *argv[]) { sigset_t global_mask; sigset_t tmp_mask; if (SIG_ERR == signal(SIGUSR1, sig_usr)) { perror("signal error"); exit(1); } // block all signals sigfillset(&global_mask); sigprocmask(SIG_BLOCK, &global_mask, NULL); // unblock SIGUSR1 temporarily sigfillset(&tmp_mask); sigdelset(&tmp_mask, SIGUSR1); sigsuspend(&tmp_mask); printf("after sigsuspend.....\n"); return 0; }上面的例子的意思是,屏蔽所有的信号,仅只处理SIGUSR1信号一次,注意,这里必须为SIGUSR1信号设置信号处理程序,否则,该程序就不会在sigsuspend之后返回了。
sigsuspend一般都是用来做进程同步用的,进程间通过发送信号告诉对方它自己已经准备好了,常常是接收信号的一方要等待对方的信号,所以一般都会使用sigsuspend来等待。但是要注意了,在等待这些信号之前,就必须要先把这些信号屏蔽了,否则就会产生一个时间窗口,在sigsuspend之前就错过了信号了(如果这个信号永远都不再发生,那么这个进程就永远阻塞)。在APUE中有这样的一个例子,就是进程间的同步,在for之前就把信号屏蔽好了,然后再fork,各个子进程然后再sigsuspend,这样就会不错过信号了。
注意,以上所讲述的调用,仅只针对进程,而在线程中则无效的。
4、线程中关于信号的机制(建议看FreeBSD的manpages,Linux的manpages是Posix定义的,太TMD晕了)
(1)每个线程都有自己的信号屏蔽字,新被创建的线程继承创建者线程的信号屏蔽字(副本), 他们须通过pthread_sigmask来设置自己的信号屏蔽字(包括main在内)。
(2)信号处理是进程中所有线程共享的,也就是说,任何一个线程改变了信号的处理方法,都影响其它线程。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去。其它信号则被发送到任意一个线程(在Linux中,就是当前进程的第一个线程-_-|||)。
(3)在线程处理中,与sigsuspend(进程)相似的调用是sigwait,但是它功能就强多了,语义上也有所不同,首先,它第一个参数的意思是要等待信号集(sigsuspend则是把第一个参数信号集临时设为当前进程的信号屏蔽字,意思是相反的),然后,第二个参数是取得当前等待得到的信号(这一点,sigsuspend是做不到的)。
sigwait的机制是:sigwait调用会自动取消传入参数信号集的阻塞状态,直到有新的信号被递送,而在sigwait返回之前,它会把该等待到的信号从未决的信号集中去除,并恢复线程的信号屏蔽字。(APUE中文版关于这部份的翻译是错的,请看英文版相关的描述。)。
如果信号被捕获,而线而又正在用sigwait来等待该信号呢?那么这时就将由操作系统实现来决定以何种方式来递关信号了,要么就递送给sigwait,要么就递送给信号处理程序,但仅只能选择其中一种方式(在开发时,最后还是仅选择一种方式)。而sigsuspend则必须等待信号处理程序返回。
sigwait常用的用法就是,使用一个专门的线程来处理信号,而不像进程那样使用signal调用来设置handler。这是相当有用的用法,进程在一开始就屏蔽所有信号,然后建立一个线程sigwait信号,然后就是while true 和switch了。
下面有一个例子:
void *signal_handler(void *args) { sigset_t mask; int signo; // wait all signals sigfillset(&mask); while(1) { sigwait(&mask, &signo); switch (signo) { case SIGUSR1: // SIGUSR1 handler break; case SIGINT: // SIGINT handler break; ..... default: fprintf(stderr, "unexpected signal %d\n", signo); break; } } return (void *) 0; } int main(int argc, char *argv[]) { pthread_t tid; sigset_t mask; // mask all signals sigfillset(&mask); pthread_sigmask(SIG_BLOCK, &mask, NULL); pthread_create(&tid, NULL, signal_handler, NULL); while (1) { printf("main thread: I am working......\n"); sleep(1); } return 0; }
5、那么线程的同步呢?哦,不应该叫同步,而应该叫通信。其实有另外的几种办法,一个是pthread_cond_broadcast,条件广播,另一种就是利用线程sigwait,向指定线程发送信号pthread_kill。(本文未完,还欠代码未写,-_-||赶着洗碗去了)