linux-进程信号的产生
Linux中的进程信号(signal)是一种用于进程间通信或向进程传递异步事件通知的机制。信号是一种软中断,用于通知进程某个事件的发生,如错误、终止请求、计时器到期等。
1. 信号的基本概念
- 信号(Signal):是一种异步通知机制,当内核或其他进程需要通知某个进程发生了某种事件时,会向该进程发送一个信号。进程接收到信号后,可以根据预设的处理方式进行响应。
- 默认处理:每种信号都有默认的处理动作,比如终止、忽略、停止或继续执行。
- 捕捉信号:进程可以通过注册信号处理函数(signal handler)来捕捉信号,从而自定义对信号的响应。
查看信号 kill -l
1~31为普通信号。34~64为实时信号。
实时信号需要立即处理,可以不立即处理的是普通信号。
怎么进行信号处理?a.默认处理 b.忽略处理 c.自定义处理, 都叫做信号捕捉。
#include <iostream>
#include <unistd.h>
int main()
{while(true){std::cout << "I am a process, I am waiting signal!" << std::endl;sleep(1);}
}

个系统调用函数。
signal:用于设置对特定信号的处理方式
signum:要处理的信号编号[只需要知道是数字即可]
handler:函数指针,表示更改信号的处理动作,当收到对应的信号,就回调执行handler方法。
#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber)
{std::cout << "我是: " << getpid() << ", 我获得了⼀个信号: " << signumber <<std::endl;
}
int main()
{std::cout << "我是进程: " << getpid() << std::endl;signal(SIGINT/*2*/, handler);while(true){std::cout << "I am a process, I am waiting signal!" << std::endl;sleep(1);}
}
这里重定义了二号信号的处理方式,二号信号默认处理是终止进程
2. 信号的产生
在 Linux 中,信号(Signal)是进程间通信(IPC)和异常处理的重要机制。信号的产生方式主要包括 硬件事件、软件命令 和 内核机制 触发。
2.1 硬件事件触发信号
当发生硬件异常时,操作系统会向相应的进程发送信号,例如:
非法操作:程序执行非法指令,如除零 (SIGFPE
)、访问非法内存 (SIGSEGV
)。
键盘输入:用户在终端输入 Ctrl+C
或 Ctrl+Z
,分别产生 SIGINT
(中断进程)和 SIGTSTP
(暂停进程)。
硬件事件 | 触发的信号 |
---|---|
除零错误 | SIGFPE |
非法内存访问 | SIGSEGV |
非法指令 | SIGILL |
总线错误 | SIGBUS |
用户按 Ctrl+C | SIGINT |
用户按 Ctrl+Z | SIGTSTP |
进程访问非法地址触发 SIGSEGV
访问 NULL
指针或越界访问内存,会触发 SIGSEGV(段错误)
遇到除0错误
触发八号信号SIGFPE
2.2 系统调用触发信号
发送信号的本质是相进程写信号,通过进程的pid和信号编号修改位图(本质是OS修改内核的数据)。
用户或进程可以使用 命令 或 系统调用 产生信号。
2.2.1 使用 kill
命令
kill
可用于向指定进程发送信号。例如:
kill -SIGTERM 1234 # 向进程 1234 发送 SIGTERM(终止进程)
kill -9 1234 # 等同于 kill -SIGKILL 1234,强制终止进程
kill -STOP 1234 # 暂停进程
kill -CONT 1234 # 继续运行被暂停的进程
其中:
SIGTERM(15):请求终止进程,进程可捕获并决定是否退出(默认 kill 发送的信号)。
SIGKILL(9):强制终止进程,进程无法捕获,立即终止。
SIGSTOP(19):暂停进程,类似 Ctrl+Z,进程无法忽略。
SIGCONT(18):恢复暂停的进程。
2.2.2 使用 kill()
系统调用
在 C 语言中,kill()
可以向指定进程发送信号:
#include <signal.h>
#include <unistd.h>int main() {pid_t pid = 1234; // 目标进程的 PIDkill(pid, SIGTERM); // 发送 SIGTERM 终止进程return 0;
}
//mykill.c
#include <iostream>
#include <sys/types.h>
#include <signal.h>
#include <string>int main(int argc, char *argv[])
{if(argc != 3){std::cout << "./mykill signum pid" << std::endl;return 1;}pid_t target = std::stoi(argv[2]);int signum = std::stoi(argv[1]);int n = kill(target, signum);if(n == 0){std::cout << "Send " << signum << " to " << target << std::endl;}return 0;
}
//testsig.cc
#include <iostream>
#include <signal.h>void handler(int signum)
{std::cout << "i get a signal: " << signum << std::endl;
}
int main()
{signal(SIGINT, handler);while(true){std::cout << "i am a process, pid: " << getpid() << std::endl;sleep(1);}return 0;
}
2.2.3 使用 raise()
让当前进程向自己发送信号
#include <iostream>
#include <signal.h>void handler(int signum)
{std::cout << "i get a signal: " << signum << std::endl;
}
int main()
{signal(SIGINT, handler);//捕捉信号for(int i = 1; i < 32; i++)signal(i, handler);for(int i = 1; i < 32; i++){sleep(1);raise(i);}return 0;
}
信号 9 也就是 SIGKILL
信号,它是一个强制终止信号,并且不可被捕获、阻塞或忽略的,也就无法对其进行修改和自定义。
2.2.4 abort
abort用于异常终止进程,并生成核心转储(core dump),以便调试程序崩溃的原因。
#include <stdlib.h>void abort(void);无参数,直接终止当前进程
不会返回,进程立即结束
默认产生 SIGABRT 信号,导致进程终止并生成 core dump(如果系统允许)
abort()
与 exit()
的区别
abort() | exit() | |
---|---|---|
终止方式 | 发送 SIGABRT ,可能生成 core dump | 正常终止 |
释放资源 | 不执行 atexit() 注册的函数 | 执行 atexit() 注册的清理函数 |
可捕获 | 可通过 signal(SIGABRT, handler) 处理 | 不发送信号 |
适用场景 | 程序遇到致命错误时终止 | 正常退出,返回状态码 |
捕获SIGABRT
#include <iostream>
#include <signal.h>void handler(int signum)
{std::cout << "i get a signal: " << signum << std::endl;
}
int main()
{signal(SIGABRT, handler);printf("before pause\n");abort();printf("after pause\n");return 0;
}
2.3 软件命令触发信号
2.3.1 使用 alarm()
触发 SIGALRM
alarm()
是一个用于设置定时器的系统调用,它会在指定的秒数后向进程发送 SIGALRM
信号,从而触发相应的信号处理函数或终止进程。
函数原型:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);seconds:设置的定时秒数。
返回值:返回上一个 alarm() 调用设置的剩余时间(如果没有,则返回 0)。
alarm(0):取消闹钟
alarm()
只能设置一个定时器,如果在定时器未触发前再次调用 alarm()
,则前一个定时器会被覆盖。
#include <iostream>
#include <signal.h>void handler(int signum)
{std::cout << "i get a signal: " << signum << std::endl;
}
int main()
{signal(SIGINT, handler);alarm(3);int cnt = 0;while(true){std::cout << "i am a process " << cnt++ << " pid: " << getpid() << std::endl;sleep(1);}return 0;
}
2.3.2 pause()
pause 是一个系统调用,它使进程挂起(阻塞),直到接收到信号(且该信号的处理方式不是忽略)。它通常与 signal()
或 sigaction()
结合使用,以等待某个特定信号的到来。
函数原型:
#include <unistd.h> int pause(void);返回值:通常不返回,除非被信号中断,此时返回 -1,并设置 errno 为 EINTR(被信号中断的错误)。
#include <iostream>
#include <signal.h>void handler(int signum)
{std::cout << "i get a signal: " << signum << std::endl;
}
int main()
{signal(SIGALRM, handler);alarm(3);printf("before pause\n");pause();printf("after pause\n");return 0;
}
2.3.3 设置重复闹钟
#include <iostream>
#include <signal.h>
#include <vector>
#include <functional>
#include <unistd.h>using func_t = std::function<void()>;
std::vector<func_t> funcs;void Schel()
{std::cout << "我是进程调度" << std::endl;
}
void MemManger()
{std::cout << "我是周期性的内存管理, 正在检查有没有内存问题" << std::endl;
}
void Fflush()
{std::cout << "我是刷新程序,定期刷新内存数据" << std::endl;
}
void handler(int signum)
{gcount++;std::cout << "###################" << std::endl;for(auto &f : funcs)f();std::cout << "###################" << std::endl;int n = alarm(1);std::cout << gcount << std::endl;
}
int main()
{funcs.push_back(Schel);funcs.push_back(MemManger);funcs.push_back(Fflush);signal(SIGALRM, handler);alarm(1);while(true)pause();return 0;
}
alarm内核数据结构
struct timer_list {struct list_head entry; //将 timer_list 结构体组织成链表unsigned long expires; //表示定时器的超时时间void (*function)(unsigned long); //指向回调函数的指针,当定时器超时后,内核会调用该函数。unsigned long data; //作为 function 回调函数的参数,通常用于传递自定义数据。struct tvec_t_base_s *base;
};
2.4 内核触发信号
Linux 内核在特定情况下会向进程发送信号,例如:
2.4.1 进程终止时,父进程收到 SIGCHLD
子进程终止后,父进程会收到 SIGCHLD
,可用于回收子进程资源:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>void child_handler(int signum) {printf("Child process exited.\n");
}int main() {signal(SIGCHLD, child_handler);if (fork() == 0) {printf("Child process running...\n");sleep(2);exit(0);}pause(); // 等待信号return 0;
}
(2)磁盘 I/O 错误触发 SIGBUS
访问未映射的内存或硬件错误会触发 SIGBUS
。
(3)后台进程写入终端触发 SIGHUP
后台进程尝试写入终端时,可能收到 SIGHUP
,表示挂起(通常用于会话管理)。
2.4 信号的分类
Linux 信号可分为 终止信号、忽略信号、暂停信号 和 核心转储信号。
类别 | 常见信号 |
---|---|
终止信号 | SIGTERM 、SIGKILL 、SIGINT 、SIGHUP |
暂停信号 | SIGSTOP 、SIGTSTP 、SIGCONT |
核心转储信号 | SIGSEGV 、SIGILL 、SIGABRT |
忽略信号 | SIGCHLD 、SIGURG |
SIGTERM (15):终止信号,用于请求进程正常终止,允许进程进行清理工作后退出。
SIGKILL (9):强制杀死进程的信号,无法被捕捉或忽略,立即终止进程。
SIGSTOP:暂停进程的执行,无法被捕捉或忽略。
SIGCONT:使处于暂停状态的进程继续运行。
SIGHUP (1):挂起信号,常用于通知进程重新读取配置文件或重启。
SIGALRM:定时器信号,定时器到期时发出,用于处理超时操作。
man 7 siganl
Core(终止),Term(终止),Cont(继续),Stop(暂停),Ign(忽略)
信号 vs 通信IPC:
(1)信号是用户和OS,IPC是用户之间
(2)信号是OS修改内核数据结构,IPC是写到缓冲区中
3.目标进程
前台进程是指 直接与终端交互 的进程,用户可以通过 键盘输入 来控制它。
后台进程指的是 不直接与终端交互,在后台运行的进程,用户可以继续在终端执行其他操作。
假如有一个可执行程序code:
./code -> 前台进程
./code & -> 后台进程
命令行shell进程是前台进程。
- 后台进程无法从标准输入获取内容,前台可以。但是都可以向标准输出打印内容。
- 前台进程只能有一个,后台进程可以有多个。
在上图中,testcode
进程成为了前台进程,因此当我们在终端输入 ls
命令时,并未在屏幕上看到输出。这是因为此时命令行 Shell 进程已切换到后台,而 testcode
进程本身 并未提供执行 ls
等命令的接口,导致输入的命令无法被正确解析和执行。
在上述代码中,testcode
进程虽然在标准输出上打印内容,但它是 后台进程,而 前台进程仍然是 Shell 进程。因此,当用户在终端输入 ls
等命令时,Shell 进程能够正常接收输入并执行相应命令,输出也会正确显示在终端上。
几个命令:
jobs查看所有的后台任务
fg(frontground)任务号,将特定的进程提到前台
ctrl + z:将进程暂停。前台进不能被暂停,如果对前台进程使用ctrl+z,该进程会被自动提到后台。
bg:让后台进程回复运行
相关文章:

