gdb 调试多进程中多线程的方法

embedded/2025/2/4 5:53:45/
  1. 示例代码
    首先,给出一个简单的示例程序,演示如何使用 fork 创建多个子进程并且每个进程内部创建多个线程。

示例代码 (main.cpp)

#include <iostream>
#include <thread>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdio>using namespace std;// 线程函数
void thread_function(int thread_id) {printf("Thread %d in process %d is running\n", thread_id, getpid());sleep(2);
}// 创建多个线程
void create_threads(const int threads_num) {vector<thread> threads;for (int i = 0; i < threads_num; i++) {threads.emplace_back(thread_function, i);}for (auto& th : threads) {if (th.joinable()) {th.join();}}
}int main() {const int num_processes = 8;const int num_threads = 8;vector<pid_t> child_pids;// 创建子进程printf("Started to create subprocesses\n");for (int i = 0; i < num_processes; i++) {pid_t pid = fork();if (pid == 0) { // 子进程create_threads(num_threads);exit(EXIT_SUCCESS);} else if (pid == -1) {perror("fork");exit(EXIT_FAILURE);} else { // 父进程记录 PIDchild_pids.push_back(pid);}}printf("End of subprocess creation\n");// 等待子进程结束for (pid_t pid : child_pids) {waitpid(pid, nullptr, 0);}cout << "All processes and threads completed." << endl;return 0;
}

代码说明:
主进程创建多个子进程:通过 fork() 创建 8 个子进程。 每个子进程创建多个线程:每个子进程中创建 8 个线程,通过 std::thread 启动线程并调用 thread_function。 进程和线程的输出:每个线程打印自己的 ID 和所属进程的 PID。
2. 编译程序
使用 g++ 编译程序时,确保添加 -g 选项以生成调试信息,并且链接线程库 -pthread。

Makefile

# 设置编译器
CXX = g++
# 设置编译选项
CXXFLAGS = -Wall
# 设置目标文件和输出可执行文件
TARGET = main
SRC = main.cpp# Debug 和 Release 的编译选项
DEBUG_FLAGS = -g -O0
RELEASE_FLAGS = -O3# 默认目标
all: debug release# 编译 Debug 版本
debug: CXXFLAGS += $(DEBUG_FLAGS)
debug: $(TARGET)_debug$(TARGET)_debug: $(SRC)$(CXX) $(CXXFLAGS) -o $@ $(SRC)# 编译 Release 版本
release: CXXFLAGS += $(RELEASE_FLAGS)
release: $(TARGET)_release$(TARGET)_release: $(SRC)$(CXX) $(CXXFLAGS) -o $@ $(SRC)# 清理目标文件
clean:rm -f $(TARGET)_debug $(TARGET)_release *.o

gdb ./multi_process_threads
3.1. 调试时如何处理 fork 和多进程
当程序执行到 fork() 时,GDB 默认只跟踪父进程。为了在 fork() 时调试子进程,你需要告诉 GDB 在 fork() 后应该跟踪父进程还是子进程。

跟踪子进程:

在 GDB 中输入以下命令来让调试器跟踪子进程:

(gdb) set follow-fork-mode child

跟踪父进程:

如果你想调试父进程而忽略子进程,可以设置为跟踪父进程:

(gdb) set follow-fork-mode parent

3.2. 设置断点
你可以在程序的关键部分设置断点,例如:

在 fork() 处设置断点:

(gdb) break fork

在 thread_function() 函数内部设置断点:

(gdb) break thread_function

在 main() 函数的开头设置断点:

(gdb) break main

3.3. 查看和切换进程
在调试多进程程序时,GDB 允许你查看当前的所有进程以及切换到不同的进程进行调试。

查看所有进程:

使用 info inferiors 命令查看当前调试的所有进程:

(gdb) info inferiors

GDB 会显示当前的进程和子进程信息,包括进程的 ID。

切换到特定进程:

通过 inferior 命令切换到一个特定的进程。例如,切换到进程 2:

(gdb) inferior 2

你可以在切换到进程后使用 continue 来继续调试当前进程。

3.4. 查看和切换线程
在调试多线程程序时,GDB 允许你查看当前线程列表并切换到特定线程。

查看线程列表:

使用 info threads 查看当前进程中的所有线程:

(gdb) info threads

GDB 会列出所有线程的 ID 和当前执行的位置。

切换到特定线程:

通过 thread <thread_id> 命令切换到指定的线程。例如,切换到线程 2:

(gdb) thread 2

切换到目标线程后,你可以使用 next (n) 或 step (s) 命令逐步调试该线程。

