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

ops/2025/2/4 6:25:43/
  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/ops/155498.html

相关文章

【游戏设计原理】98 - 时间膨胀

从上文中&#xff0c;我们可以得到以下几个启示&#xff1a; 游戏设计的核心目标是让玩家感到“时间飞逝” 游戏的成功与否&#xff0c;往往取决于玩家的沉浸感。如果玩家能够完全投入游戏并感受到时间飞逝&#xff0c;说明游戏设计在玩法、挑战、叙事等方面达到了吸引人的平衡…

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

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

人工智能学习(四)之机器学习基本概念

机器学习基本概念详细解析&#xff1a;从生活实例轻松入门 在当今数字化时代&#xff0c;机器学习作为人工智能领域的核心技术之一&#xff0c;正深刻地改变着我们的生活和工作方式。从智能语音助手到图像识别系统&#xff0c;从个性化推荐引擎到自动驾驶汽车&#xff0c;机器…

如何让DeepSeek恢复联网功能?解决(由于技术原因,联网搜索暂不可用)

DeekSeek提示&#xff1a;&#xff08;由于技术原因&#xff0c;联网搜索暂不可用&#xff09; 众所周知&#xff0c;因为海外黑客的ddos攻击、僵尸网络攻击&#xff0c;deepseek的联网功能一直处于宕机阶段&#xff0c;但是很多问题不联网出来的结果都还是2023年的&#xff0c…

ubuntuCUDA安装

系列文章目录 移动硬盘制作Ubuntu系统盘 前言 根据前篇“移动硬盘制作Ubuntu系统盘”安装系统后&#xff0c;还不能够使用显卡。 如果需要使用显卡&#xff0c;还需要进行相关驱动的安装&#xff08;如使用的为Nvidia显卡&#xff0c;就需要安装相关的Nvidia显卡驱动&#xff…

VSCode 中的 Git Graph扩展使用详解

VSCode 中的 Git Graph 详解 1. 什么是 Git Graph&#xff1f; Git Graph 是 VSCode 中的一款 Git 可视化扩展&#xff0c;它提供了一种 图形化方式 来查看 Git 提交历史、分支、合并记录等信息&#xff0c;使得 Git 版本管理更加直观和高效。 通过 Git Graph&#xff0c;你…

ARM嵌入式学习--第十天(UART)

--UART介绍 UART(Universal Asynchonous Receiver and Transmitter)通用异步接收器&#xff0c;是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用来与PC进行通信&#xff0c;包括与监控…

指导初学者使用Anaconda运行GitHub上One - DM项目的步骤

以下是指导初学者使用Anaconda运行GitHub上One - DM项目的步骤&#xff1a; 1. 安装Anaconda 下载Anaconda&#xff1a; 让初学者访问Anaconda官网&#xff08;https://www.anaconda.com/products/distribution&#xff09;&#xff0c;根据其操作系统&#xff08;Windows、M…