[TOC]
# 信号捕捉
## 信号处理方式
一个进程收到一个信号的时候,可以用如下方法进行处理:
1)执行系统默认动作
对大多数信号来说,系统默认动作是用来终止该进程。
2)忽略此信号(丢弃)
接收到此信号后没有任何动作。
3)执行自定义信号处理函数(捕获)
用用户定义的信号处理函数处理该信号。
【注意】:SIGKILL 和 SIGSTOP 不能更改信号的处理方式,因为它们向用户提供了一种使进程终止的可靠方法。
内核实现信号捕捉过程:

## signal函数
~~~
#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:
注册信号处理函数(不可用于 SIGKILL、SIGSTOP 信号),即确定收到信号后处理函数的入口地址。此函数不会阻塞。
参数:
signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l" 为字母)进行相应查看。
handler : 取值有 3 种情况:
SIG_IGN:忽略该信号
SIG_DFL:执行系统默认动作
信号处理函数名:自定义信号处理函数,如:func
回调函数的定义如下:
void func(int signo)
{
// signo 为触发的信号,为 signal() 第一个参数的值
}
返回值:
成功:第一次返回 NULL,下一次返回此信号上一次注册的信号处理函数的地址。如果需要使用此返回值,必须在前面先声明此函数指针的类型。
失败:返回 SIG_ERR
~~~
该函数由ANSI定义,由于历史原因在不同版本的Unix和不同版本的Linux中可能有不同的行为。因此应该尽量避免使用它,取而代之使用sigaction函数。
~~~
// 信号处理函数
void signal_handler(int signo)
{
if (signo == SIGINT)
{
printf("recv SIGINT\n");
}
else if (signo == SIGQUIT)
{
printf("recv SIGQUIT\n");
}
}
int main()
{
printf("wait for SIGINT OR SIGQUIT\n");
/* SIGINT: Ctrl+c ; SIGQUIT: Ctrl+\ */
// 信号注册函数
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
while (1); //不让程序结束
return 0;
}
~~~
## sigaction函数
~~~
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:
检查或修改指定信号的设置(或同时执行这两种操作)。
参数:
signum:要操作的信号。
act: 要设置的对信号的新处理方式(传入参数)。
oldact:原来对信号的处理方式(传出参数)。
如果 act 指针非空,则要改变指定信号的处理方式(设置),如果 oldact 指针非空,则系统将此前指定信号的处理方式存入 oldact。
返回值:
成功:0
失败:-1
~~~
**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); //已弃用
};
~~~
1) sa\_handler、sa\_sigaction:信号处理函数指针,和 signal() 里的函数指针用法一样,应根据情况给sa\_sigaction、sa\_handler 两者之一赋值,其取值如下:
a) SIG\_IGN:忽略该信号
b) SIG\_DFL:执行系统默认动作
c) 处理函数名:自定义信号处理函数
2) sa\_mask:信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。
3) sa\_flags:用于指定信号处理的行为,通常设置为0,表使用默认属性。它可以是一下值的“按位或”组合:
Ø SA\_RESTART:使被信号打断的系统调用自动重新发起(已经废弃)
Ø SA\_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
Ø SA\_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
Ø SA\_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
Ø SA\_RESETHAND:信号处理之后重新设置为默认的处理方式。
Ø SA\_SIGINFO:使用 sa\_sigaction 成员而不是 sa\_handler 作为信号处理函数。
**信号处理函数:**
~~~
void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
参数说明:
signum:信号的编号。
info:记录信号发送进程信息的结构体。
context:可以赋给指向 ucontext_t 类型的一个对象的指针,以引用在传递信号时被中断的接收进程或线程的上下文。
~~~
~~~
void myfunc(int sig)
{
printf("hello signal: %d\n", sig);
sleep(5);
printf("wake up .....\n");
}
int main()
{
// 注册信号捕捉函数
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = myfunc;
// 设置临时屏蔽的信号
sigemptyset(&act.sa_mask); // 清空
// ctrl + 反斜杠
sigaddset(&act.sa_mask, SIGQUIT);
sigaction(SIGINT, &act, NULL); //注册信号
while (1);
return 0;
}
~~~
## sigqueue函数
~~~
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
功能:
给指定进程发送信号。
参数:
pid : 进程号。
sig : 信号的编号。
value : 通过信号传递的参数。
union sigval 类型如下:
union sigval
{
int sival_int;
void *sival_ptr;
};
返回值:
成功:0
失败:-1
~~~
向指定进程发送指定信号的同时,携带数据。但如传地址,需注意,不同进程之间虚拟地址空间各自独立,将当前进程地址传递给另一进程没有实际意义。
下面我们做这么一个例子,一个进程在发送信号,一个进程在接收信号的发送。
发送信号示例代码如下:
~~~
/*******************************************************
*功能: 发 SIGINT 信号及信号携带的值给指定的进程
*参数: argv[1]:进程号 argv[2]:待发送的值(默认为100)
*返回值: 0
********************************************************/
int main()
{
if (argc >= 2)
{
pid_t pid, pid_self;
union sigval tmp;
pid = atoi(argv[1]); // 进程号
if (argc >= 3)
{
tmp.sival_int = atoi(argv[2]);
}
else
{
tmp.sival_int = 100;
}
// 给进程 pid,发送 SIGINT 信号,并把 tmp 传递过去
sigqueue(pid, SIGINT, tmp);
pid_self = getpid(); // 进程号
printf("pid = %d, pid_self = %d\n", pid, pid_self);
}
return 0;
}
~~~
接收信号示例代码如下:
~~~
// 信号处理回调函数
void signal_handler(int signum, siginfo_t *info, void *ptr)
{
printf("signum = %d\n", signum); // 信号编号
printf("info->si_pid = %d\n", info->si_pid); // 对方的进程号
printf("info->si_sigval = %d\n", info->si_value.sival_int); // 对方传递过来的信息
}
int main()
{
struct sigaction act, oact;
act.sa_sigaction = signal_handler; //指定信号处理回调函数
sigemptyset(&act.sa_mask); // 阻塞集为空
act.sa_flags = SA_SIGINFO; // 指定调用 signal_handler
// 注册信号 SIGINT
sigaction(SIGINT, &act, &oact);
while (1)
{
printf("pid is %d\n", getpid()); // 进程号
pause(); // 捕获信号,此函数会阻塞
}
return 0;
}
~~~
- c语言
- 基础知识
- 变量和常量
- 宏定义和预处理
- 随机数
- register变量
- errno全局变量
- 静态变量
- 类型
- 数组
- 类型转换
- vs中c4996错误
- 数据类型和长度
- 二进制数,八进制数和十六进制数
- 位域
- typedef定义类型
- 函数和编译
- 函数调用惯例
- 函数进栈和出栈
- 函数
- 编译
- sizeof
- main函数接收参数
- 宏函数
- 目标文件和可执行文件有什么
- 强符号和弱符号
- 什么是链接
- 符号
- 强引用和弱引用
- 字符串处理函数
- sscanf
- 查找子字符串
- 字符串指针
- qt
- MFC
- 指针
- 简介
- 指针详解
- 案例
- 指针数组
- 偏移量
- 间接赋值
- 易错点
- 二级指针
- 结构体指针
- 字节对齐
- 函数指针
- 指针例子
- main接收用户输入
- 内存布局
- 内存分区
- 空间开辟和释放
- 堆空间操作字符串
- 内存处理函数
- 内存分页
- 内存模型
- 栈
- 栈溢出攻击
- 内存泄露
- 大小端存储法
- 寄存器
- 结构体
- 共用体
- 枚举
- 文件操作
- 文件到底是什么
- 文件打开和关闭
- 文件的顺序读写
- 文件的随机读写
- 文件复制
- FILE和缓冲区
- 文件大小
- 插入,删除,更改文件内容
- typeid
- 内部链接和外部链接
- 动态库
- 调试器
- 调试的概念
- vs调试
- 多文件编程
- extern关键字
- 头文件规范
- 标准库以及标准头文件
- 头文件只包含一次
- static
- 多线程
- 简介
- 创建线程threads.h
- 创建线程pthread
- gdb
- 简介
- mac使用gdb
- setjump和longjump
- 零拷贝
- gc
- 调试器原理
- c++
- c++简介
- c++对c的扩展
- ::作用域运算符
- 名字控制
- cpp对c的增强
- const
- 变量定义数组
- 尽量以const替换#define
- 引用
- 内联函数
- 函数默认参数
- 函数占位参数
- 函数重载
- extern "C"
- 类和对象
- 类封装
- 构造和析构
- 深浅拷贝
- explicit关键字
- 动态对象创建
- 静态成员
- 对象模型
- this
- 友元
- 单例
- 继承
- 多态
- 运算符重载
- 赋值重载
- 指针运算符(*,->)重载
- 前置和后置++
- 左移<<运算符重载
- 函数调用符重载
- 总结
- bool重载
- 模板
- 简介
- 普通函数和模板函数调用
- 模板的局限性
- 类模板
- 复数的模板类
- 类模板作为参数
- 类模板继承
- 类模板类内和类外实现
- 类模板和友元函数
- 类模板实现数组
- 类型转换
- 异常
- 异常基本语法
- 异常的接口声明
- 异常的栈解旋
- 异常的多态
- 标准异常库
- 自定义异常
- io
- 流的概念和类库结构
- 标准io流
- 标准输入流
- 标准输出流
- 文件读写
- STL
- 简介
- string容器
- vector容器
- deque容器
- stack容器
- queue容器
- list容器
- set/multiset容器
- map/multimap容器
- pair对组
- 深浅拷贝问题
- 使用时机
- 常用算法
- 函数对象
- 谓词
- 内建函数对象
- 函数对象适配器
- 空间适配器
- 常用遍历算法
- 查找算法
- 排序算法
- 拷贝和替换算法
- 算术生成算法
- 集合算法
- gcc
- GDB
- makefile
- visualstudio
- VisualAssistX
- 各种插件
- utf8编码
- 制作安装项目
- 编译模式
- 内存对齐
- 快捷键
- 自动补全
- 查看c++类内存布局
- FFmpeg
- ffmpeg架构
- 命令的基本格式
- 分解与复用
- 处理原始数据
- 录屏和音
- 滤镜
- 水印
- 音视频的拼接与裁剪
- 视频图片转换
- 直播
- ffplay
- 常见问题
- 多媒体文件处理
- ffmpeg代码结构
- 日志系统
- 处理流数据
- linux
- 系统调用
- 常用IO函数
- 文件操作函数
- 文件描述符复制
- 目录相关操作
- 时间相关函数
- 进程
- valgrind
- 进程通信
- 信号
- 信号产生函数
- 信号集
- 信号捕捉
- SIGCHLD信号
- 不可重入函数和可重入函数
- 进程组
- 会话
- 守护进程
- 线程
- 线程属性
- 互斥锁
- 读写锁
- 条件变量
- 信号量
- 网络
- 分层模型
- 协议格式
- TCP协议
- socket
- socket概念
- 网络字节序
- ip地址转换函数
- sockaddr数据结构
- 网络套接字函数
- socket模型创建流程图
- socket函数
- bind函数
- listen函数
- accept函数
- connect函数
- C/S模型-TCP
- 出错处理封装函数
- 多进程并发服务器
- 多线程并发服务器
- 多路I/O复用服务器
- select
- poll
- epoll
- epoll事件
- epoll例子
- epoll反应堆思想
- udp
- socket IPC(本地套接字domain)
- 其他常用函数
- libevent
- libevent简介