Linux kernel signal原理(下)- aarch64架构sigreturn流程

news2025/4/23 19:46:03

一、前言

       在上篇中写到了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-littleaarch64

DYNAMIC 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; //X1
                        regs->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;
        else
                sigtramp = 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函数的上下文,并继续执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2340994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

matlab论文图一的地形区域图的球形展示Version_1

matlab论文图一的地形区域图的球形展示Version_1 图片 此图来源于&#xff1a; ![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…

Flask API 项目 Swagger 版本打架不兼容

Flask API 项目 Swagger 版本打架不兼容 1. 问题背景 在使用 Flask 3.0.0 时遇到以下问题&#xff1a; 安装 flask_restful_swagger 时&#xff0c;它强制将 Flask 降级到 1.1.4&#xff0c;并导致其他依赖&#xff08;如 flask-sqlalchemy、flask-apispec&#xff09;出现版…

基于YOLOv11 和 ByteTrack 实现目标跟踪

介 绍 之前我们介绍了使用YOLOv9与 ByteTrack 结合进行对象跟踪的概念&#xff0c;展示了这两种强大的技术如何有效地协同工作。现在&#xff0c;让我们通过探索与 ByteTrack 结合的 YOLOv11 来进一步了解这一概念。 实战 | 基于YOLOv9和OpenCV实现车辆跟踪计数&#xff08;步骤…

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制作 项目背景 比较老的工程&#xff0c;尤其是keil嵌入式的工程&#xff0c;其文本文件&#xff08;.c、.cpp、.h、.txt、……&#xff09;编码为gb2312&#xff0c;这为移植维护等带来了不便。现在uit-8用…

[密码学实战]密评考试训练系统v1.0程序及密评参考题库(获取路径在文末)

[密码学实战]密评考试训练系统v1.0程序及密评参考题库 引言:密评考试的重要性与挑战 商用密码应用安全性评估(简称"密评") 作为我国密码领域的重要认证体系,已成为信息安全从业者的必备技能。根据国家密码管理局最新数据,截至2024年6月,全国仅有3000余人持有…

蓝桥杯常考的找规律题

目录 灵感来源&#xff1a; B站视频链接&#xff1a; 找规律题具有什么样的特点&#xff1a; 报数游戏&#xff08;Java组&#xff09;&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路详解&#xff1a; 代码详解&#xff1a; 阶乘求和&#xff08;Java组…

MySQL_MCP_Server_pro接入cherry_studio实现大模型操作数据库

大模型直接与数据库交互&#xff0c;实现基本增删改查操作。首先贴下代码地址&#xff1a; https://github.com/wenb1n-dev/mysql_mcp_server_pro 安装环境&#xff1a;win10 1、下载代码 git clone https://github.com/wenb1n-dev/mysql_mcp_server_pro 2、使用conda创建…

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数据库有个比较很抓瞎的事情&#xff0c;不同的版本搭建的大致流程是一样的&#xff0c;但是在实操细节上会有不同&#xff0c;比如操作的脚本位置和配置项等等&#xff0c;这些会变&#xff0c;所以需要时常积累不同版本的文档 这里有一点要说明&#xff0c;之所以使用…

C++ 的 输入输出流(I/O Streams)

什么是输入输出流 C 的输入输出操作是通过 流&#xff08;stream&#xff09; 机制实现的。 流——就是数据的流动通道&#xff0c;比如&#xff1a; 输入流&#xff1a;从设备&#xff08;如键盘、文件&#xff09;读取数据 → 程序 输出流&#xff1a;程序将数据写入设备&…

电子电器架构 --- DFMEA设计失效模式和后果分析

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…

Apache SeaTunnel:新一代开源、高性能数据集成工具

Apache SeaTunnel 是一款开源、分布式、高性能的数据集成工具&#xff0c;可以通过配置快速搭建数据管道&#xff0c;支持实时海量数据同步。 Apache SeaTunnel 专注于数据集成和数据同步&#xff0c;主要旨在解决数据集成领域的常见问题&#xff1a; 数据源多样性&#xff1a…

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# 的…

从 0 到 1 打通 AI 工作流:Dify+Zapier 实现工具自动化调用实战

一、引言&#xff1a;当 AI 遇到工具孤岛 在企业数字化转型的浪潮中&#xff0c;AI 工具的应用早已从单一的对话交互进阶到复杂的业务流程自动化。但开发者常常面临这样的困境&#xff1a;本地开发的 MCP 工具&#xff08;如 ERP 数据清洗脚本、CRM 工单系统 API&#xff09;如…

Spring Boot中`logging.config`配置项的详解及使用说明

以下是Spring Boot中logging.config配置项的详解及使用说明&#xff1a; 1. logging.config 作用 功能&#xff1a;指定自定义日志配置文件的路径&#xff0c;覆盖Spring Boot默认的日志配置。适用场景&#xff1a;当需要深度定制日志行为&#xff08;如输出格式、文件路径、…

相机模型--CMOS和CCD的区别

1--CMOS和CCD的工作原理 CCD&#xff08;Charge Coupled Device&#xff0c;电荷耦合器件&#xff09;&#xff1a; 1. 图像通过光电效应在感光单元中转化为电荷&#xff1b; 2. 每个像素上的电荷被依次“耦合”并传输到芯片的角落&#xff0c;通过一个或几个模拟输出放大器输…

el-date-picker时间范围 赋值报错问题

问题&#xff1a; 点击时间范围组件右边清除图标&#xff0c;点击近6小时会把设置好的时间赋值给时间范围组件 但是出现报错 原因&#xff1a; 尝试对null值进行属性设置操作&#xff1a;修改一个数组的元素&#xff0c;但此时这个数组是null&#xff0c;而不是预期的数组类型…