线程拥有与信号相关的私有数据——线程信号掩码,这就决定了线程在信号操作时具有以下特性:
(1)每个线程可以先别的线程发送信号。pthread_kill()函数用来完成这一操作。
(2)每个线程可以设置自己的信号阻塞集合。pthread_sigmask()函数用来完成这一操作,其类似于进程的sigprocmask()函数。
(3)每个线程可以设置针对某个信号的处理方式,但同一进程中对某信号的处理方式只能有一个有效,即最后一次处置的处理方式。
(4)如果别的进程向当前进程中发送一个信号,那么由哪个线程处理则是未知的。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <signal.h> int pthread_kill(pthread_t thread, int signo);</span><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:18px;">//返回值:若成功,返回0;否则,返回错误编号</span>
第一个参数thread是要向其传送信号的线程。
第二个参数signo是要传送给线程的信号。如果signo为0,就会进行错误检查而不发送信号。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <signal.h> int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); //返回值:若成功,返回0;否则,返回错误编号</span>
第一个参数how定义如何更改调用线程的信号掩码。合法值包括:
如果set是空指针,则参数how的值没有意义,且不会更改线程的阻塞信号集,因此该调用可用于查询当前受阻塞的信号。
需要注意的是,要阻塞SIGKILL或SIGSTOP信号时不可能。这是由系统强制执行的,而不会导致错误。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <signal.h> int signal(const sigset_t, sigset_t *restrict set, int *restrict signop); //返回值:若成功,返回0;否则,返回错误编号。</span>
第二个参数是函数返回时,signop指向的整数将包含发送信号的数量。
sigwait函数一直阻塞直到*sigmask指定的任何一个信号被挂起为止,然后从挂起信号集中删除那个信号,并解除对它的阻塞。当sigwait返回时,从挂起信号集中删除的信号的个数被存储在signo指定的那个位置中。在返回之前,sigwait将恢复线程的信号屏蔽字。如果信号在sigwait被调用的时候没有被阻塞,那么在线程完成对sigwait的调用之前会出现一个时间窗,在这个时间窗中信号就可以被发送给线程。这里的sigwait是阻塞本进程,去等待指定的信号。等到了就会解除阻塞。
在多线程中处理信号的原则却完全不同,它的基本原则是:将对信号的异步处理,转换成同步处理,也就是说用一个线程专门的来“同步等待”信号的到来,而其它的线程可以完全不被该信号中断/打断(interrupt)。这样就在相当程度上简化了在多线程环境中对信号的处理。而且可以保证其它的线程不受信号的影响。这样我们对信号就可以完全预测,因为它不再是异步的,而是同步的(我们完全知道信号会在哪个线程中的哪个执行点到来而被处理!)。而同步的编程模式总是比异步的编程模式简单。其实多线程相比于多进程的其中一个优点就是:多线程可以将进程中异步的东西转换成同步的来处理。使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后可以安排专用线程处理信号。
信号是向进程发送的软件通知,通知进程有事件发生。引发信号的事件发生时,信号就被生成了。进程根据信号采取行动时,信号就被传递了。信号的寿命就是信号的生成和传递之间的时间间隔。已经生成但还未被传递的信号被称为挂起的信号。在信号生成和信号传递之间可能会有相当长的时间。
挂起就是未决pending(未处理的)
信号类似于处理事务
1。有事情发生了(信号产生)
2。该事情通知了有关部门,他们已登记在一个未处理事件的本子上(信号传递给目标进程,目标进程登记该信号到本进程的task_struct结构)
3。有关部门定期检查未处理事件的本子,发现有事情没处理时,就注销该事件(目标进程会检测是否有信号等待处理,如果有未决信号等待处理且该信号没有被进程阻塞,则在运行相应的信号处理函数前,进程会把信号在未决信号链中占有的结构卸掉)
4。最后有关部门去实施事件的处理。(进程注销信号后,立即执行相应的信号处理函数,执行完毕后,信号的本次发送对进程的影响彻底结束。)
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> int quitflag; sigset_t mask; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t waitloc = PTHREAD_COND_INITIALIZER; void *thr_fn(void *arg) { int err, signo; for ( ; ;) { err = sigwait(&mask, &signo); if (err != 0) { perror("sigwait failed"); exit(EXIT_FAILURE); } switch (signo) { case SIGINT: printf("\ninterrupt\n"); break; case SIGQUIT: pthread_mutex_lock(&lock); quitflag = 1; pthread_mutex_unlock(&lock); pthread_cond_signal(&waitloc); return(0); default: printf("unexpected signal %d\n", signo); exit(1); } } } int main(void) { int err; sigset_t oldmask; pthread_t tid; int res; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0) { perror("SIG_BLOCK error"); exit(EXIT_FAILURE); } err = pthread_create(&tid, NULL, thr_fn, 0); if (err != 0) { perror("can't create thread"); exit(EXIT_FAILURE); } pthread_mutex_lock(&lock); while(quitflag == 0) pthread_cond_wait(&waitloc, &lock); pthread_mutex_unlock(&lock); quitflag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { perror("SIG_SETMASK error"); exit(EXIT_FAILURE); } exit(0); } </span>