我的上一篇文章研究了一下如何在程序的正文(而不是信号处理函数)中捕获和处理信号。当时用的方案是 sigprocmask()。但那个方法理论上是可能漏掉一些信号的。

真正安全的做法,是使用进程 / 线程间通信手段,在信号处理函数中向外发送信号,然后在程序正文中监听(epoll, select 等等)这些数据。

这其中是需要使用全局变量的,我目前还没有不使用全局变量的方案。

本文地址:https://segmentfault.com/a/1190000009280819

Reference & Related

《UNIX 环境高级编程》
libevent 源码深度剖析
使用 sigprocmask 和 sigpending 在程序正文中捕获和处理信号

基本原理

在设置捕获信号之前(signal()),首先创建一个通信通道。在中断处理函数中,将捕获到的信号数往这个通道内写入。而在程序正文中,则对这个通道进行读取,这样就可以实现在程序正文中捕获信号了。

这其实是参考了 “libevent 源码深度剖析” 里面所说的 libevent 实现 evsignal的方案。大家都知道,在信号处理函数中,我们一般不要调用 printf 等 stdio 库里的函数。原因请参照《UNIX 环境高级编程》中 “信号” 章的 “可重入函数” 小节。

而实现这个功能中最重要的 read / write 函数,是可以在信号处理函数中调用的!这就是本方案的原理基础。

优点

  1. 由于通信通道基本上是 FIFO 的,所以如果信号多次产生,程序正文也可以收到排队发来的数据,避免了错过多个的信号。
  2. 信号处理函数非常非常短,只需要调用一个 write() 并且写入极少的数据即可。
  3. 大量的数据处理放在程序正文中,获取信号的方法很简单,只是 read()。并且每次获取数据的长度是固定的:signum 的类型是 int,获取 sizeof(int) 字节的数据即可。
  4. pipe 可以直接使用 read / write API 操作,可以方便地对接异步 I/O 库。

选择 pipe 的原因

“libevent 源码深度剖析” 中提到 libevent 使用的是 UNIX域socket(AF_UNIX)。这里我不使用这个方案,而用了 pipe,原因如下:

  1. pipe 初始化和创建简单
  2. pipe 的创建是非命名的,生存周期仅在进程内部,也不会出现多个进程使用相同架构,发生命名冲突的问题
  3. AF_UNIX 是命名的,而且协议栈较为复杂,系统开销稍有些大

一般而言 pipe 是用在父子进程间通信用的,甚至在《UNIX 环境高级编程》中还原文提到 “单个进程中的管道几乎没有任何用处” 。我就哈哈大笑啦——在本文的应用场景下,实际上就是 pipe 为数不多的在单个进程之内的使用。

代码实现

我正在自己设计一个基于 epoll 的异步 I/O 库(GitHub 链接),目前已经实现了类似于 libevent 的普通 event 和 evsignal。如果对这个实现感兴趣的话,可以直接到我的工程里看代码。本文内容主要是 epEventSignal.c 文件里的实现。

主要的实际上也就是 epEventSignal_AddToBase() 函数啦。在这个函数的操作流程如下:

  1. 调用 pipe() 创建管道
  2. 设置 nonblock、closeonexec 选项
  3. pipe[0] 赋值到全局变量中
  4. 使用 sigaction() 函数捕获信号。信号处理函数中,将信号值写入 pipe[0]
  5. pipe[1] 注册入 epoll 中,捕获读事件

我在自己的简单测试程序 test_server.c 中捕获了两个信号,分别是 SIGQUIT(忽略,仅输出)和 SIGINT(触发 event loop 安全退出)。读者可以 checkout 出来试试看。

有什么问题,欢迎告诉我~~~~

