linux ptrace 图文详解(八) gdb跟踪被调试程序的子线程、子进程
目录
一、gdb跟踪被调试程序的fork、pthread_create操作
二、实现原理
三、代码实现
四、总结
(代码:linux 6.3.1,架构:arm64)
One look is worth a thousand words. —— Tess Flanders
相关链接:
linux ptrace 图文详解(一)基础介绍
linux ptrace 图文详解(二) PTRACE_TRACEME 跟踪程序
linux ptrace 图文详解(三) PTRACE_ATTACH 跟踪程序
linux ptrace 图文详解(四) gdb设置软断点
linux ptrace 图文详解(五) gdb设置硬断点、观察点
linux ptrace 图文详解(六) gdb单步调试
linux ptrace 图文详解(七) gdb、strace跟踪系统调用
在开始之前,我们先思考几个问题:
1)gdb调试一个程序,该程序所创建的子进程、子线程,是否同样会被gdb跟踪?
2)若gdb可以跟踪被调试程序的子进程、子线程,这是如何实现的?gdb如何获取到子进程、子线程的信息?
3)gdb一定要跟踪被调试程序的子线程、子进程么?
一、gdb跟踪被调试程序的fork、pthread_create操作
GDB 从 版本 7.0 开始支持用户显式控制是否监控被调试程序创建的子进程和子线程。以下是一些gdb控制调试子进程、子线程的一些常见指令:
1)gdb控制子进程调试
set follow-fork-mode <mode>
mode选项:
- parent(默认):仅调试父进程,子进程继续运行不受监控
- child:切换到调试子进程,父进程继续运行
set detach-on-fork <on/off>
- on:分离非调试的进程(若选择跟踪子进程时,父进程会被分离)
- off:同时调试父子进程(需配合inferior命令切换)
2)gdb控制子线程调试
set scheduler-locking <mode>
mode选项:
- off(默认):所有线程自由运行;
- on:仅当前调试的线程运行,其他线程暂停;
- setp:单步执行时仅当前线程运行(避免其他线程干扰);
使用示例:
# 启动gdb设置调试模式
gdb ./test
(gdb) set follow-fork-mode child # 跟踪子进程
(gdb) set detach-on-fork off # 不分离父进程
(gdb) break child_function # 在子进程函数设断点
(gdb) run# 切换进程
(gdb) info inferiorsNum Description Executable1 process 1234 ./test (父进程)2 process 1235 ./test (子进程)
(gdb) inferior 2 # 切换到子进程
二、实现原理
gdb跟踪被调试程序创建的子进程/子线程的原理如下:
1)被调试程序(父进程)调用fork、pthread_create后,陷入内核的系统调用都是kernel_clone;
2)为 子进程/子线程 创建对应的task_struct对象,并且dup相应的内核对象;
3)子进程/子线程 的task_struct对象,继承父进程的ptrace字段(该字段的用途:控制当前进程的哪些行为需要被gdb接管,该字段通常是由gdb通过ptrace进行设置的);
4)父进程 给 子进程/子线程 的task对象添加一个SIGSTOP信号,目的是让子进程/子线程被调度运行时将自己挂起;
5)父进程通过wake_up_new_task 将 子进程/子线程 添加到就绪队列等待被调度运行;
6)子进程/子线程被调度运行时,返回用户态前夕,检查是否存在pending信号,发现有SIGSTOP,于是将自己挂起,并通知gdb;
7)父进程 将 子进程/子线程 的pid,记录到自己的ptrace_message字段中(后续gdb会来拿这个信息);
8)父进程发现自己的task->ptrace字段包含了 PTRACE_EVENT_CLONE、PTRACE_EVENT_FORK,代表当前任务的fork、pthread_create动作需要通知gdb,于是调用ptrace_stop,通知父进程并将自己挂起;
9)gdb被唤醒后,通过wait的status参数得知被调试程序执行了fork、pthread_create动作;
10)gdb通过ptrace(PTRACE_GETEVENTMSG),陷入内核获取记录在父进程task->ptrace_message中的信息,该信息就是父进程创建的 子进程/子线程 的 pid;
12)gdb 为 被调试程序创建的 子进程/子线程 创建对应的数据结构,记录在gdb中,至此gdb侧就掌握了被调试程序的所有子线程、子进程相关信息;
13)最后,gdb通过ptrace(PTRACE_CONT)将被调试程序(父进程)、及其创建的 子进程/子线程 唤醒继续运行;
14)父进程 以及其创建的 子进程/子线程 被调度继续运行;
三、代码实现
1、gdb为被调试程序设置对应的ptrace字段(告知子进程其哪些动作需要被gdb拦截)
// Case1
linux_process_target::post_create_inferiorlinux_enable_event_reportingsupported_ptrace_options = (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK| PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXEC)options &= supported_ptrace_optionsptrace(PTRACE_SETOPTIONS, pid, ..., options)flags = task->ptraceflags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT)flags |= (options << PT_OPT_FLAG_SHIFT)task->ptrace = flags// Case2
linux_enable_event_reporting (pid_t pid, int options) {if (supported_ptrace_options == -1) {linux_check_ptrace_features() {supported_ptrace_options = (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK| PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXEC)...}}/* We always want clone events. */options |= PTRACE_O_TRACECLONE/* Filter out unsupported options. */options &= supported_ptrace_optionsptrace(PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) (uintptr_t) options) {sys_ptrace(request, pid, addr, data) {ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data) {switch (request)case PTRACE_SETOPTIONS:ptrace_setoptions(child, data) {flags = child->ptraceflags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT)flags |= (data << PT_OPT_FLAG_SHIFT) // 即: flag |= data << 3child->ptrace = flags // <<<<<< 将 PTRACE_O_TRACEFORK 等event, 设置给目标任务task_struct->ptrace字段}}}}
}
2、父进程执行fork/pthread_create,在内核中的kernel_clone实现
SYSCALL_DEFINE0(fork) {kernel_clone(struct kernel_clone_args *args)int trace = 0u64 clone_flags = args->flags/* Determine whether and which event to report to ptracer. */if (!(clone_flags & CLONE_UNTRACED)) {if (clone_flags & CLONE_VFORK)trace = PTRACE_EVENT_VFORKelse if (args->exit_signal != SIGCHLD) // pthread_createtrace = PTRACE_EVENT_CLONEelsetrace = PTRACE_EVENT_FORK/******** 判断current是否包含对应trace event *********/ret = ptrace_event_enabled(struct task_struct *task = current, int event = trace) {return task->ptrace & PT_EVENT_FLAG(event)}if (likely(!ret)) trace = 0}struct task_struct *p = copy_process(NULL, trace, NUMA_NO_NODE, args) {p = dup_task_struct(current, node) // 为新创建的任务生成一个新的task_structptrace_init_task(p, bool ptrace = (clone_flags & CLONE_PTRACE) || trace) // initialize ptrace state for a new childif (unlikely(ptrace) && current->ptrace)child->ptrace = current->ptrace /* Set ptrace event to new child task's ptrace, which will be checked later */__ptrace_link(child, current->parent, current->ptracer_cred)sigaddset(&child->pending.signal, SIGSTOP) // Add SIGSTOP to new task's pending.signal}wake_up_new_task(p) // new task will stop when go back to user space a.k.a do_signalif (unlikely(trace)) { /* forking complete and child started to run, tell ptracer */ptrace_event_pid(event = trace, pid) {message = pid_nr_ns(pid, ns) /* message: new task's pid. 保存在父进程的task_struct->ptrace_message中 */ptrace_event(event, message)if (unlikely(ptrace_event_enabled(current, event)))current->ptrace_message = messageptrace_notify(exit_code = (event << 8) | SIGTRAP) // event: PTRACE_EVENT_FORK / PTRACE_EVENT_CLONE / PTRACE_EVENT_VFORKptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED)ptrace_stop(exit_code, why, 1, &info)}}
}
3、gdb收集被调试程序fork/pthread_create创建的子进程/子线程信息
linux_process_target::wait_for_event_filtered { // 3) *** gdb获取子线程/子进程 pid信息 ***/* Always pull all events out of the kernel */while (event_child == NULL) {ret = my_waitpid(wstatp)if (ret > 0) {linux_process_target::filter_event(int lwpid = ret, int wstat = *wstatp) {ret = linux_is_extended_waitstatus (wstat) // 该函数用于check wait返回后的入参,是否有event事件, 即: PTRACE_EVENT_FORK、PTRACE_EVENT_CLONE 这类事件return (linux_ptrace_get_extended_event (wstat) != 0)return (wstat >> 16)if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP && ret) {linux_process_target::handle_extended_wait {int event = linux_ptrace_get_extended_event (wstat)return (wstat >> 16)if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK) || (event == PTRACE_EVENT_CLONE)) {/* Get the pid of the new lwp. */ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0, &new_pid)*(unsigned long *)data = child->ptrace_messageA.K.A*new_pid = child->ptrace_message // 获取新的task的pid/* If we haven't already seen the new PID stop, wait for it now. */if (!pull_pid_from_list (&stopped_pids, new_pid, &status)) {ret = my_waitpid (new_pid, &status, __WALL)}}if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK) {ptid = ptid_t (new_pid, new_pid)/* Add the new process to the tables and clone the breakpoint lists of the parent. */child_proc = add_linux_process (new_pid, 0)child_lwp = add_lwp (ptid)child_lwp->stopped = 1clone_all_breakpoints (child_thr, event_thr)...}}}}/* Retry until nothing comes out of waitpid. */continue}/* Now that we've pulled all events out of the kernel, resume* LWPs that don't have an interesting event to report. */for_each_thread ([this] (thread_info *thread) {linux_process_target::resume_stopped_resumed_lwps(thread)resume_one_lwp {resume_one_lwp_throw/* 将fork/pthread_create的调用者及新创建的任务重新唤醒 */ptrace (PTRACE_CONT, lwpid_of (thread),...)}}}
}
四、总结
被调试程序tracee创建新进程/线程时, 会给新任务添加SIGSTOP信号,同时将新创建子任务的pid记录到被调试任务的task->ptrace_message中(用于后续gdb通过ptrace GETEVENTMSG来获取新创建任务的pid),然后通过ptrace_stop将自己及新创建的子任务都挂起并通知gdb, 待gdb收集好相关信息后, 会将被调试程序及其创建的子任务再次唤醒。
至于被调试程序如何知道自己执行fork、pthread_create的时候需要通知gdb,是由gdb通过ptrace(PTRACE_SETOPTIONS)进行设置的。由gdb控制,是否需要跟踪被调试程序创建的子进程、子线程。
最后,回答下文章开头的几个问题:
1)被调试程序通过fork/pthread_create创建的子进程/子线程,同样会被gdb跟踪,不过gdb可以控制是否需要跟踪,也可以设置为不跟踪;
2)在kernel_clone中,会将被调试程序以及其创建的子进程/子线程通过不同方式将其挂起,然后通知gdb;
3)gdb通过ptrace(PTRACE_GETEVENTMSG)获取子进程/子线程的pid信息;
相关文章:

