每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。

进程中的信号是递送到单个线程的。如果信号与硬件故障或计时器超时相关,该信号就被发送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。

http://www.cnblogs.com/nufangrensheng/p/3515257.html中讨论了进程如何使用sigprocmask来阻止信号发送。sigpromask的行为在多线程的进程中并没有定义,线程必须使用pthread_sigmask

#include <signal.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
返回值:若成功则返回0,否则返回错误编号

pthread_sigmask函数与sigprocmask函数基本相同,除了pthread_sigmask工作在线程中,并且失败时返回错误码,而不像sigprocmask中那样设置errno并返回-1。

线程可以通过调用sigwait等待一个或多个信号发生。

#include <signal.h>
int sigwait(const sigset_t *restrict set, int *restrict signop);
返回值:若成功则返回0,否则返回错误编号

set参数指出了线程等待的信号集,signop指向的整数将作为返回值,表明信号编号。

如果信号集中的某个信号在sigwait调用的时候处于未决状态,那么sigwait将无阻塞地返回,在返回之前,sigwait将从进程中移除那些处于未决状态的信号。为了避免错误动作发生,线程在调用sigwait之前,必须阻塞那些它正在等待的信号。sigwait函数会自动取消信号集的阻塞状态,直到有新的信号被递送。在返回之前,sigwait将恢复线程的信号屏蔽字。如果信号在sigwait调用的时候没有被阻塞,在完成对sigwait调用之前会出现一个时间窗,在这个时间窗口期,某个信号可能在完成sigwait调用之前就被递送了。

使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后安排专用线程作信号处理。这些专用线程可以进行函数调用,不需要担心在信号处理程序中调用哪些函数是安全的,因为这些函数调用来自正常的线程环境,而非传统的信号处理程序,传统信号处理程序通常会中断线程的正常执行。

如果多个线程在sigwait调用时,等待的是同一个信号,这时就会出现线程阻塞。当信号递送的时候,只有一个线程可以从sigwait中返回。如果信号被捕获(例如进程通过使用sigaction建立了一个信号处理函数),而且线程正在sigwait调用中等待同一个信号,那么这时将由操作系统实现来决定以何种方式递送信号。在这种情况下,操作系统实现可以让sigwait返回,也可以激活信号处理程序,但不可能出现两者皆可的情况。

