Linux进程信号详解(二):信号产生
当前阶段一、通过终端按键产生信号1.1 基本操作CtrlC → SIGINTCtrl\ →SIGQUIT 可以发送终止信号Ctrl Z - SIGSTP 可以发送停止信号将当前前台进程挂起到后台设置所有信号都可以自定义捕捉 1.2 理解OS如何得知键盘有数据1.3 初步理解信号起源信号其实是从纯软件角度模拟硬件中断的行为只不过硬件中断是发给CPU而信号是发送给进程两者都有相似性但是层级不同二、调用系统命令向进程发信号kill命令可以给指定进程发送信号。# 方式1通过信号宏定义发送 kill -信号名 进程PID # 方式2通过信号编号发送推荐更通用 kill -信号编号 进程PID # 示例向PID为213784的进程发送SIGSEGV11号信号 kill -SIGSEGV 213784 kill -11 213784示例先运行一个后台死循环进程// deadloop.cc #include iostream #include unistd.h int main() { while(true) { sleep(1); } }g deadloop.cc -o deadloop ./deadloop # 后台运行 ps aux | grep deadloop # 查看PID假设是213784 kill -SIGSEGV 213784 # 发送段错误信号进程会收到SIGSEGV11号信号默认动作是终止并产生core dump。三、使用函数产生信号3.1 kill() 函数kill命令的底层实现可向任意进程需有对应权限发送任意信号。#include sys/types.h #include signal.h int kill(pid_t pid, int sig);作用给指定进程发送信号。返回值成功返回0失败返回-1。Makefile.PHONY:all all:testSig mykill testSig:testSig.cc g -o $ $^ -stdc11 mykill:mykill.cc g -o $ $^ -stdc11 .PHONY:clean clean: rm -f testSig mykillmykill.cc#include iostream #include sys/types.h #include signal.h #include string // ./mykill signumber pid int main(int argc, char *argv[]) { if (argc ! 3) { std::cout ./mykill signumber pid std::endl; return 1; } int signum std::stoi(argv[1]); pid_t target std::stoi(argv[2]); int n kill(target, signum); if (n 0) { std::cout send signum to target success. std::endl; } return 0; }testSig.cc#include iostream #include unistd.h #include signal.h #include sys/types.h #include unistd.h void handlerSig(int sig) { std::cout 获得了一个信号: sig std::endl; } int main() { signal(SIGINT,handlerSig); int cnt 0; while (true) { std::cout hello world, cnt , pid: getpid() std::endl; sleep(1); } return 0; }3.2 raise() 函数自己给自己发信号底层是调用kill(getpid(), sig)实现。#include signal.h int raise(int sig);作用给当前进程自己发送信号。返回值成功0失败非0。9 号信号无法被自定义捕捉把9号信号跳过去 查看是否还有其他信号无法被自定义捕捉把9号和 19号 信号跳过去 查看是否还有其他信号无法被自定义捕捉没有了 就只有 9号和 19 号无法自定义捕捉3.3 abort() 函数强制让进程异常终止无论是否捕捉 SIGABRT 信号进程最终都会终止捕捉后会先执行自定义逻辑再终止。#include stdlib.h void abort(void);关键特性无返回值总是会成功进程最终一定会异常终止。四、硬件异常产生信号4.1 除零异常 → SIGFPE// divzero.cc #include stdio.h #include signal.h void handler(int sig) { printf(catch a sig : %d\n, sig); } int main() { // signal(SIGFPE, handler); // 取消注释可捕捉 int a 10; a / 0; // 除零错误 while(1); return 0; }默认情况下进程会收到SIGFPE8号信号并终止。如果捕捉了该信号会一直打印“catch a sig : 8” —— 为什么一直打印因为CPU的状态寄存器中除零标志位没有被清除每次从异常处理返回后再次执行除零指令又会触发异常形成循环。4.2 野指针 → SIGSEGV// segv.cc #include stdio.h #include signal.h void handler(int sig) { printf(catch a sig : %d\n, sig); } int main() { // signal(SIGSEGV, handler); int *p NULL; *p 100; // 向NULL地址写入非法内存访问 while(1); return 0; }默认产生段错误SIGSEGV11号信号。捕捉后也会一直打印因为MMU异常状态未清除。核心结论C/C中的除零、野指针等异常在系统层面都是通过信号来通知进程的。4.3 硬件异常与信号的对应关系异常类型典型代码产生的信号信号值默认行为除零错误a / 0;SIGFPE8终止 Core野指针/段错误*p 100; // pnullptrSIGSEGV11终止 Core非法指令执行损坏的二进制代码SIGILL4终止 Core总线错误内存对齐错误SIGBUS7终止 Core浮点异常浮点溢出/下溢SIGFPE8终止 Core断点调试int 3指令SIGTRAP5终止 CoreCore表示会产生 core dump 文件用于调试分析。4.4 核心问题OS 怎么知道硬件异常了前置知识信号全部都是由操作系统发送的 。硬件层面的检测机制关键组件CPU 寄存器与状态组件作用EFLAGS 标志寄存器记录运算结果状态溢出、零标志、符号标志等CR0 控制寄存器控制 CPU 操作模式包含异常使能位CR2 寄存器页故障时保存导致故障的线性地址CR3 寄存器保存页目录表物理地址MMU 使用IDT 中断描述符表存储异常处理程序的入口地址4.5 操作系统如何识别谁犯了错当前进程上下文
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2486875.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!