linux-进程信号的产生
Linux中的进程信号(signal)是一种用于进程间通信或向进程传递异步事件通知的机制。信号是一种软中断,用于通知进程某个事件的发生,如错误、终止请求、计时器到期等。 1. 信号的基本概念 - 信号(Signal)&am…...

内容中台重构企业知识管理路径
智能元数据驱动知识治理 现代企业知识管理的核心挑战在于海量非结构化数据的有效治理。通过智能元数据分类引擎,系统可自动识别文档属性并生成多维标签体系,例如将技术手册按产品版本、功能模块、适用场景进行动态标注。这种动态元数据框架不仅支持跨部…...
ubuntu22.04卸载vscode
方法 1:通过 Snap 卸载 VSCode 如果你是通过 Snap 安装的 VSCode(Ubuntu 22.04 默认推荐方式),按照以下步骤卸载: 检查是否通过 Snap 安装: bash snap list | grep code如果输出显示 code,说明…...
AGI大模型(19):下载模型到本地之ModelScope(魔搭社区)
1 安装模块 魔塔社区提供了下载的模块,如下: pip install modelscope -i https://pypi.tuna.tsinghua.edu.cn/simple 2 模型下载 from modelscope import snapshot_download model_dirsnapshot_download(LLM-Research/Meta-Llama-3-8B,cache_dirrD:\…...

