Linux kernel signal原理(下)- aarch64架构sigreturn流程
一、前言
在上篇中写到了linux中signal的处理流程,在do_signal信号处理的流程最后,会通过sigreturn再次回到线程现场,上篇文章中介绍了在X86_64架构下的实现,本篇中介绍下在aarch64架构下的实现原理。
二、sigaction系统调用
#include <signal.h>
#include <stdio.h>
#include <string.h>void signal_handler(int signum, siginfo_t *siginfo, void *context)
{printf("Received signal %d\n", signum);printf("Send by PID: %d\n", siginfo->si_pid);
}int main()
{struct sigaction act;memset(&act, 0, sizeof(act));act.sa_sigaction = signal_handler;act.sa_flags = SA_SIGINFO;if (sigaction(SIGTERM, &act, NULL) < 0) {perror("sigaction");return 1;}while (1) {printf("perfect\n");sleep(10);}return 0;
}
如上是使用sigaction系统调用做的一个简单的测试。
1、放到环境上,并使用strace跟踪进程的系统调用。strace ./test_siginfo
2、向该进程发送SIGTERM信号

可以看到用户态进程在处理SIGTERM信号之后,通过特殊的rt_sigreturn系统调用到内核之后,又再次返回到用户态执行。具体这个rt_sigreturn从哪里来的,下面分析下。
首先看了下glibc的源码,看下在注册sigaction函数的时候是否会把sigreturn系统调用也注册进去,通过阅读源码发现x86_64是采用这个方式实现的,但是aarch64不是。



有事就找man,通过看了下man 2 sigreturn,看到了如下关键信息:

怀疑aarch64 架构下是通过vdso实现的。
三、sigreturn实现流程
通过查看上述测试进程在/proc下的内存映射,如下所示:
sh-5.0# cat /proc/974770/maps
00400000-00401000 r-xp 00000000 b3:07 255 /data/test_siginfo
00410000-00411000 r--p 00000000 b3:07 255 /data/test_siginfo
00411000-00412000 rw-p 00001000 b3:07 255 /data/test_siginfo
06801000-06822000 rw-p 00000000 00:00 0 [heap]
7f94c28000-7f94d81000 r-xp 00000000 b3:01 424154 /usr/lib/aarch64-linux-gnu/libc-2.31.so
7f94d81000-7f94d90000 ---p 00159000 b3:01 424154 /usr/lib/aarch64-linux-gnu/libc-2.31.so
7f94d90000-7f94d93000 r--p 00158000 b3:01 424154 /usr/lib/aarch64-linux-gnu/libc-2.31.so
7f94d93000-7f94d96000 rw-p 0015b000 b3:01 424154 /usr/lib/aarch64-linux-gnu/libc-2.31.so
7f94d96000-7f94d99000 rw-p 00000000 00:00 0
7f94da7000-7f94dac000 r-xp 00000000 b3:01 42742 /usr/lib64/libpsh_essence.so
7f94dac000-7f94dbb000 ---p 00005000 b3:01 42742 /usr/lib64/libpsh_essence.so
7f94dbb000-7f94dbc000 r--p 00004000 b3:01 42742 /usr/lib64/libpsh_essence.so
7f94dbc000-7f94dbd000 rw-p 00005000 b3:01 42742 /usr/lib64/libpsh_essence.so
7f94dbd000-7f94dde000 r-xp 00000000 b3:01 423760 /usr/lib/aarch64-linux-gnu/ld-2.31.so
7f94de6000-7f94dea000 rw-p 00000000 00:00 0
7f94deb000-7f94ded000 r--p 00000000 00:00 0 [vvar]
7f94ded000-7f94dee000 r-xp 00000000 00:00 0 [vdso]
7f94dee000-7f94def000 r--p 00021000 b3:01 423760 /usr/lib/aarch64-linux-gnu/ld-2.31.so
7f94def000-7f94df1000 rw-p 00022000 b3:01 423760 /usr/lib/aarch64-linux-gnu/ld-2.31.so
7feceb1000-7feced2000 rw-p 00000000 00:00 0 [stack]
其中:
7f94ded000-7f94dee000 r-xp 00000000 00:00 0 [vdso]
可以看到vdso内存大小:0x1000 = 4096,即一个page的大小。
vdso的起始虚拟地址在进程974770是: 7f94ded000,转化为十进制即547958476800,将这段内存dump到文件中:
sh-5.0# dd if=/proc/974770/mem of=/tmp/linus-vdso.so skip=547958476800 ibs=1 count=4096
dd: /proc/974770/mem: cannot skip to specified offset
4096+0 records in
8+0 records out
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.0178463 s, 230 kB/s
由于vdso是一个完整的ELF镜像,可以对其进行符号查找:
sh-5.0# objdump -T /tmp/linus-vdso.so /tmp/linus-vdso.so: file format elf64-littleaarch64DYNAMIC SYMBOL TABLE:
0000000000000000 g DO ABS 0000000000000000 LINUX_2.6.39 LINUX_2.6.39
0000000000000750 g DF .text 0000000000000078 LINUX_2.6.39 __kernel_clock_getres
00000000000007cc g D .text 0000000000000008 LINUX_2.6.39 __kernel_rt_sigreturn
00000000000005a0 g DF .text 00000000000001b0 LINUX_2.6.39 __kernel_gettimeofday
0000000000000300 g DF .text 00000000000002a0 LINUX_2.6.39 __kernel_clock_gettime
从符号表中可以看出,确实是有__kernel_rt_sigreturn的实现。
下面看下内核是如何实现的:
通过阅读内核源码,handle_signal的实现在构建用户态栈帧的时候可以看到如下关键流程:
static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,struct pt_regs *regs)
{struct rt_sigframe_user_layout user;struct rt_sigframe __user *frame;int err = 0;fpsimd_signal_preserve_current_state();if (get_sigframe(&user, ksig, regs))return 1;frame = user.sigframe;__put_user_error(0, &frame->uc.uc_flags, err);__put_user_error(NULL, &frame->uc.uc_link, err);err |= __save_altstack(&frame->uc.uc_stack, regs->sp);err |= setup_sigframe(&user, regs, set);if (err == 0) {setup_return(regs, &ksig->ka, &user, usig); //信号返回关键函数if (ksig->ka.sa.sa_flags & SA_SIGINFO) { //如果注册的时候传入了SA_SIGINFO标记,就会把X1,X2寄存器值传给用户态回调err |= copy_siginfo_to_user(&frame->info, &ksig->info);regs->regs[1] = (unsigned long)&frame->info; //X1regs->regs[2] = (unsigned long)&frame->uc; //X2}}return err;
}
static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,struct rt_sigframe_user_layout *user, int usig)
{__sigrestore_t sigtramp;regs->regs[0] = usig;regs->sp = (unsigned long)user->sigframe;regs->regs[29] = (unsigned long)&user->next_frame->fp;regs->pc = (unsigned long)ka->sa.sa_handler;/** Signal delivery is a (wacky) indirect function call in* userspace, so simulate the same setting of BTYPE as a BLR* <register containing the signal handler entry point>.* Signal delivery to a location in a PROT_BTI guarded page* that is not a function entry point will now trigger a* SIGILL in userspace.** If the signal handler entry point is not in a PROT_BTI* guarded page, this is harmless.*/if (system_supports_bti()) {regs->pstate &= ~PSR_BTYPE_MASK;regs->pstate |= PSR_BTYPE_C;}/* TCO (Tag Check Override) always cleared for signal handlers */regs->pstate &= ~PSR_TCO_BIT;if (ka->sa.sa_flags & SA_RESTORER) //x86_64架构默认实现sigtramp = ka->sa.sa_restorer;elsesigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp); //aarch_64架构实现方式regs->regs[30] = (unsigned long)sigtramp; //将sigreturn系统调用地址保存在X30寄存器中
}
通过以上代码可以很清晰的看出在aarch64架构下的实现,即首先会在VDSO的符号表中找到sigreturn的地址,然后保存在X30寄存器中,X30寄存器保存的是函数的返回地址,即在用户态handler执行完成之后要执行的函数地址。对arm寄存器不熟悉的可以参考之前的文章:
ARM64架构栈帧以及帧指针FP-CSDN博客
整个流程可以归结如下图所示:

1、用户程序注册了处理函数signal_handler来捕获SIGTERM信号。
2、当前正在执行main函数时,若发生中断或异常导致切换到内核态。
3、在中断处理完成后,在返回用户态执行main函数之前,检测到有SIGTERM信号pending。
4、内核决定在返回用户态后,不恢复main函数的上下文继续执行,而是调用signal_handler函数。signal_handler函数和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。
5、signal_handler函数执行完毕后,会自动执行特殊的系统调用sigreturn,再次进入内核态。
6、如果没有新的信号pending,此次返回用户态将会恢复main函数的上下文,并继续执行。
相关文章:
Linux kernel signal原理(下)- aarch64架构sigreturn流程
一、前言 在上篇中写到了linux中signal的处理流程,在do_signal信号处理的流程最后,会通过sigreturn再次回到线程现场,上篇文章中介绍了在X86_64架构下的实现,本篇中介绍下在aarch64架构下的实现原理。 二、sigaction系统调用 #i…...
matlab论文图一的地形区域图的球形展示Version_1
matlab论文图一的地形区域图的球形展示Version_1 图片 此图来源于: ![Jieqiong Zhou, Ziyin Wu, Dineng Zhao, Weibing Guan, Chao Zhu, Burg Flemming, Giant sand waves on the Taiwan Banks, southern Taiwan Strait: Distribution, morphometric relationship…...
发布一个npm包,更新包,删除包
发布一个npm包,更新包,删除包 如何将自己的项目 发布为一个 npm 包,并掌握 更新 和 删除 的操作流程。 🚀 一、发布一个 npm 包的完整流程 ✅ 1. 注册并登录 npm 账号 如果还没有账号,先注册: 官网注册&…...
Redis专题
前言 Redis的各种思想跟机组Cache和操作系统对进程的管理非常类似! 一:看到你的简历上写了你的项目里面用到了redis,为啥用redis? 因为传统的关系型数据库如Mysql,已经不能适用所有的场景,比如秒杀的库存扣减ÿ…...
LeetCode[232]用栈实现队列
思路: 一道很简单的题,就是栈是先进后出,队列是先进先出,用两个栈底相互对着,这样一个队列就产生了,右栈为空的情况,左栈栈底就是队首元素,所以我们需要将左栈全部压入右栈ÿ…...
Flask API 项目 Swagger 版本打架不兼容
Flask API 项目 Swagger 版本打架不兼容 1. 问题背景 在使用 Flask 3.0.0 时遇到以下问题: 安装 flask_restful_swagger 时,它强制将 Flask 降级到 1.1.4,并导致其他依赖(如 flask-sqlalchemy、flask-apispec)出现版…...
基于YOLOv11 和 ByteTrack 实现目标跟踪
介 绍 之前我们介绍了使用YOLOv9与 ByteTrack 结合进行对象跟踪的概念,展示了这两种强大的技术如何有效地协同工作。现在,让我们通过探索与 ByteTrack 结合的 YOLOv11 来进一步了解这一概念。 实战 | 基于YOLOv9和OpenCV实现车辆跟踪计数(步骤…...
Qt Creator 创建 Qt Quick Application一些问题
一、Qt Creator 创建 Qt Quick Application 时无法选择 MSVC 编译器(即使已安装 Qt 5.15.2 和 MSVC2019) 1、打开 Qt Creator 的编译器设置 工具 (Tools) → 选项 (Options) → Kits → 编译器 (Compilers) 检查是否存在 Microsoft Visual C++ Compiler (x86_amd64) 或类似条…...
编码转换器
大批量转换编码 可以将整个工程文件夹从GB18030转为UTF-8 使用Qt C制作 项目背景 比较老的工程,尤其是keil嵌入式的工程,其文本文件(.c、.cpp、.h、.txt、……)编码为gb2312,这为移植维护等带来了不便。现在uit-8用…...
Django 中集成 Apache Kafka 可以实现异步消息处理、数据流式传输
在 Django 中集成 Apache Kafka 可以实现异步消息处理、数据流式传输等功能,以下是详细的集成步骤和示例代码: 1. 安装必要的库 首先,你需要安装 kafka-python 库,它是 Python 中操作 Kafka 的常用库。可以使用以下命令进行安装: pip install kafka-python2. 配置 Kafk…...
Scala 入门指南
Scala 入门指南 目录 简介环境搭建基础语法面向对象编程函数式编程集合模式匹配特质隐式转换并发编程与 Java 互操作最佳实践常见问题 简介 Scala 是一种多范式编程语言,结合了面向对象编程和函数式编程的特性。它运行在 JVM 上,与 Java 完全兼容&am…...
[密码学实战]密评考试训练系统v1.0程序及密评参考题库(获取路径在文末)
[密码学实战]密评考试训练系统v1.0程序及密评参考题库 引言:密评考试的重要性与挑战 商用密码应用安全性评估(简称"密评") 作为我国密码领域的重要认证体系,已成为信息安全从业者的必备技能。根据国家密码管理局最新数据,截至2024年6月,全国仅有3000余人持有…...
【Rust 精进之路之第6篇-流程之舞】控制流:`if/else`, `loop`, `while`, `for` 与模式匹配初窥
系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 作者: 码觉客 发布日期: 2025-04-20 引言:让代码“活”起来——指令的流动 在前面的文章中,我们已经掌握了 Rust 的基础数据类型(标量和复合类型)以及如何通过变量绑定来存储和命名它们。这相当于我们准备好了程序…...
Git ——提交至github,Vercel拉取,更新不了项目的问题解决
首先因为github上有个错误 1 failing check Vercel - No GitHub account was found matching the commit author email address 发现好像是vercel拉取不了项目,vercel登录的邮箱与我此次提交更改的邮箱不匹配,查看Git的user确实如此(之前的…...
原型模式详解及在自动驾驶场景代码示例(c++代码实现)
模式定义 原型模式(Prototype Pattern)是一种创建型设计模式,通过克隆已有对象来创建新对象,避免重复执行昂贵的初始化操作。该模式特别适用于需要高效创建相似对象的场景,是自动驾驶感知系统中处理大量重复数据结构的…...
蓝桥杯常考的找规律题
目录 灵感来源: B站视频链接: 找规律题具有什么样的特点: 报数游戏(Java组): 题目描述: 题目链接: 思路详解: 代码详解: 阶乘求和(Java组…...
MySQL_MCP_Server_pro接入cherry_studio实现大模型操作数据库
大模型直接与数据库交互,实现基本增删改查操作。首先贴下代码地址: https://github.com/wenb1n-dev/mysql_mcp_server_pro 安装环境:win10 1、下载代码 git clone https://github.com/wenb1n-dev/mysql_mcp_server_pro 2、使用conda创建…...
Element UI、Element Plus 里的表单验证的required必填的属性不能动态响应?
一 问题背景 想要实现: 新增/修改对话框中(同一个),修改时“备注”字段非必填,新增时"备注"字段必填 结果发现直接写不生效-初始化一次性 edit: [{ required: true, message: "请输入备注", trigger: "blur" }…...
Spark-Streaming
WordCount案例 添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"…...
transformer 子层连接结构
子层连接结构 目标 了解什么是子层连接结构掌握子层连接结构的实现过程 什么是子层连接结构? 输入到每个子层以及规范化层的过程中, 使用了残差连接(跳跃连接, 从Add&Norm -> Add&Norm), 因此我们把这一部分结构整体叫做子层连接(代表子层及其链接结构), 在每个…...
linux oracle 19c 静默安装
oracle数据库有个比较很抓瞎的事情,不同的版本搭建的大致流程是一样的,但是在实操细节上会有不同,比如操作的脚本位置和配置项等等,这些会变,所以需要时常积累不同版本的文档 这里有一点要说明,之所以使用…...
C++ 的 输入输出流(I/O Streams)
什么是输入输出流 C 的输入输出操作是通过 流(stream) 机制实现的。 流——就是数据的流动通道,比如: 输入流:从设备(如键盘、文件)读取数据 → 程序 输出流:程序将数据写入设备&…...
mybatis-plus开发orm
1、mybatis 使用mybatis-generator自动生成代码 这个也是有系统在使用 2、mybatis-plus开发orm--有的系统在使用 MybatisPlus超详细讲解_mybatis-plus-CSDN博客...
使用Python+OpenCV将多级嵌套文件夹下的视频文件抽帧为JPG图片
使用PythonOpenCV将多级嵌套文件夹下的视频文件抽帧为JPG图片 import os import cv2 import time# 存放视频文件的多层嵌套文件夹路径 videoPath D:\\videos\\ # 保存抽帧的图片的文件夹路径 savePath D:\\images\\if not os.path.exists(savePath):os.mkdir(savePath) vide…...
电子电器架构 --- DFMEA设计失效模式和后果分析
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...
C#抽象类和虚方法的作用是什么?
抽象类 (abstract class): 不能直接实例化,只能被继承。 用来定义一套基础框架和规范,强制子类必须实现某些方法(抽象方法)。 可用来封装一些共通的逻辑,减少代码重复。 虚方法 (virtual): …...
C#获取当前方法的命名空间、类名称、方法名称以及方法的参数信息
C#获取当前方法的命名空间、类名称、方法名称以及方法的参数信息 输出示例模块示例 输出示例 获取信息:WindowsFormsApp1.Form1.button1_Click(System.Object sender,System.EventArgs e) 引发的异常:“System.IndexOutOfRangeException”(位于 WindowsFormsApp1.ex…...
Apache SeaTunnel:新一代开源、高性能数据集成工具
Apache SeaTunnel 是一款开源、分布式、高性能的数据集成工具,可以通过配置快速搭建数据管道,支持实时海量数据同步。 Apache SeaTunnel 专注于数据集成和数据同步,主要旨在解决数据集成领域的常见问题: 数据源多样性:…...
python+selenium+pytest自动化测试chrome driver版本下载
chrome浏览器chromedriver版本下载地址 https://googlechromelabs.github.io/chrome-for-testing/#stable...
3.1 WPF使用MaterialDesign的介绍1
MaterialDesignInXAML Toolkit 是一个流行的开源 C# WPF 控件库,它实现了 Google 的 Material Design 规范,让开发者可以轻松创建现代化的 WPF 应用程序界面 Material Design 是一个流行的设计语言,由 Google 开发,旨在帮助开发者构建美观且一致的 UI 界面。对于使用 C# 的…...