linux ptrace 图文详解(八) gdb跟踪被调试程序的子线程、子进程
目录 一、gdb跟踪被调试程序的fork、pthread_create操作 二、实现原理 三、代码实现 四、总结 (代码:linux 6.3.1,架构:arm64) One look is worth a thousand words. —— Tess Flanders 相关链接: …...

游戏:用python写梦幻西游脚本(谢苏)
《梦幻西游》是一款受欢迎的网络游戏,许多玩家希望通过脚本来增强游戏体验,比如自动打怪、自动治疗等。本文将为您展示一个用Python编写简单《梦幻西游》自动打怪脚本的方案。 需求分析 1.1 具体问题 在《梦幻西游》中,玩家需要频繁与怪物进行…...
MLX-Audio:高效音频合成的新时代利器
MLX-Audio:高效音频合成的新时代利器 现代社会的快节奏生活中,对语音技术的需求越来越高。无论是个性化语音助手,还是内容创作者所需的高效音频生成工具,语音技术都发挥着不可或缺的作用。今天,我们将介绍一个创新的开…...

Spring Boot 3.x集成SaToken使用swagger3+knife4j 4.X生成接口文档
说一说Spring Boot 3.X集成SaToken使用swagger3并使用第三方的knife4j踩过的坑,废话不多说直接上正题,SaToken的我就不贴了 第一步当然是要先导入相关的依赖,包括swagger和knife4j,如下 <dependency><groupId>com.gi…...