基于Spring Boot+Layui构建企业级电子招投标系统实战指南
一、引言:重塑招投标管理新范式 在数字经济浪潮下,传统招投标模式面临效率低、透明度不足、流程冗长等痛点。本文将以Spring Boot技术生态为核心,融合Mybatis持久层框架、Redis高性能缓存及Layui前端解决方案,构建一个覆盖招标代理…...

Kali安装详细图文安装教程(文章内附有镜像文件连接提供下载)
Kali镜像文件百度网盘:通过网盘分享的文件:kali-linux-2024.2-installer-amd64.iso 链接: https://pan.baidu.com/s/1MfCXi9KrFDqfyYPqK5nbKQ?pwdSTOP 提取码: STOP --来自百度网盘超级会员v5的分享 1.下载好镜像文件后,我们打开我们的VMwa…...

2.4GHz无线芯片核心技术解析与典型应用
2.4G芯片作为工作在2.4GHz ISM频段的无线通信集成电路,主要面向短距离数据传输应用。这类芯片具有以下技术特点: 多协议支持 兼容蓝牙、Wi-Fi和ZigBee等主流协议 采用SDR技术实现协议灵活切换 适用于智能家居和物联网设备 低功耗特性 采用休眠唤醒和动态…...
ai agent(智能体)开发 python高级应用4:什么是代理,如何设置squid代理服务器,让crawl4ai 0.6.3 用上代理,获取到数据平权
crawl4ai 0.6.3为啥用代理,什么情况下需要用到代理 在 crawl4ai 中设置代理服务器的好处: 一、设置代理的好处 避免IP封禁 高频请求同一网站时,目标服务器可能封禁真实IP。代理通过轮换IP分散请求,降低封禁风险。 绕过地理限制 …...
技术融资:概念与形式、步骤与案例、挑战与应对、发展趋势
一、技术融资概述 技术融资是指通过外部资金支持技术研发、产品开发或市场扩展的过程。它通常涉及风险投资、天使投资、私募股权、众筹等多种形式。技术融资的核心目标是为技术创新提供资金保障,推动技术从概念到市场的转化。 技术融资的主要形式包括以下几种&…...

