一、线程处理信号的特点
二、信号屏蔽字函数(pthread_sigmask)
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
//返回值:成功返回0;失败返回错误编码
- 功能:用来设置或得到当前线程的信号屏蔽字
pthread_sigmask函数与sigprocmask函数的区别:
- sigprocmask只适合单线程的进程使用。如果进程中有多线程,必须使用pthread_sigmask
- pthread_sigmask是工作在线程中的,并且pthread_sigmask函数出错时返回错误编码,而不是设置errno的值
参数:
- how参数:SIG_BOLCK(把参数2指向的信号集添加到线程信号屏蔽字中)、SIG_SETMASK(用参数2指向的信号集替换线程的信号屏蔽字)、SIG_UNBLOCK(从线程信号屏蔽字中移除参数2中所指向的那些信号)
- set参数:是线程用于修改信号屏蔽字的信号集
- oset参数:如果此参数非空,线程之前的信号屏蔽字存储在这个结构体中。如果set参数设置为NULL,oset参数设置为sigset_t结构的地址,来获取当前的信号屏蔽字。这种情况下,how参数被忽略
三、信号等待函数(sigwait)
#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
//返回值:成功返回0;失败返回错误编码
参数:
- set参数:指定了线程等待的信号集
- signop参数:sigwait函数返回时,此参数包含发送信号的数量
- 如果set参数指向的信号集中的某个信号在sigwait调用的时候处于挂起状态:那么sigwait将无阻塞地返回。在返回之前,sigwait将从进程中移除那些处于挂起等待状态的信号。如果具体实现支持排队信号,并且信号的多个实例被挂起,那么sigwait将会移除该信号的一个实例,其他的实例还要继续排队
- 为了避免错误行为发生,线程在调用sigwait之前,必须阻塞那些正在等待的信号。sigwait函数会原子地取消信号集的阻塞状态,直到有新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。如果信号在sigwait被调用的时候没有被阻塞,那么在线程完成对sigwait的调用之前会出现一个时间窗,在这个时间窗中,信号就可以被发送给线程
- 使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中。然后可以安排专业线程处理信号。这些专用线程可以进行函数调用,不需要担心咋信号处理程序中调用哪些函数是安全的,因为这些函数调用来自正常的线程上下文,而非会中断线程正常执行的传统信号处理程序
- 如果多个线程在sigwait的调用中因等待同一个信号而阻塞,那么在信号递送的时候,就只有一个线程可以从sigwait中返回。如果一个信号被捕获(例如进程通过使用sigwait建立了一个信号处理程序),而且一个线程正在sigwait调用中等待同一信号,那么这时将由操作系统实现来决定以何种方式递送信号。操作系统实现可以让sigwait返回,也可以激活信号处理程序,但这两种情况不会同时发生
四、线程信号发送函数(pthread_kill)
#include <signal.h>
int pthread_kill(pthread_t thread,int signo);
//返回值:成功返回0;失败返回错误编号
- 功能:将信号发送给线程
- 与kill函数的区别:kill函数是将信号发送给进程,pthread_kill函数是将信号发送给线程
注意事项:
- 可以传一个0值给signo,来检查线程是否存在
- 如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程也就会杀死整个进程
- 闹钟定时器是进程资源,并且所有的线程共享相同的闹钟。所以,进程中的多个线程不可能互不干扰(或互补合作)地使用闹钟定时器
五、演示案例
#include "apue.h"
#include <pthread.h>
int quitflag; /* set nonzero by thread */
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)
err_exit(err, "sigwait failed");
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;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
err_exit(err, "SIG_BLOCK error");
err = pthread_create(&tid, NULL, thr_fn, 0);
if (err != 0)
err_exit(err, "can't create thread");
pthread_mutex_lock(&lock);
while (quitflag == 0)
pthread_cond_wait(&waitloc, &lock);
pthread_mutex_unlock(&lock);
/* SIGQUIT has been caught and is now blocked; do whatever */
quitflag = 0;
/* reset signal mask which unblocks SIGQUIT */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
exit(0);
}
六、演示案例
- 主线程发送信号SIGUSR1、SIGUSR2给子线程1和子线程2,然后分别处理
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
void* func_thread_one(void* arg);
void* func_thread_two(void* arg);
void fun_sig(int signo);
int main()
{
pthread_t tid1,tid2;
//创建线程1
if(pthread_create(&tid1,NULL,func_thread_one,NULL)!=0){
perror("pthread_create");
exit(EXIT_FAILURE);
}
//创建线程2
if(pthread_create(&tid2,NULL,func_thread_two,NULL)!=0){
perror("pthread_create");
exit(EXIT_FAILURE);
}
printf("Farther thread:i will send SIGUSR1、SIGUSR2 to child thread...\n");
sleep(3);
printf("Farther thread:send SIGUSR1 to %ld thread...\n",tid1);
sleep(2);
if(pthread_kill(tid1,SIGUSR1)!=0){
perror("pthread_kill");
exit(EXIT_FAILURE);
}
printf("Farther thread:send SIGUSR2 to %ld thread...\n",tid1);
sleep(2);
if(pthread_kill(tid1,SIGUSR2)!=0){
perror("pthread_kill");
exit(EXIT_FAILURE);
}
printf("Farther thread:send SIGUSR1 to %ld thread...\n",tid2);
sleep(2);
if(pthread_kill(tid2,SIGUSR1)!=0){
perror("pthread_kill");
exit(EXIT_FAILURE);
}
printf("Farther thread:send SIGUSR2 to %ld thread...\n",tid2);
if(pthread_kill(tid2,SIGUSR2)!=0){
perror("pthread_kill");
exit(EXIT_FAILURE);
}
printf("Farther thread:send SIGKILL to %ld thread...\n",tid1);
sleep(2);
if(pthread_kill(tid1,SIGKILL)!=0){
perror("pthread_kill");
exit(EXIT_FAILURE);
}
if(pthread_join(tid1,NULL)!=0){
perror("pthread_join");
exit(EXIT_FAILURE);
}
if(pthread_join(tid2,NULL)!=0){
perror("pthread_join");
exit(EXIT_FAILURE);
}
exit(0);
}
void* func_thread_one(void* arg)
{
//线程1屏蔽SIGUSR1信号,处理SIGUSR2信号
printf("thread %ld running...\n",pthread_self());
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig,SIGUSR1);
signal(SIGUSR2,fun_sig);
if(pthread_sigmask(SIG_SETMASK,&sig,NULL)!=0){
perror("pthread_mask");
exit(EXIT_FAILURE);
}
sleep(15);
}
void* func_thread_two(void* arg)
{
//线程2屏蔽SIGUSR2信号,处理SIGUSR1信号
printf("thread %ld running...\n",pthread_self());
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig,SIGUSR2);
signal(SIGUSR1,fun_sig);
if(pthread_sigmask(SIG_SETMASK,&sig,NULL)!=0){
perror("pthread_mask");
exit(EXIT_FAILURE);
}
sleep(15);
}
//信号处理函数
void fun_sig(int signo)
{
printf("My tid is %ld,receive signo,signo num is %d\n",pthread_self(),signo);
}