← 返回目录

信号

第 94 / 230 卷日期:

的总长度也是有上限

  1. 消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销

信号

信号是linux进程通信的最古老的方式;

信号是软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件;

信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生了哪些系统事件.

1、信号的特点

  1. 简单
  2. 不能携带大量信息
  3. 满足某个特定条件才发送

2、一个完整的信号周期

  1. 信号的产生
  2. 信号在进程种的注册,信号在进程种的注销
  3. 执行信号处理函数

3、信号编号

(1)不存在编号为0的信号

  1. 其中1-31号信号称之为常规信号(也叫普通信号或标准信号)
  2. 34-64称之为实时信号,驱动编程与硬件相关。名字上区别不大。而前32个名字各不相同

(2)不存在编号为0的号,也没有32-33号

(3)比较重要的一些,需要记住的几个信号

  1. SIGINT 当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号,终止进

程

  1. SIGQUIT 用户按下<ctrl+>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号,终

止进程

  1. SIGSEGV 指示进程进行了无效内存访问(段错误), 终止进程并产生core文件
  2. SIGPIPE Broken pipe向一个没有读端的管道写数据,终止进程
  3. SIGCHLD 子进程结束时,父进程会收到这个信号,忽略这个信号

4、信号四要素

(1)编号: man 7 signal 查看文档帮助

(2)名称

(3)事件

  1. Core:终止进程,生成Core文件。(查验死亡原因,用于gdb调试)
  2. Stop:停止(暂停)进程
  3. Cont:继续运行进程

特别强调: 9) SIGKILL 和19) SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞.

5、信号的状态

(1)产生

  1. 当用户按某些终端键时,将产生信号
  2. 硬件异常将产生信号
  3. 软件异常将产生信号
  4. 调用系统函数(如:kill、raise、abort)将发送信号
  5. 运行 kill /killall命令将发送信号

(2)未决状态:没有被处理

(3)递达状态:信号被处理了

6、阻塞信号集和未决信号集

(1)阻塞信号集

将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(处理发生在解除屏蔽后)

(2)未决信号集合

信号产生,未决信号集中描述该信号的位立刻翻转为1,表示信号处于未决状态。当信号被处理对应位翻转回为0。

这一时刻往往非常短暂.

7、信号产生函数

(1)kill函数:

#include <sys/types.h> #include <signal.h>

/**

  • 给指定进程发送指定信号(不一定杀死).
  • @param pid 取值有四种情况:
*pid>0:将信号传送给进程ID为pid的进程.
*pid=0:将信号传送给当前进程所在进程组中的所有进程.
*pid=-1:将信号传送给系统内所有的进程.
*pid<-1:将信号传给指定进程组的所有进程,这个进程组号等于pid的绝对值.
  • @param sig 信号的编号,这里可以填数字编号,也可以填信号的宏定义.

*可以通过命令 kill - l("l" 为字⺟)进行相应查看.

*不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致.

  • @return 成功: 0; 失败: -1.
  • 普通用户基本规则是:发送者实际或有效用户ID == 接收者实际或有效用户ID.

#include <signal.h> /**

  • 给当前进程发送指定信号(自己给自己发),等价于 kill(getpid(), sig).
  • @param sig 信号编号.
  • @return成功: 0; 失败: 非0值.

*/ int raise(int sig);

(3)abort函数

#include <stdlib.h> /**

  • 给自己发送异常终止信号 6) SIGABRT,并产生core文件,等价于kill(getpid(), SIGABRT).

*/ void abort(void);

(4)alarm函数(闹钟)

#include <unistd.h> /**

  • 设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终

止。每个进程都有且只有唯一的一个定时器;

  • 取消定时器alarm(0),返回旧闹钟余下秒数.
  • @param seconds 指定的时间,以秒为单位.
  • @return 返回0或剩余的秒数.
  • /

unsigned int alarm(unsigned int seconds);

(5)setitimer函数(定时器)

#include <sys/time.h> struct itimerval {

struct timerval it_interval; // 闹钟触发周期 struct timerval it_value;// 闹钟触发时间

};