Chrome代理IP配置教程常见方式附问题解答
在网络隐私保护和跨境业务场景中,为浏览器配置代理IP已成为刚需。无论是访问地域限制内容、保障数据安全,还是管理多账号业务,掌握Chrome代理配置技巧都至关重要。本文详解三种主流代理设置方式,助你快速实现精准流量管控。 方式一…...
微信小程序 密码框改为text后不可见,需要点击一下
这个问题是做项目的时候碰到的。 密码框常规写法: <view class"inputBox"><view class"input-container"><input type"{{inputType}}" placeholder"请输入密码" data-id"passwordValue" bindin…...
LLM笔记(六)线性代数
公式速查表 1. 向量与矩阵:表示、转换与知识存储的基础 向量表示 (Vectors): 语义的载体 在LLM中,向量 x ∈ R d \mathbf{x}\in\mathbb{R}^d x∈Rd 是信息的基本单元,承载着丰富的语义信息: 词嵌入向量 (Word Embeddings)&am…...

Linux——UDP/TCP协议理论
1. UDP协议 1.1 UDP协议格式 系统内的UDP协议结构体: 注1:UDP协议的报头大小是确定的,为8字节 注2:可以通过报头中,UDP长度将UDP协议的报头和有效载荷分离,有效载荷将存储到接收缓冲区中等待上层解析。 注…...