3.5. 锁定线程调度
为了避免 GDB 在多线程调试时自动切换线程,你可以使用 set scheduler-locking 命令来控制线程调度。

锁定当前线程:设置 scheduler-locking on 后,GDB 只会调度当前选中的线程,其他线程会被暂停。

(gdb) set scheduler-locking on

恢复调度所有线程:

(gdb) set scheduler-locking off

3.6. 单步调试
使用 next (n) 或 step (s) 命令单步调试程序时,默认情况下 GDB 会调度其他线程。这时,可以使用 set scheduler-locking 锁定线程,确保只步进当前线程。

(gdb) set scheduler-locking on
(gdb) thread 2  # 切换到线程 2
(gdb) next      # 只步进当前线程
  1. 总结
    在调试 fork 创建的多进程和多线程程序时,GDB 提供了多种工具和命令,帮助我们有效地查看和切换进程与线程。通过使用以下技巧,可以帮助你在复杂的多进程和多线程调试环境中更加高效地工作:
  • 使用 set follow-fork-mode 来选择调试父进程或子进程。
  • 使用 info inferiors 来查看并切换进程。
  • 使用 info threads 和 thread 命令来查看并切换线程。
  • 使用 set scheduler-locking 来锁定当前线程的调度,避免其他线程的干扰。
    这些方法将帮助你更好地调试涉及多个进程和线程的程序,并能够精确控制调试过程。希望这个博客的框架和内容可以帮助你顺利写出详细的文章!如果你需要进一步的帮助或调整内容,请随时告诉我。

http://www.ppmy.cn/embedded/159387.html

相关文章

《大语言模型》综述学习笔记

《A Survey of Large Language Models》英文版综述最近出了中文版书——《大语言模型》&#xff0c;本博客作为阅读笔记记录一下&#xff0c;综述主页&#xff1a;https://github.com/RUCAIBox/LLMSurvey 关于LLM的一些概述和理解 记录一些有启发性的说法&#xff1a; 1、当前…

SpringBoot使用 easy-captcha 实现验证码登录功能

文章目录 一、 环境准备1. 解决思路2. 接口文档3. redis下载 二、后端实现1. 引入依赖2. 添加配置3. 后端代码实现4. 前端代码实现 在前后端分离的项目中&#xff0c;登录功能是必不可少的。为了提高安全性&#xff0c;通常会加入验证码验证。 easy-captcha 是一个简单易用的验…

XML DOM 节点树

XML DOM 把 XML 文档视为一棵节点树。 树中的所有节点彼此之间都有关系。 XML DOM 节点树 XML DOM 把 XML 文档视为一种树结构。这种树结构被称为节点树。 可通过这棵树访问所有节点。可以修改或删除它们的内容&#xff0c;也可以创建新的元素。 这颗节点树展示了节点的集合…

图论——最小生成树的扩展应用

最小生成树相关原理 acwing1146.新的开始 假设存在一个“超级发电站” 在每一个矿井修发电站相当于从这个“超级发电站”到各个矿井连一条长度为 v [ i ] v[i] v[i]的边。 这样一来这就是一个最短路的模板题。 #include <iostream> #include <cstring> using na…

基于微信小程序的新闻资讯系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Simula语言的物联网

Simula语言与物联网的结合探讨 引言 物联网&#xff08;Internet of Things&#xff0c;IoT&#xff09;是信息技术与物理设备相结合而形成的一种新兴网络体系。它通过互联网将各种物体与网络连接起来&#xff0c;实现设备之间的智能通信与数据交换&#xff0c;从而提高生活和…

BurpSuite抓包与HTTP基础

文章目录 前言一、BurpSuite1.BurpSuite简介2.BurpSuite安装教程(1)BurpSuite安装与激活(2)安装 https 证书 3.BurpSuite使用4.BurpSuite资料 二、图解HTTP1.HTTP基础知识2.HTTP客户端请求消息3.HTTP服务端响应消息4.HTTP部分请求方法理解5.HTTPS与HTTP 总结 前言 在网络安全和…

【漫话机器学习系列】078.如何选择隐藏单元激活函数(How To Choose Hidden Unit Activation Functions)

选择隐藏单元激活函数是神经网络设计中的一个重要步骤&#xff0c;它直接影响到模型的学习能力和训练效果。不同的激活函数具有不同的性质和适用场景&#xff0c;因此在选择时需要根据模型的需求和问题的特性来决定。以下是一些常见的激活函数及其选择依据&#xff1a; 1. Sig…