struct timeval { long tv_sec;// 秒

long tv_usec;// 微秒 }

/**

  • 设置定时器(闹钟)。可代替alarm函数。精度微秒us,可以实现周期定时.
  • @param which 指定定时方式:

*(1) 自然定时:ITIMER_REAL → 14)SIGALRM计算自然时间;

  • @param new_value 负责设定timeout时间.
  • @param old_value 存放旧的timeout值,一般指定为NULL.
  • @return 成功: 0; 失败: -1.

*/ int setitimer(int which,const struct itimerval *new_value, struct itimerval

*old_value);

// itimerval.it_value:设定第一次执行function所延迟的秒数 // itimerval.it_interval:设定以后每几秒执行function

8、自定义信号集函数

#include <signal.h>

intsigemptyset(sigset_t*set);//将set集合置空
intsigfillset(sigset_t*set);//将所有信号加入set集合
intsigaddset(sigset_t*set,intsigno);//将signo信号加入到set集合
intsigdelset(sigset_t*set,intsigno);//从set集合中移除signo信号
intsigismember(constsigset_t*set,intsigno);//判断信号是否存在

除sigismember外,其余操作函数中的set均为传出参数。sigset_t类型的本质是位图;

9、阻塞信号集

(1)信号阻塞集也称信号屏蔽集、信号掩码;

(2)信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞;

(3)sigprocmask函数

#include <signal.h> /**

  • 检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,
  • 新的信号阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存.

*

  • @param how 信号阻塞集合的修改方法,有 3 种情况:
*(1)SIG_BLOCK:向信号阻塞集合中添加set信号集,
*新的信号掩码是set和旧信号掩码的并集。相当于mask=mask|set;
*(2)SIG_UNBLOCK:从信号阻塞集合中删除set信号集,
*从当前信号掩码中去除set中的信号。相当于mask=mask&~set;
*(3)SIG_SETMASK:将信号阻塞集合设为set信号集,
*相当于原来信号阻塞集的内容清空,然后按照set中的信号重新设置信号阻塞集。相当于

mask = set.

*

  • @set 要操作的信号集地址,若 set 为 NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到

oldset 中.

  • @oldset 保存原先信号阻塞集地址.
  • @return 成功: 0; 失败: -1,失败时错误代码只可能是 EINVAL,表示参数 how 不合法.

*/

#include <signal.h> /**

  • 读取当前进程的未决信号集.
  • @param set 未决信号集.
  • @return 成功: 0; 失败: -1.

*/ int sigpending(sigset_t *set);

10、信号捕捉

注意: SIGKILL 和 SIGSTOP 不能更改信号的处理方式,因为它们向用户提供了一种使进程终止的可靠方法;

(1)sigaction函数

#include <signal.h> /**

  • 检查或修改指定信号的设置(或同时执行这两种操作).
  • @param signum 要操作的信号.
  • @param act 要设置的对信号的新处理方式(传入参数).
  • @param oldact:原来对信号的处理方式(传出参数).

*

  • 如果 act 指针非空,则要改变指定信号的处理方式(设置),
  • 如果 oldact 指针非空,则系统将此前指定信号的处理方式存入 oldact.

*

  • @return 成功: 0; 失败: -1.

*/ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

(2)sa_handler、sa_sigaction:信号处理函数指针

和 signal() 里的函数指针用法一样,应根据情况给sa_sigaction、sa_handler 两者之一赋值

其取值如下:

  1. SIG_IGN:忽略该信号
  2. SIG_DFL:执行系统默认动作
  3. 处理函数名:自定义信号处理函数

(3)sa_mask:

信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号;

(4)sa_flags:

用于指定信号处理的行为,通常设置为0,表使用默认属性。它可以是一下值的“按位或”组合:

  1. A_RESTART:使被信号打断的系统调用自动重新发起(已经废弃)
  2. SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号
  3. SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为
  1. SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数

11、struct sigaction结构体

struct sigaction { void(*sa_handler)(int); //旧的信号处理函数指针

void(*sa_sigaction)(int, siginfo_t *, void *); //新的信号处理函数指针

sigset_tsa_mask;//信号阻塞集
intsa_flags;//信号处理的方式

void(*sa_restorer)(void); //已弃用 };

12、信号处理函数

/**

  • @param signum 信号的编号.
  • @param info 记录信号发送进程信息的结构体.
  • @param context 可以赋给指向 ucontext_t 类型的一个对象的指针
  • 以引用在传递信号时被中断的接收进程或线程的上下文.

*/ void(*sa_sigaction)(int signum, siginfo_t *info, void *context);

13、不可重入、可重入函数

如果有一个函数不幸被设计成为这样:那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。

这样的函数是不安全的函数,也叫不可重入函数;

(1)不可重入函数:

  1. 函数体内使用了静态的数据结构;
  2. 函数体内调用了malloc() 或者 free() 函数(谨慎使用堆);
  3. 函数体内调用了标准 I/O 函数;

(2)可重入函数:

  1. 所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错;
  2. 在写函数时候尽量使用局部变量(例如寄存器、栈中的变量);
  3. 对于要使用的全局变量要加以保护(如采取关中断、信号量等互斥方法),这样构成的函数就一定是一个可重

入的函数.

14、SIGCHLD信号

  1. 子进程终止时;
  2. 子进程接收到SIGSTOP信号停止时;
  3. 子进程处在停止态,接受到SIGCONT后唤醒时。
全站分卷索引 · 230 卷
  1. 1代码随想录知识星球精华(最强八股文)第五版(计算机基础篇)
  2. 2DNS
  3. 3DNS解析过程
  4. 4递归查询和迭代查询
  5. 5HTTP特性与简述
  6. 6HTTP版本演变
  7. 7HTTP/2
  8. 8HTTP1.1协议的性能问题
  9. 9兼容HTTP1.1
  10. 10二进制帧
  11. 11并发传输
  12. 12服务器主动推送资源
  13. 13总结
  14. 14HTTP/3
  15. 15HTTP/2的缺点
  16. 16QUIC协议的特点
  17. 17HTTP/3协议
  18. 18HTTP缓存
  19. 19协商缓存
  20. 20HTTPS
  21. 21HTTPS的特点
  22. 22非对称加密
  23. 23摘要算法+数字签名
  24. 24数字证书
  25. 25证书信任链
  26. 26HTTPS是怎么建立连接的
  27. 27TCP与UDP头部格式
  28. 28TCP
  29. 29列举TCP选项
  30. 30TCP三次握手
  31. 31为什么需要三次握手?
  32. 32什么是半连接队列
  33. 33什么是SYN攻击
  34. 34为什么挥手需要四次
  35. 35TIME_WAIT 过多有什么危害?
  36. 36TCP的滑动窗口
  37. 374. 程序是如何表示发送方的四个部分的呢?
  38. 386. 接收窗口和发送窗口的大小是相等的吗?
  39. 391. 慢启动
  40. 403. 拥塞发生
  41. 414. 快速恢复
  42. 42IP基础
  43. 43IP地址
  44. 44在浏览器中输入URL并按下回车之后会发生什么
  45. 45TCP 抓包实践
  46. 46TCP连接与断开优化
  47. 47TCP内核参数
  48. 48主动方的优化
  49. 49被动方的优化
  50. 50Linux是如何收发网络包的
  51. 51收发流程
  52. 52面试题
  53. 53TCP/IP四层网络模型
  54. 54五层网络体系结构
  55. 55HTTP响应报文
  56. 56HTTP常见字段
  57. 57什么是TCP粘包问题?如何解决TCP粘包问题?
  58. 58UDP会不会产生粘包问题呢?
  59. 59请求报文
  60. 60时间消耗
  61. 61HTTP缓存有几种
  62. 62HTTP1.0和HTTP1.1的区别?
  63. 63HTPP1.1和HTTP1.0有什么区别
  64. 64HTTP/3
  65. 65HTTPS是如何建立连接的
  66. 66DNS是什么
  67. 67递归查询和迭代查询
  68. 68TCP与UDP的区别
  69. 69TCP和UDP分别在什么场景使用?
  70. 70三次握手的过程
  71. 71Keep-Alive的优缺点
  72. 72CDN的工作流程
  73. 73Session的工作原理
  74. 74TCP重传机制是怎么实现的?
  75. 75什么是SS0
  76. 76HTTP优化方案
  77. 77浏览器地址栏输入URL回车后涉及到的流程
  78. 781.5 浏览器发送HTTP/HTTPS请求到web服务器
  79. 79SYN攻击
  80. 80操作系统
  81. 81操作系统的功能
  82. 82进程和线程
  83. 83线程基础
  84. 84进程调度
  85. 85进程调度方式
  86. 86进程通信
  87. 87消息传递(消息队列)
  88. 88信号量
  89. 89信号
  90. 90线程通信
  91. 91多线程
  92. 92进程通信
  93. 93消息队列
  94. 94信号
  95. 95守护进程
  96. 96互斥与同步
  97. 974. 互斥锁解锁
  98. 983. 处理方法
  99. 99读写锁
  100. 1002. 销毁读写锁
  101. 1014. 写锁定
  102. 1021. 创建条件变量
  103. 1034. 阻塞线程
  104. 104信号量
  105. 1054. V操作
  106. 1062. 读者-写者问题
  107. 107存储系统
  108. 108虚拟内存
  109. 109内存分段
  110. 110内存分页
  111. 111虚拟内存
  112. 112文件系统
  113. 1135. 文件访问
  114. 114目录
  115. 1154. 目录操作
  116. 1163. 目录的实现
  117. 1177. 虚拟文件系统
  118. 1184. 文件系统性能
  119. 119磁盘编址
  120. 120三层抽象
  121. 121外设管理
  122. 122Linux
  123. 123select/poll/epoll的区别和联系
  124. 124Linux 内存管理
  125. 125Linux虚拟内存
  126. 126Linux 信号
  127. 127Linux设计
  128. 1283. 各种IO模型的比较
  129. 129Linux网络
  130. 130Linux中的软链接和硬链接有什么区别?
  131. 131什么是中断和异常
  132. 132信号和信号量有什么区别
  133. 133Linux中如何查看一个进程
  134. 134进程、线程的区别?
  135. 135进程之间的通信方式
  136. 136如何解除死锁
  137. 137分段和分页的区别
  138. 138IO多路复用
  139. 139MySQL表连接
  140. 140过滤条件
  141. 141执行一次update语句,发生了什么
  142. 142用户账号存储
  143. 143用户账号删除
  144. 144索引
  145. 145索引有哪些类别
  146. 1462. 按照存储分类
  147. 1475. 按照字段个数分类
  148. 148索引区分度
  149. 149索引什么时候会失效
  150. 150事务的四大特性ACID
  151. 151并行事务会出现什么问题
  152. 152幻读是如何解决的
  153. 153事务隔离的实现
  154. 154读已提交是怎么实现的
  155. 155锁
  156. 1561、全局锁
  157. 1573、行级锁
  158. 158锁的划分
  159. 1592.、程序员角度
  160. 160Innodb使用表锁还是行锁
  161. 161什么是binlog
  162. 162执行引擎
  163. 163MySQL常见问题
  164. 1642. 事务
  165. 1653. 脏页
  166. 1664. 表结构
  167. 1676. 查询效率
  168. 168MySQL分库分表
  169. 169垂直分表
  170. 170水平分库
  171. 171分库分表的问题
  172. 172数据库排序思路
  173. 173MySQL主从复制
  174. 1745. 主从复制模式
  175. 175面试题
  176. 176执行一条select语句,发生了什么?
  177. 177你对关系范式有哪些了解
  178. 178数据库
  179. 179Redis基础
  180. 180Redis单线程为什么还这么快
  181. 181Redis底层数据结构
  182. 182Redis持久化
  183. 183AOF日志
  184. 184RDB快照
  185. 185Redis功能
  186. 1862. 哨兵模式
  187. 1873. 切片集群
  188. 188Redis哨兵机制是什么
  189. 189什么是集群脑裂
  190. 190过期删除策略有哪些
  191. 191Redis的LRU算法和LFU算法有什么区别
  192. 192什么是缓存击穿
  193. 193如何保证删除缓存操作一定能成功?
  194. 194为什么Redis是单线程
  195. 195Redis底层使用了什么数据结构
  196. 196Redis实现分布式锁
  197. 197缓存中的热点数据和冷数据是什么
  198. 198缓存雪崩、击穿、穿透和解决办法【常问】
  199. 199Redis过期删除策略有哪些
  200. 200关键概念
  201. 201单例模式的应用场景
  202. 202工厂模式
  203. 203实际应用
  204. 204抽象工厂模式
  205. 205概念
  206. 206应用
  207. 207实现步骤
  208. 208主要角色
  209. 209代理模式应用
  210. 210策略模式
  211. 211示例
  212. 212装饰模式
  213. 213主要角色
  214. 21423种设计模式的概念
  215. 215创建型模式
  216. 216行为型模式
  217. 217设计原则
  218. 218迪米特法则
  219. 219你在实际开发中用过哪些设计模式
  220. 220依赖倒置
  221. 221代理模式和适配器模式有什么区别
  222. 222固定长度编码
  223. 223冯诺依曼模型
  224. 224总线
  225. 225输入/输出设备
  226. 226编译系统如何工作
  227. 227处理器解释指令
  228. 228场景题 & 系统设计
  229. 229如何设计一个本地缓存
  230. 230如何设计一个RPC框架