Go语言爬虫系列教程(一) 爬虫基础入门
Go爬虫基础入门 1. 网络爬虫概念介绍 1.1 什么是网络爬虫 网络爬虫(Web Crawler),又称网页蜘蛛、网络机器人,是一种按照一定规则自动抓取互联网信息的程序或脚本。其核心功能是模拟人类浏览网页的行为,通过发送网络…...

PromptIDE提示词开发工具支持定向优化啦
老粉们都知道,PromptIDE 是一款专门解决 AI 提示词生成和优化的工具,让 AI 真正听懂你在说什么,生成更符合预期的结果! 我们这次更新主要争对提示词优化这一块,推出了不同提示词优化方向,贴近用户需求。 举…...
多返回值(Multiple Return Values)- 《Go语言实战指南》
Go 语言支持函数返回多个值,这一特性在实际开发中非常常见,尤其用于错误处理。 一、函数返回多个值的基本语法 func 函数名(参数列表) (返回值1类型, 返回值2类型, ...) {// 函数体return 值1, 值2, ... } 示例:计算商和余数 func divide(…...

致远OA人事标准模块功能简介【附应用包百度网盘下载地址,官方售价4W】
人事管理应用,围绕岗位配置、招聘管理、员工档案、入转调离、员工自助申报、数据信息管理等人力资源管理关键业务,构建全员可参与的人事工作协同平台,让人事从繁杂琐碎的事务中解脱出来,高质高效工作,让管理层清楚掌握…...

Python-简单网络编程 I
目录 一、UDP 网络程序1. 通信结构图2. Python 代码实现1)服务器端2)客户端 3. 注意 二、TCP 网络程序1. 通信结构图2. Python 代码实现1)服务器端2)客户端 3. 注意 三、文件下载1. PyCharm 程序传参1)图形化界面传参2…...

鸿蒙北向应用开发: deveco5.0 创建开源鸿蒙项目
本地已经安装deveco5.0 使用5.0创建开源鸿蒙项目 文件->新建->新建项目 直接创建空项目,一路默认 next 直接编译项目 直接连接开源鸿蒙5.0开发板编译会提示 compatibleSdkVersion and releaseType of the app do not match the apiVersion and releaseType on the dev…...
数据库故障排查指南:从入门到精通
1. 常见数据库故障类型 1.1 连接故障 数据库连接超时连接池耗尽网络连接中断认证失败1.2 性能故障 查询执行缓慢内存使用过高CPU使用率异常磁盘I/O瓶颈1.3 数据故障 数据不一致数据丢失数据损坏事务失败2. 故障排查流程 2.1 初步诊断 -- 检查数据库状态SHOW STATUS;SHOW PRO…...

