当前位置: 首页 > article >正文

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

  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 { // 父进程记录 PID
            child_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.kler.cn/a/531182.html

相关文章:

  • ubuntu解决普通用户无法进入root
  • 基于密度泛函理论研究二维材料掺杂前后光电性能变化的模拟项目规划
  • 洛谷 P10289 [GESP样题 八级] 小杨的旅游 C++ 完整题解
  • Spring Boot 实例解析:配置文件
  • 数据库 - Sqlserver - SQLEXPRESS、由Windows认证改为SQL Server Express认证进行连接 (sa登录)
  • Zemax 中带有体素探测器的激光谐振腔
  • linux远程链接mysql数据库的方法
  • 全面剖析 XXE 漏洞:从原理到修复
  • 读书笔记--分布式架构的异步化和缓存技术原理及应用场景
  • Weevely代码分析
  • Vue和Java使用AES加密传输
  • DeepSeek系列模型完全使用手册|附安装教程
  • SpringCloud系列教程:微服务的未来(二十)Seata快速入门、部署TC服务、微服务集成Seata
  • Vue.js 异步、延迟组件加载
  • 数据结构:时间复杂度
  • list容器(详解)
  • diffusion 训练trick 多横纵比设置
  • 算法总结-二分查找
  • 取模与加减乘除原理,模拟实现代码及相关公式推导
  • 【线程】基于阻塞队列的生产者消费者模型
  • 【C语言篇】“三子棋”
  • kubernetes(二)
  • 对比JSON和Hessian2的序列化格式
  • 前端 | JavaScript中的reduce方法
  • 【14】WLC3504 HA配置实例
  • 【股票数据API接口49】如何获取股票实时交易数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据