要把信号发送到进程,可以调用kill(见http://www.cnblogs.com/nufangrensheng/p/3514817.html);要把信号发送到线程,可以调用pthread_kill。

#include <signal.h>
int pthread_kill(pthread_t thread, int signo);
返回值:若成功则返回0,否则返回错误编号

可以传一个0值的signo来检查线程是否存在。如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程仍然会杀掉整个进程。

注意闹钟定时器是进程资源,并且所有的线程共享相同的alarm。所以进程中的多个线程不可能互不干扰(或互不合作)地使用闹钟定时器。

实例

回忆程序清单10-16(http://www.cnblogs.com/nufangrensheng/p/3516427.html),等待信号处理程序设置标志,从而表明主程序应该退出。唯一可运行的控制线程就是主线程和信号处理程序,所以阻塞信号足以避免错失标志修改。在线程中,需要使用互斥量来保护标志,如程序清单12-6所示。

程序清单12-6 同步信号处理

#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    wait = 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(&wait);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(&wait, &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);
}

这里并不让信号处理程序中断主控线程,而是由专门的独立控制线程进行信号处理。改动quitflag的值是在互斥量的保护下进行的,这样主控线程不会在调用pthread_cond_signal时错失唤醒调用。在主控线程中使用相同的互斥量来检查标志的值,并且原子地释放互斥量,等待条件的发生。

注意在主线程开始时阻塞SIGINT和SIGQUIT。当创建线程进行信号处理时,新建线程继承了现有的信号屏蔽字。因为sigwait会解除信号的阻塞状态,所以只有一个线程可以用于信号的接收。这使得对主线程进行编码时不必担心来自这些信号的中断。

运行这个程序可以得到与程序10-16类似的输出结果:

interrupt        键入中断字符
interrupt        再次键入中断字符
interrupt        再一次用结束字符终止

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

线程控制之线程和信号相关推荐

  1. [Linux]线程概念_线程控制(线程与进程的区别与联系 | 线程创建 | 线程等待 | 线程终止 | 线程分离 | LWP)

    文章目录 线程概念 进程和线程的关系 线程的优点 线程的缺点 线程控制 Linux线程和接口关系的认识 线程创建 线程ID及进程地址空间布局 线程等待 线程终止 线程终止状态 线程分离 LWP和pth ...

  2. Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...

  3. 线程控制 12 | 线程属性

    本节作为指引性介绍,linux下线程的属性是可以根据实际项目需要,进行设置,之前我们讨论的线程都是采用线程的默认属性,默认属性已经可以解决绝大部分开发遇到的问题.如我们对程序的性能提出更高的要求那么需 ...

  4. 【线程控制:线程休眠】

    package com.test;import java.util.Date;public class ThreadSleep extends Thread {@Overridepublic void ...

  5. 实现多线程、进程、线程、设置和获取线程名称、线程调度、线程控制、线程生命周期、同步代码块、线程安全的类、Lock锁、生产者消费者模式

    实现多线程:

  6. Linux多线程---线程概念和线程控制

    线程概念 什么是线程? CPU视角: 与进程的关系: Linux下线程概念 那么CPU能分辨task_struct是进程和线程吗? 理解页表 为何以多级页表实现? 多级页表是如何实现的? 多级页表的优 ...

  7. Linux_多线程(进程与线程的联系_pthread库_线程创建_线程等待_线程正常终止_线程取消_线程分离_pthread_t与LWP)

    文章目录 1.线程的定义,进程和线程的关系 2.Linux下的线程 Linux原生线程库(pthread库pthread.h) 线程的优点 线程的私有数据 3.线程控制 ①创建线程(pthread_c ...

  8. Linux 多线程(一)线程概念:线程概念、线程与进程、线程间的独有与共享、多线程与多进程、线程控制

    线程概念 线程与进程 线程间的独有与共享 多线程与多进程 线程控制 线程概念 什么是线程 线程是进程中的一条执行流,执行程序中的某部分代码.linux下没有具体实现的线程,只有库函数用pcb来实现的线 ...

  9. 如何有效控制 Go 线程数?

    前阵子,在读者交流群中有人提到 Go 默认设置的最大线程数的问题:如果超过一万个 G (挂载于 M 上)阻塞于系统调用,那么程序就会被挂掉. 这是对的,因为 Go 对运行时创建的线程数量有一个限制,默 ...

  10. linux多线程学习(五)——信号量线程控制

    在上一篇文章中,讲述了线程中互斥锁的使用,达到对共享资源互斥使用.除了使用互斥锁,信号量,也就是操作系统中所提到的PV原语,能达到互斥和同步的效果,这就是今天我们所要讲述的信号量线程控制. PV原语是 ...

最新文章

  1. webview 加载php页面内容,WebView加载优化的方法介绍
  2. 医疗影像专用计算机,FCI医疗专用接口满足数码影像需求 -医疗电子-与非网
  3. [小北De编程手记] : Lesson 05 玩转 xUnit.Net 之 从Assert谈UT框架实践
  4. 超全药理学问答题汇总
  5. C/C++ | 字节对齐
  6. map分组后取前10个_map根据属性排序、取出map前n个
  7. 一款开源免费且快速,高效和安全的跨平台备份程序:Restic使用教程
  8. [gtest][001] A quick introduction to the Google C++ Testing Framework
  9. VI编辑器将//替换为#
  10. Profile Lookup任务详解
  11. 《自然语言处理-基于预训练模型的方法》笔记
  12. 蓝桥杯2020第二场JAVA C真题
  13. 如何平衡工作与生活?真相在此
  14. 尝试CornerNet-Lite进行目标识别并嵌入ROS
  15. 浏览器下载文件不全解决方法
  16. 预处理丨去趋势(Matlab和C++)
  17. 低功耗、高性能智能开发主板MED3568
  18. 浩荡离愁白日斜,吟鞭东指即天涯。落红不是无情物,化作春泥更护花。
  19. 轻松玩转iPhone 3GS(第三版)
  20. uniapp开发微信小程序APPID的获取,微信开发者工具游客模式下,调用 wx.operateWXData 是受限的

热门文章

  1. [HDOJ]1001. Sum Problem
  2. Python+Selenium练习篇之4-利用link text定位元素
  3. 蓝桥杯-标题:六角填数
  4. 让Docker功能更强大的10个开源工具
  5. string.Format字符串格式化说明
  6. Android的NDK开发(2)————利用Android NDK编写一个简单的HelloWorld
  7. 一次library cache pin故障的解决过程
  8. 我的5年Python7年R,述说她们的差异在哪里?
  9. R导出可编辑图到ppt:结合使用ggplot2以及officer
  10. tidyverse —— readr包