APUE编程:68---线程处理(线程与信号处理:pthread_sigmask、sigwait、pthread_kill)

一、线程处理信号的特点

 

二、信号屏蔽字函数(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);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值