当前位置: 首页 > news >正文

Linux的进程信号

目录

1、快速认识信号

1.1 信号的概念

1.2 信号的查看

2、信号的产生/发送

2.1 键盘

2.1.1 前台进程与后台进程

2.2 函数

2.3 系统命令

2.4 硬件异常

2.5 软件异常

3、信号的保存

3.1 pending位图

3.2 sigset_t类型

3.3 block位图

4、信号的处理

4.1 默认处理

4.2 自定义捕捉

4.1.1 signal

4.1.2 sigaction

5、操作系统是怎么运行的

5.1 硬件中断

5.2 软中断

5.3 用户态和内核态

6、3个小知识点

6.1 可重入函数

6.2 volatile关键字

6.3 SIGCHLD信号


1、快速认识信号

1.1 信号的概念

  • 信号是发送给进程的。
  • 信号是一个数字,但是可读性不好,就定义成一个宏。
  • 信号的产生/发送接收相对于进程异步的(互不干扰)。发送信号的进程,不关心处理结果,接收进程在接收信号之前,执行自己的程序,可能在执行任何代码时被信号中断。

1.2 信号的查看

注意:

本章只介绍 1-31 的标准信号,不讨论后面的实时信号。

2、信号的产生/发送

2.1 键盘

  • Ctrl+C (终止) 2号信号SIGINT
  • Ctrl+\ (终止) 3号信号SIGQUIT
  • Ctrl+Z (暂停) 20号信号SIGTSTP

注意:这三个只对前台进程有效

2.1.1 前台进程与后台进程
  • 路径/可执行程序 是 前台进程标准输入(键盘)中获取内容只能有一个,因为标准输入的内容要给到一个确定的进程。
  • 路径/可执行程序 & 是 后台进程无法从标准输入(键盘)中获取内容可有多个
  • 前台进程和后台进程,都可以想标准输出(显示器)打印内容。
  • 一个现象,运行前台程序,父进程创建子进程,Ctrl+C父进程终止,父进程退出,子进程被一号进程“领养”,变成后台进程,Ctrl+C无法杀死子进程。
  • jobs查看所有后台进程
  • fg 任务号指定进程提到前台
  • Ctrl+Z暂停前台进程提到后台
  • bg 任务号。让后台进程恢复运行

2.2 函数

  • int kill(pid_t pid, int sig);指定进程 发送指定信号
  • int raise(int sig);对自己 发送指定信号
  • void abort(void);使当前进程 接收到信号而异常终止

2.3 系统命令

  • kill -信号编号 pid对指定进程 发送指定的信号

2.4 硬件异常

  • 如:/0,野指针。由软件引起的硬件错误,发送指定信号。

2.5 软件异常

  • 如:管道文件。读端关闭,写端继续,无意义,写端进程收到SIGPIPE信号而终止。
  • 如:unsigned int alarm(unsigned int seconds);在指定秒数后终止进程

3、信号的保存

  • 流程:信号发送过来,未决位图由0置1,如果阻塞位图为0,再递达,进行对应的信号处理;如果阻塞位图为1,就屏蔽该信号,信号一直处于未决状态

注意:

正在处理某一个信号(pending 1->0,block 0->1),当多次发送该信号,未决位图记录一次,并且因为block位图为1,会自动屏蔽该信号,防止递归式处理。

3.1 pending位图

  • 对于pending位图,可以使用int sigpending(sigset_t *set);,set为输出型参数,获取pending位图信息(信号集)

3.2 sigset_t类型

#include <signal.h> 
int sigemptyset(sigset_t *set); // 信号集全置为0
int sigfillset(sigset_t *set); // 信号集全置为1
int sigaddset(sigset_t *set, int signo); // 指定信号位,置为1
int sigdelset(sigset_t *set, int signo); // 指定信号位,置为0
int sigismember(const sigset_t *set, int signo); // 是否为1
// 指定信号位为1,返回1; 为0,返回0; 失败,返回-1

3.3 block位图

  • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

注意:

  • 9号信号SIGKILL,19号信号SIGSTOP,不能被屏蔽。

4、信号的处理

  • 信号的处理有,默认处理忽略自定义捕捉
  • 流程:主函数因为中断或异常或系统调用等,进入内核态,处理完,从内核态返回用户态时,进行信号处理,如果没有信号,直接返回用户态;如果是默认处理,大多数都是进程终止或暂停;如果是忽略,直接返回用户态;如果是自定义捕捉,回到用户态执行自定义函数,自定义函数返回时执行系统调用sigreturn,返回内核态,如果没有新信号到来,就返回用户态。

注意:

  • 内核态用户态切换,是权限的隔离与保护
  • 屏蔽在抵达前,忽略是在抵达后的处理。

4.1 默认处理

  • man 7 signal,可查看,信号的默认处理
  • 9号信号SIGKILL,一定能够终止进程,19号信号SIGSTOP,一定能够暂停进程,不会被屏蔽和和忽略和自定义捕捉。

注意:

  • Core和Term都是进程终止,但是Core,会产生一个core文件(保存进程在内存中的核心数据,支持debug),云服务器是生产环境,不是开发环境,所以不支持生成core文件。如果想要生成core文件,使用ulimit -c 命令,把core文件的大小从0改成非0。
  • core dump就是标志,是否是被Core终止。

4.2 自定义捕捉

4.1.1 signal
  • sighandler_t signal(int signum, sighandler_t handler);。对指定信号传指定的函数。
  • sighandler_t 是一个函数指针typedef void (*sighandler_t)(int);,参数为int,无返回值。
  • handler = SIG_DFL,就是默认处理;handler = SIG_IDN,就是忽略处理。
  • signal()返回原先的函数指针。

