信号
的总长度也是有上限
- 消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销
信号
信号是linux进程通信的最古老的方式;
信号是软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件;
信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生了哪些系统事件.
1、信号的特点
- 简单
- 不能携带大量信息
- 满足某个特定条件才发送
2、一个完整的信号周期
- 信号的产生
- 信号在进程种的注册,信号在进程种的注销
- 执行信号处理函数
3、信号编号
(1)不存在编号为0的信号
- 其中1-31号信号称之为常规信号(也叫普通信号或标准信号)
- 34-64称之为实时信号,驱动编程与硬件相关。名字上区别不大。而前32个名字各不相同
(2)不存在编号为0的号,也没有32-33号
(3)比较重要的一些,需要记住的几个信号
- SIGINT 当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号,终止进
程
- SIGQUIT 用户按下<ctrl+>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号,终
止进程
- SIGSEGV 指示进程进行了无效内存访问(段错误), 终止进程并产生core文件
- SIGPIPE Broken pipe向一个没有读端的管道写数据,终止进程
- SIGCHLD 子进程结束时,父进程会收到这个信号,忽略这个信号
4、信号四要素
(1)编号: man 7 signal 查看文档帮助
(2)名称
(3)事件
- Core:终止进程,生成Core文件。(查验死亡原因,用于gdb调试)
- Stop:停止(暂停)进程
- Cont:继续运行进程
特别强调: 9) SIGKILL 和19) SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞.
5、信号的状态
(1)产生
- 当用户按某些终端键时,将产生信号
- 硬件异常将产生信号
- 软件异常将产生信号
- 调用系统函数(如:kill、raise、abort)将发送信号
- 运行 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 两者之一赋值
其取值如下:
- SIG_IGN:忽略该信号
- SIG_DFL:执行系统默认动作
- 处理函数名:自定义信号处理函数
(3)sa_mask:
信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号;
(4)sa_flags:
用于指定信号处理的行为,通常设置为0,表使用默认属性。它可以是一下值的“按位或”组合:
- A_RESTART:使被信号打断的系统调用自动重新发起(已经废弃)
- SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号
- SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为
- SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数
11、struct sigaction结构体
struct sigaction { void(*sa_handler)(int); //旧的信号处理函数指针
void(*sa_sigaction)(int, siginfo_t *, void *); //新的信号处理函数指针
| sigset_t | sa_mask; | //信号阻塞集 |
|---|---|---|
| int | sa_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)不可重入函数:
- 函数体内使用了静态的数据结构;
- 函数体内调用了malloc() 或者 free() 函数(谨慎使用堆);
- 函数体内调用了标准 I/O 函数;
(2)可重入函数:
- 所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错;
- 在写函数时候尽量使用局部变量(例如寄存器、栈中的变量);
- 对于要使用的全局变量要加以保护(如采取关中断、信号量等互斥方法),这样构成的函数就一定是一个可重
入的函数.
14、SIGCHLD信号
- 子进程终止时;
- 子进程接收到SIGSTOP信号停止时;
- 子进程处在停止态,接受到SIGCONT后唤醒时。
全站分卷索引 · 230 卷
- 1代码随想录知识星球精华(最强八股文)第五版(计算机基础篇)
- 2DNS
- 3DNS解析过程
- 4递归查询和迭代查询
- 5HTTP特性与简述
- 6HTTP版本演变
- 7HTTP/2
- 8HTTP1.1协议的性能问题
- 9兼容HTTP1.1
- 10二进制帧
- 11并发传输
- 12服务器主动推送资源
- 13总结
- 14HTTP/3
- 15HTTP/2的缺点
- 16QUIC协议的特点
- 17HTTP/3协议
- 18HTTP缓存
- 19协商缓存
- 20HTTPS
- 21HTTPS的特点
- 22非对称加密
- 23摘要算法+数字签名
- 24数字证书
- 25证书信任链
- 26HTTPS是怎么建立连接的
- 27TCP与UDP头部格式
- 28TCP
- 29列举TCP选项
- 30TCP三次握手
- 31为什么需要三次握手?
- 32什么是半连接队列
- 33什么是SYN攻击
- 34为什么挥手需要四次
- 35TIME_WAIT 过多有什么危害?
- 36TCP的滑动窗口
- 374. 程序是如何表示发送方的四个部分的呢?
- 386. 接收窗口和发送窗口的大小是相等的吗?
- 391. 慢启动
- 403. 拥塞发生
- 414. 快速恢复
- 42IP基础
- 43IP地址
- 44在浏览器中输入URL并按下回车之后会发生什么
- 45TCP 抓包实践
- 46TCP连接与断开优化
- 47TCP内核参数
- 48主动方的优化
- 49被动方的优化
- 50Linux是如何收发网络包的
- 51收发流程
- 52面试题
- 53TCP/IP四层网络模型
- 54五层网络体系结构
- 55HTTP响应报文
- 56HTTP常见字段
- 57什么是TCP粘包问题?如何解决TCP粘包问题?
- 58UDP会不会产生粘包问题呢?
- 59请求报文
- 60时间消耗
- 61HTTP缓存有几种
- 62HTTP1.0和HTTP1.1的区别?
- 63HTPP1.1和HTTP1.0有什么区别
- 64HTTP/3
- 65HTTPS是如何建立连接的
- 66DNS是什么
- 67递归查询和迭代查询
- 68TCP与UDP的区别
- 69TCP和UDP分别在什么场景使用?
- 70三次握手的过程
- 71Keep-Alive的优缺点
- 72CDN的工作流程
- 73Session的工作原理
- 74TCP重传机制是怎么实现的?
- 75什么是SS0
- 76HTTP优化方案
- 77浏览器地址栏输入URL回车后涉及到的流程
- 781.5 浏览器发送HTTP/HTTPS请求到web服务器
- 79SYN攻击
- 80操作系统
- 81操作系统的功能
- 82进程和线程
- 83线程基础
- 84进程调度
- 85进程调度方式
- 86进程通信
- 87消息传递(消息队列)
- 88信号量
- 89信号
- 90线程通信
- 91多线程
- 92进程通信
- 93消息队列
- 94信号
- 95守护进程
- 96互斥与同步
- 974. 互斥锁解锁
- 983. 处理方法
- 99读写锁
- 1002. 销毁读写锁
- 1014. 写锁定
- 1021. 创建条件变量
- 1034. 阻塞线程
- 104信号量
- 1054. V操作
- 1062. 读者-写者问题
- 107存储系统
- 108虚拟内存
- 109内存分段
- 110内存分页
- 111虚拟内存
- 112文件系统
- 1135. 文件访问
- 114目录
- 1154. 目录操作
- 1163. 目录的实现
- 1177. 虚拟文件系统
- 1184. 文件系统性能
- 119磁盘编址
- 120三层抽象
- 121外设管理
- 122Linux
- 123select/poll/epoll的区别和联系
- 124Linux 内存管理
- 125Linux虚拟内存
- 126Linux 信号
- 127Linux设计
- 1283. 各种IO模型的比较
- 129Linux网络
- 130Linux中的软链接和硬链接有什么区别?
- 131什么是中断和异常
- 132信号和信号量有什么区别
- 133Linux中如何查看一个进程
- 134进程、线程的区别?
- 135进程之间的通信方式
- 136如何解除死锁
- 137分段和分页的区别
- 138IO多路复用
- 139MySQL表连接
- 140过滤条件
- 141执行一次update语句,发生了什么
- 142用户账号存储
- 143用户账号删除
- 144索引
- 145索引有哪些类别
- 1462. 按照存储分类
- 1475. 按照字段个数分类
- 148索引区分度
- 149索引什么时候会失效
- 150事务的四大特性ACID
- 151并行事务会出现什么问题
- 152幻读是如何解决的
- 153事务隔离的实现
- 154读已提交是怎么实现的
- 155锁
- 1561、全局锁
- 1573、行级锁
- 158锁的划分
- 1592.、程序员角度
- 160Innodb使用表锁还是行锁
- 161什么是binlog
- 162执行引擎
- 163MySQL常见问题
- 1642. 事务
- 1653. 脏页
- 1664. 表结构
- 1676. 查询效率
- 168MySQL分库分表
- 169垂直分表
- 170水平分库
- 171分库分表的问题
- 172数据库排序思路
- 173MySQL主从复制
- 1745. 主从复制模式
- 175面试题
- 176执行一条select语句,发生了什么?
- 177你对关系范式有哪些了解
- 178数据库
- 179Redis基础
- 180Redis单线程为什么还这么快
- 181Redis底层数据结构
- 182Redis持久化
- 183AOF日志
- 184RDB快照
- 185Redis功能
- 1862. 哨兵模式
- 1873. 切片集群
- 188Redis哨兵机制是什么
- 189什么是集群脑裂
- 190过期删除策略有哪些
- 191Redis的LRU算法和LFU算法有什么区别
- 192什么是缓存击穿
- 193如何保证删除缓存操作一定能成功?
- 194为什么Redis是单线程
- 195Redis底层使用了什么数据结构
- 196Redis实现分布式锁
- 197缓存中的热点数据和冷数据是什么
- 198缓存雪崩、击穿、穿透和解决办法【常问】
- 199Redis过期删除策略有哪些
- 200关键概念
- 201单例模式的应用场景
- 202工厂模式
- 203实际应用
- 204抽象工厂模式
- 205概念
- 206应用
- 207实现步骤
- 208主要角色
- 209代理模式应用
- 210策略模式
- 211示例
- 212装饰模式
- 213主要角色
- 21423种设计模式的概念
- 215创建型模式
- 216行为型模式
- 217设计原则
- 218迪米特法则
- 219你在实际开发中用过哪些设计模式
- 220依赖倒置
- 221代理模式和适配器模式有什么区别
- 222固定长度编码
- 223冯诺依曼模型
- 224总线
- 225输入/输出设备
- 226编译系统如何工作
- 227处理器解释指令
- 228场景题 & 系统设计
- 229如何设计一个本地缓存
- 230如何设计一个RPC框架