使用 pipe 在程序正文中捕获和处理信号相关推荐

  1. iOS 中捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时 ...

  2. 从键盘输入两个数作为除数和被除数。要求程序中捕获NumberFormatException 异常和ArithmeticException 异常, 而且无论在哪种情况下,“程序执行结束”这句话都会在控制

    编写应用程序,从键盘输入两个数作为除数和被除数.要求程序中捕获NumberFormatException 异常和ArithmeticException 异常, 而且无论在哪种情况下,"程序执 ...

  3. Python程序运行中出现异常错误的捕获语法——try、except和finally

    文章目录 01. 异常的概念 02. 捕获异常 2.1 简单的捕获异常语法 简单异常捕获演练 -- 要求用户输入整数 2.2 错误类型捕获 异常类型捕获演练 -- 要求用户输入整数 捕获未知错误 2. ...

  4. 编写应用程序,从命令行传入两个整型数作为除数和被除数。要求程序中捕获NumberFormatException 异常和ArithmeticException异常,而且无论在哪种情况下,“总是被执行

    package com.bw.tryCatch;import java.util.Scanner;public class zuoYe1 {// 编写应用程序,从命令行传入两个整型数作为除数和被除数. ...

  5. python捕获所有异常状态_如何在scrapy中捕获并处理各种异常

    前言 使用scrapy进行大型爬取任务的时候(爬取耗时以天为单位),无论主机网速多好,爬完之后总会发现scrapy日志中"item_scraped_count"不等于预先的种子数量 ...

  6. 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常

    毕竟人不是神,谁写的程序都会有bug,有了bug不可怕,可怕的是出错了,你却不知道错误在哪里.所以我们需要将应用程序中抛出的所有异常都记录起来,不然出了错,找问题就能要了你的命.下面我们主要讨论的是如 ...

  7. Identifying Patch Correctness in Test-Based Program Repair--基于测试的程序修复中补丁正确性的识别

    Identifying Patch Correctness in Test-Based Program Repair–基于测试的程序修复中补丁正确性的识别 摘要 近年来,基于测试的程序自动修复引起了广 ...

  8. 小程序开发过程中常见问题[微信小程序、支付宝小程序]

    小程序开发过程中常见问题[微信小程序.支付宝小程序] 正文 一.样式中如何使用background-image呢? background-image支持网络的图片链接或者base64 二.使用自适应单 ...

  9. sigterm信号_详解如何在 docker 容器中捕获信号

    概述 玩过docker的朋友可能都使用过 docker stop 命令来停止正在运行的容器,有些会使用 docker kill 命令强行关闭容器或者把某个信号传递给容器中的进程.这些操作的本质都是通过 ...

最新文章

  1. 关于Visual Studio2019的4996错误警告解决方法
  2. 2015/09/09夜晚js继续学习
  3. socketserver
  4. 求职及学习心情文章收集
  5. ASP.NET跨平台实践:无需安装Mono的Jexus“独立版”
  6. 图嵌入综述 (arxiv 1709.07604) 译文第一、二章
  7. Linux后台进程(和jobs、bg、fg)
  8. Kubernetes v1.19 正式发布!更新 33 项功能
  9. Linux命令解释之du
  10. 【机器学习】输出层的设计
  11. 使用ggplot2画图
  12. TFS与Git结合进行代码管理
  13. TripMode 管理 App 网络数据使用情况
  14. linux 如何开启shell,linux下开启Shell命令
  15. 软件测试必须知道的精华总结
  16. 清理window日志垃圾.bat
  17. js获取浏览器的宽度和高度
  18. python机器学习分析影响房价的主要因素
  19. CS231A:Vanishing Points and Lines
  20. 荣誉加持,驭势科技近期斩获奖项回顾

热门文章

  1. BILIBILI 高并发实时弹幕系统的实战之路 | 架构师实践日
  2. jsoncpp的输出顺序
  3. libevent中的基本数据结构
  4. HDU2018 母牛的故事
  5. linux03:系统常用的命令
  6. iOS数据存储简要笔记
  7. Python之路,day3-Python基础
  8. UNIX网络编程——客户/服务器程序设计示范(一)
  9. Windows Phone 7 Image Controller: Zoom In, Zoom Out, and Rotate (WP 7 图像控制器:放大,缩小,旋转)...
  10. GPass:GNOME 暗码治理器