注意:

  • signal()只需执行一次,不用一直执行。
4.1.2 sigaction
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);struct sigaction {void     (*sa_handler)(int); // 和signal的handler一样void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask; // 处理某一个信号时,还可以屏蔽其他信号int        sa_flags;void     (*sa_restorer)(void);};

5、操作系统是怎么运行的

5.1 硬件中断

  • 操作系统不用关注外部设备是否准备好,而是它准备好,会通知操作系统。
  • 在CPU里面有一个时钟源,以固定频率(CPU主频)发送中断(时钟中断),让操作系统周期性的执行任务(如:自动调度)。

5.2 软中断

  • 那软件能不能触发类似硬件中断的逻辑?可以。
  • 32位,系统调用就是使用int 0x80触发硬件中断的逻辑。流程:执行int 0x80(中断向量表中的处理软中断的下标),触发中断,在系统调用表里,根据系统调用号,执行对应的系统调用。
  • 64位,通过 syscall 指令(非中断)实现,性能更高。
  • glibc封装了系统调用号+对应的命令
  • int 0x80,syscall等,叫做陷阱(由 CPU 自动检测);缺页、除零、段错误等,叫做硬件异常(由程序主动触发)。

5.3 用户态和内核态

  • 操作系统只有一份,所用进程共享。
  • 系统调用的执行,都是在进行虚拟地址空间中执行的。
  • 当前身份状态,由CPU里的权限寄存器决定。

6、3个小知识点

6.1 可重入函数

  • 一个函数如果 能被多个执行流(如多线程、信号处理函数)同时调用,且不会因共享资源(如全局变量、静态变量)导致数据冲突或逻辑错误,则称为可重入函数。

6.2 volatile关键字

  • volatile为C语言关键字,作用:一般修饰变量防止被编译器优化(编译器可能认为局部不会发生改变,就不从内存读取,从寄存器读取),每次都要从内存中读取
#include <stdio.h> 
#include <signal.h> volatile int flag = 0;void handler(int sig) 
{ printf("chage flag 0 to 1\n"); flag = 1; 
}int main() 
{ signal(2, handler); while(!flag);printf("process quit normal\n"); return 0; 
}

如果不加volatile,编译器优化后,可能认为flag永远不会变,所以只从寄存器里读,即使Ctrl+C,flag改变了,但是还是从寄存器读取,继续死循环。

6.3 SIGCHLD信号

  • 子进程在退出时,会给父进程发送 SIGCHLD 信号,父进程默认处理(什么都不做,忽略)。
  • 父进程可以自定义 SIGCHLD 信号的处理函数,回收子进程。
  • #include <stdio.h> 
    #include <stdlib.h> 
    #include <signal.h> void handler(int sig) 
    { pid_t id; while( (id = waitpid(-1, NULL, WNOHANG)) > 0){ printf("wait child success: %d\n", id); } printf("child is quit! %d\n", getpid()); 
    }int main() 
    { signal(SIGCHLD, handler); pid_t cid; if((cid = fork()) == 0) // child{ printf("child : %d\n", getpid()); sleep(3); exit(1); } while(1){ printf("father proc is doing some thing!\n"); sleep(1); } return 0; 
    }
  • 在Linux中,如果不需要获取子进程退出时的信息,可以signal(SIGCHLD,SIG_IGN);,子进程退出时,就会自动回收,不会产生僵尸进程。
http://www.xdnf.cn/news/1291609.html

相关文章:

  • ASP.NET 上传文件安全检测方案
  • 设计秒杀系统从哪些方面考虑
  • 微软正式将GPT-5接入Microsoft Copilot Studio(国际版)
  • 【物联网】基于树莓派的物联网开发【26】——树莓派开启串口并配置串口助手Minicom
  • jvm学习笔记之jvm的生命周期和发展历程
  • Ansible 实操笔记:Playbook 与变量管理
  • dubbo应用之门面设计模式
  • 《Python学习之基础语法2:掌握程序流程控制的艺术》
  • 101、【OS】【Nuttx】【周边】文档构建渲染:reStructuredText 格式
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day3
  • C++多态:理解面向对象的“一个接口,多种实现”
  • 《AVL树的原理与C++实现:详解平衡二叉搜索树的高效构建与操作》
  • 旧版MinIO的安装(windows)、Spring Boot 后端集成 MinIO 实现文件存储(超详细,带图文)
  • 使用 6 种方法将文件从 Android 无缝传输到iPad
  • [Linux]学习笔记系列 -- [arm][process]
  • WPF 开发的瑞士军刀:Prism 框架从入门到精通指南
  • C++写文件,open函数的参数in、out、ate、app、trunc等标志分别是什么作用?
  • C++ 面向对象四大特性:面试深度解析
  • 河南萌新联赛2025第五场 - 信息工程大学
  • IDEA创建一个VUE项目
  • C# 微软依赖注入 (Microsoft.Extensions.DependencyInjection) 详解
  • 数据分析项目----幸福感挖掘和预测
  • Python实战教程:PDF文档自动化编辑与图表绘制全攻略
  • PyTorch生成式人工智能——基于Transformer实现文本转语音
  • SeaTunnel MCP Server 入选《中国信通院开源商业产品及企业典型案例集(2025)》
  • 袖珍手持气象仪的用途
  • linux_网络层-ip协议
  • 开源日志log4cplus—调用MultiByteToWideChar提示未定义,CP_UTF8未定义定原因有哪些,如何改进?
  • 【安卓,问题记录】ImageView 在布局顺序上位于 Button 上方,却出现图像内容被 Button 遮挡
  • 北京JAVA基础面试30天打卡09