用Python监控金价并实现自动提醒!附完整源码
💂 个人网站:【 摸鱼游戏】【神级代码资源网站】【星海网址导航】💻香港大宽带-4H4G 20M只要36/月👉 点此查看详情 在日常投资中,很多朋友喜欢在一些平台买点黄金,低买高卖赚点小差价。但黄金价格实时波动频繁…...
软考-软件设计师中级备考 11、计算机网络
1、计算机网络的分类 按分布范围分类 局域网(LAN):覆盖范围通常在几百米到几千米以内,一般用于连接一个建筑物内或一个园区内的计算机设备,如学校的校园网、企业的办公楼网络等。其特点是传输速率高、延迟低、误码率低…...
【一】浏览器的copy as fetch和copy as bash的区别
浏览器的copy as fetch和copy as bash的区别 位置:devTools->network->请求列表右键 copy as fetch fetch("https://www.kuaishou.com/graphql", {"headers": {"accept": "*/*","accept-language": &qu…...

ChatTempMail - AI驱动的免费临时邮箱服务
在当今数字世界中,保护在线隐私的需求日益增长。ChatTempMail应运而生,作为一款融合人工智能技术的新一代临时邮箱服务,它不仅提供传统临时邮箱的基本功能,还通过AI技术大幅提升了用户体验。 核心功能与特性 1. AI驱动的智能邮件…...

掌握单元测试:提升软件质量的关键步骤
介绍 测试:是一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。 阶段划分:单元测试、集成测试、系统测试、验收测试。 测试方法:白盒测试、黑盒测试及灰盒测试。 单元测试:就是针对最小的功能单元(方法&…...
DeepSeek+Excel:解锁办公效率新高度
目录 一、引言:Excel 遇上 DeepSeek二、认识 DeepSeek:大模型中的得力助手2.1 DeepSeek 的技术架构与原理2.2 DeepSeek 在办公场景中的独特优势 三、DeepSeek 与 Excel 结合的准备工作3.1 获取 DeepSeek API Key3.2 配置 Excel 环境 四、DeepSeekExcel 实…...

YOLOv1模型架构、损失值、NMS极大值抑制
文章目录 前言一、YOLO系列v11、核心思想2、流程解析 二、损失函数1、位置误差2、置信度误差3、类别概率损失 三、NMS(非极大值抑制)总结YOLOv1的优缺点 前言 YOLOv1(You Only Look Once: Unified, Real-Time Object Detection)由…...

【论文阅读】——Articulate AnyMesh: Open-Vocabulary 3D Articulated Objects Modeling
文章目录 摘要一、介绍二、相关工作2.1. 铰接对象建模2.2. 部件感知3D生成 三、方法3.1. 概述3.2. 通过VLM助手进行可移动部件分割3.3. 通过几何感知视觉提示的发音估计3.4. 通过随机关节状态进行细化 四、实验4.1. 定量实验发音估计设置: 4.2. 应用程序 五、结论六、思考 摘要…...

HarmonyOS基本的应用的配置
鸿蒙HarmonyOS组建页面 1、创建ets文件并配置2、修改main_pages.json文件3、修改EntryAbility.ets文件(启动时加载的页面) 1、创建ets文件并配置 Index.ets是创建项目自动构建生成的,我们可以将其删除掉,并重新在page文件夹下创建…...

【redis】集群模式
Redis Cluster是Redis官方推出的分布式解决方案,旨在通过数据分片、高可用和动态扩展能力满足大规模数据存储与高并发访问的需求。其核心机制基于虚拟槽分区,将16384个哈希槽均匀分配给集群中的主节点,每个键通过CRC16哈希算法映射到特定槽位…...
生成自定义的androidjar文件具体操作
在Androidsdk目录下的platform找到对应的api的android源码包路径,如android-32拷贝里面的android.jar文件到目录,如 C:\Users\xxxxxxx\Desktop\android\new_android_jar,然后解压android.jar到目录new_android_jar下。在编译后的aosp源码中找…...

DeepSeek实战--微调
1.为什么是微调 ? 微调LLM(Fine-tuning Large Language Models) 是指基于预训练好的大型语言模型(如GPT、LLaMA、PaLM等),通过特定领域或任务的数据进一步训练,使其适应具体需求的过程。它是将…...
API请求参数有哪些?
通用参数 app_key:应用的唯一标识,用于验证应用身份,调用API时必须提供。 timestamp:请求时间戳,通常为当前时间的毫秒级时间戳,用于防止请求被重放攻击。 format:返回数据的格式,…...
Kaggle图像分类竞赛实战总结详细代码解读
前言 我是跟着李沐的动手学深度学习v2视频学习深度学习的,光看不做假把式,所以在学习完第七章-现代卷积神经网络之后,参加了一次李沐发布的Kaggle竞赛。自己动手,从组织数据集开始,到训练,再到推理&#x…...
系统间安全复制和同步文件
1、系统间安全的复制文件 1.1复制远端文件/目录到本地 scp 192.168.1.2:/etc/yum.conf /etc scp -r 192.168.1.2:/etc/dir /home scp -r -P 6022 root192.168.1.2:/etc/dir /home #-P参数指定远端服务器的ssh端口 1.2 复制本地文件/目录去远端 scp /etc/yum.conf root19…...
Cursor无法SSH远程连接服务器免密登录问题
在本地机器和Ubuntu服务器之间实现SSH远程免密连接,可按如下步骤操作: 1. 生成SSH密钥对 在本地机器上开启终端,使用以下命令生成SSH密钥对: ssh-keygen -t rsa按提示操作,一般直接回车,这样密钥会生成在…...
RHCSA Linux系统软件管理和进程管理
1. RPM管理工具 (1)简介 ① 包名格式 软件名 - 主版本 - 次版本 - 修订号 - 软件发布次数 - 发行商 - CPU架构平台 - 支持系统位数.rpm eg: zsh - 5.0.2 - 14.el7.x86_64.rpm ② 相关网站 http://rpmfind.net/, http://rpm.pbone.net/ ࿰…...
地平线rdk-x5部署yolo11(1) 模型转出
一. 模型导出: 可以参考RDK X5部署YOLOv8-Seg 和v8差不多 、拷贝YOLO项目 git clone https://github.com/ultralytics/ultralytics.git 2、虚拟环境和依赖安装 # 安装虚拟环境 conda create -n yolov8 python3.8 -y # 进入虚拟环境 conda activate yolov8 # 安…...
开源AI对比--dify、n8n
原文网址:开源AI对比--dify、n8n-CSDN博客 简介 本文介绍开源AI工作流工具的选型。 对比 项difyn8n占优者学习难度简单中等dify核心理念用LLM构建应用。“连接一切”。以工作流自动化连接各系统。平手工作模式 Chatflow:对话。支持用户意图识别、上下…...

移动端前端开发中常用的css
在开发移动端项目的时候,很多样式都是相同的,比如说图标大小,头像大小,页面底部保存(添加按钮),项目主体颜色等等,对于这些在项目中常用到的,通常都会写在公共样式中(pub…...

Linux安装Weblogic 教程
前言 WebLogic 是一个由 Oracle 提供的企业级应用服务器,广泛用于部署和管理 Java EE(Enterprise Edition)应用程序。它支持多种服务,包括 Web 服务、企业信息系统、消息驱动的应用等。它是一个强大的应用服务器,旨在…...
JVM——即时编译
分层编译模式:动态平衡启动速度与执行效率 分层编译是现代JVM(如HotSpot、GraalVM)实现高性能的核心策略之一,其核心思想是根据代码的执行热度动态选择不同的编译层次,实现启动速度与运行效率的最佳平衡。以HotSpot虚…...

flutter 的热更新方案shorebird
Flutter 热修复(Shorebird)_flutter shorebird-CSDN博客 Preview Locally | ShorebirdLearn how to preview an existing release of your application.https://docs.shorebird.dev/code-push/preview/ 控制台: Shorebird Console 文档&…...

创建型模式:抽象工厂(Abstract Factory)模式
一、概念与核心思想 抽象工厂(Abstract Factory)模式是创建型设计模式的重要成员,它提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。该模式将对象的创建逻辑封装在抽象工厂及其具体实现类中,客户端通过抽象工厂接口获取所需的对象族,实现对象创…...
Java面试深度解密:Spring Boot、Redis、日志优化、JUnit5及Kafka事务核心技术解析
模拟面试实战 面试官:请解释Spring Boot的自动配置原理?哪些关键注解参与了这一过程? xbhog:Spring Boot通过AutoConfiguration标记核心配置类,通过ConditonalOnClass和ConditionalOnMissingBean判断依赖是否存在并自…...
Python爬虫(22)Python爬虫进阶:Scrapy框架动态页面爬取与高效数据管道设计
目录 一、背景:Scrapy在现代爬虫中的核心价值二、Scrapy项目快速搭建1. 环境准备与项目初始化2. 项目结构解析 三、动态页面处理:集成Splash与中间件1. 配置Splash渲染服务(Docker部署)2. 修改settings.py启用中间件3. 在Spider中…...