信号是消息的载体, 标志着不同的行为; 是进程间发送异步信息的一种方式, 属于软中断.
信号随时都可能产生, 是异步发送的, 所以进程并不会立即处理信号, 进程会在合适的时间进行统一处理; 所以进程必须有保存信号的能力;
信号是以位图的形式保存在 task_struct 的 pending 中, 当进程接受到信号时, 进程会将信号对应的位置设置为 1 即可;
struct task_struct {
//...
struct signal_struct *signal; // 指向进程信号描述符
struct sighand_struct *sighand; // 指向进程信号处理程序描述符
sigset_t blocked; // 进程阻塞的信号, 信号按照比特位相对应
struct sigpending pending; // 进程的待处理信号
//...
};
在 Linux 中可以使用指令查看信号列表
kill -l

一共 62 个信号, 其中 1~31 号信号为普通信号, 适用于基于时间片的分时操作系统; 34~64 号信号为实时信号, 适用于实时操作系统.
使用指令可以查看普通信号的说明
man 7 signal


#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数:
返回值:

当操作系统从键盘中读取 “ctrl” + “c” 类似的特定的组合键时, 会将其解释为某种信号, 发送给前台进程.
#include
#include
using namespace std;
int main()
{
cout << endl;
while (1) ;
return 0;
}

将 2 号信号注册自定义行动 验证
#include
#include
#include
using namespace std;
void handler(int signal)
{
cout << endl << getpid() << " " << signal << endl;
}
int main()
{
signal(2, handler);
while (1) ;
return 0;
}


#include
#include
int kill(pid_t pid, int sig);
参数:
返回值:


#include
int raise(int sig);
参数:
返回值:


#include
void abort(void);
这种方式在管道就出现了:
管道读写时, 若读端关闭, 写端继续写入时, 那么操作系统将会发送 SIGPIPE 信号终止写端.

#include
unsigned int alarm(unsigned int seconds);
参数:
返回值:

硬件异常是指 硬件在检测到错误条件后, 通知内核, 再由内核发送相应信号至相关进程.
当 CPU 发生除零错误后, CPU 会将内部的标志寄存器设置为 1, 表示出现数据异常; 之后通知操作系统, 由操作系统向进程发送 SIGFPE 信号, 该信号默认动作为终止程序;
但若将 SIGFPE 信号设置自定义动作或忽略, 未处理异常时, 该硬件的异常数据属于进程的上下文; 由于进程依旧运行, 并且状态寄存器依旧为异常状态, 那么操作系统会轮询式的发送异常信号至进程, 直至异常被处理或终止.
#include
#include
#include
#include
using namespace std;
void handler(int signal)
{
cout << getpid() << " " << signal << endl;
sleep(1);
}
int main()
{
signal(8, handler);
int n = 1;
n /= 0;
return 0;
}


指针异常通常有两种: 无当前虚拟地址的映射关系 或 虚拟地址的操作和页表中的权限不匹配;
当 CPU 的 MMU 寄存器转换地址异常时后, CPU 会将异常的虚拟地址保存至 CR2 寄存器中, 状态设置为异常状态; 之后通知操作系统, 由操作系统向进程发送 SIGSEGV 信号, 该信号默认动作为终止程序;
但若将 SIGSEGV 信号设置自定义动作或忽略, 未处理异常时, 该硬件的异常数据属于进程的上下文; 由于进程依旧运行, 并且 CR2 寄存器依旧为异常状态, 那么操作系统会轮询式的发送异常信号至进程, 直至异常被处理或终止.
#include
#include
#include
#include
using namespace std;
void handler(int signal)
{
cout << getpid() << " " << signal << endl;
sleep(1);
}
int main()
{
signal(11, handler);
char* ptr = nullptr;
*ptr = 0;
return 0;
}


core dump(核心转储), 当进程出现异常终止时, 操作系统会将该进程的相关数据保存至当前目录下的一个核心转储文件, 文件名通常为 core 或 core.pid;
核心转储文件的主要目的是为了调试程序, 并且可以直接从出错的地方开始调试, 也叫做事后调试;
当信号的处理动作为 Core 时, 操作系统不仅终止程序, 还会创建核心转储文件; 但由于核心转储文件可能会包含敏感信息, 或文件过大等原因, 此功能通常是关闭的.
例:
ulimit -a

使用指令可以自行设置核心转储文件大小, 单位为 blocks
ulimit -c /*file_size*/
将核心转储文件大小设为 0, 即可关闭核心转储功能
ulimit -c 0
g++ file_name -g // 编译文件时使用选项生成可调试文件
// 运行程序, 生成核心转储文件
gdb file_name // 进入调试模式
core-file corefile_name // 使用核心转储文件, 直接定位至出错的地方