国产linux系统(银河麒麟,统信uos)使用 PageOffice自定义Word模版中的数据区域
PageOffice 国产版 :支持信创系统,支持银河麒麟V10和统信UOS,支持X86(intel、兆芯、海光等)、ARM(飞腾、鲲鹏、麒麟等)、龙芯(Mips、LoogArch)芯片架构。 在实际的Wor…...

基于基金净值百分位的交易策略
策略来源:睿思量化小程序 基金净值百分位,是衡量当前基金净值在过去一段时间内的相对位置。以近一年为例,若某基金净值百分位为30%,意味着过去一年中有30%的时间基金净值低于当前值,70%的时间高于当前值。这一指标犹如…...

2025蓝桥杯JAVA编程题练习Day8
1. 路径 题目描述 小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。 小蓝的图由 2021 个结点组成,依次编号 1 至 2021。 对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21࿰…...

通信安全堡垒:profinet转ethernet ip主网关提升冶炼安全与连接
作为钢铁冶炼生产线的安全检查员,我在此提交关于使用profinet转ethernetip网关前后对生产线连接及安全影响的检查报告。 使用profinet转ethernetip网关前的情况: 在未使用profinet转ethernetip网关之前,我们的EtherNet/IP测温仪和流量计与PR…...

DL00219-基于深度学习的水稻病害检测系统含源码
🌾 基于深度学习的水稻病害检测系统 — 智能农业的未来,守护农田的每一寸土地! 🚜 完整系统获取见文末 水稻病害检测,一直是农业领域的一大难题。传统的人工检测不仅耗时耗力,还容易因经验不足导致漏检或误…...
在24GB显存大小的GPU上运行27GB的Pytorch模型
在24GB显存大小的GPU上运行27GB的Pytorch模型 一.背景:显存不足时的破局之道1.1 大模型时代的显存困境1.2 CUDA统一内存的魔法二.性能测试数据深度解读关键发现:三.复现过程3.1 准备自定义分配器3.2 准备测试程序3.3 执行流程3.4 开始测试四.原理深度剖析4.1 统一内存的工作机…...
【数据机构】2. 线性表之“链表”
- 第 97 篇 - Date: 2025 - 05 - 16 Author: 郑龙浩/仟墨 【数据结构 2】 续上一篇 线性表之“顺序表” 文章目录 3 链表(用指针来首位相连)① 基本介绍② 分类 与 变量命名1 )分类:2 )大体介绍不同结构: ③ “单链表” 的实现:*…...

【51单片机中断】
目录 配置流程 1.在IE寄存器中开启总中断通道和需要的某中断通道 2.在TCON寄存器开启所用中断的触发方式 3.使用中断函数完成中断 4.若需要中断嵌套则在IP寄存器中配置 5.若需要使用串口的中断,则配置SCON寄存器 6.代码示例 配置流程 1.在IE寄存器中开启总…...

JavaSE基础语法之方法
方法 一、方法入门 1.方法定义 方法是一种语法结构,它可以把一段代码封装成一个功能,以便重复调用。 2.方法的格式 修饰符 返回值类型 方法名( 形参列表 ){方法体代码(需要执行的功能代码) }示例: public static int sum ( int a ,…...

华为网路设备学习-22(路由器OSPF-LSA及特殊详解)
一、基本概念 OSPF协议的基本概念 OSPF是一种内部网关协议(IGP),主要用于在自治系统(AS)内部使路由器获得远端网络的路由信息。OSPF是一种链路状态路由协议,不直接传递路由表,而是通过交换链路…...