GDB 基础使用与多进程调试

news2024/11/26 8:55:50

在这里插入图片描述

​ GDB 全称“GNU symbolic debugger”是 Linux 下常用的程序调试器,当下的 GDB 支持调试多种编程语言编写的程序,包括 C、C++、Go、Objective-C、OpenCL、Ada 等。

01 GDB 基础调试


1.1 基础使用

  1. 安装工具
# 安装 gcc
sudo yum install gcc
# 安装 g++
sudo yum install gcc-c++ 
# 安装 gdb
sudo yum install gdb
  1. 编译程序
gcc -g basic.c -o basic

使用GDB调试程序,编译程序的过程中需要注意以下两点:

🅰️ 要使用GDB调试某个程序,该程序编译时需要加上编译选项 -g,否则编译生成的程序是不包含调试信息的;
🅱️ GCC编译器支持 -O-g 一起参与编译,关闭编译器的程序优化选项。GCC编译过程对进行优化的程度分为5个等级:-O/O0, -O1, -O2, -O3, -Os,调试时一般选择默认选项 -O 不做任何优化。

调试信息:reference: GDB调试信息与调试原理 - 知乎 (zhihu.com)

如果编译命令中不加 -g 选项,在编译后的程序中不会保留调试符号信息。

加不加该选项在启用 GDB 调试时存在明显区别

gdb basic

(1)不加 -g 选项,尾行打印内容为

Reading symbols from basic...(No debugging symbols found in basic)

(2)加 -g 选项,尾行打印内容为

Reading symbols from basic...

可以看到,不加 -g 选项会直接提示程序不包含调试信息 No debugging symbols found in basic

  1. 启动 GDB 调试

GDB调试主要有三种方式:

  • 直接调试目标程序:gdb ./basic

  • 附加进程id:gdb attach pid

  • 调试core文件:gdb filename corename

  1. 退出 GDB 调试
  • 可以用命令:q(quit的缩写)或者 Ctr + d 退出 GDB。

  • 如果GDB attach某个进程,退出GDB之前要用命令 detach 解除附加进程。

1.2 常用命令

本节内容转自:GDB使用详解 - 知乎 (zhihu.com)

命令名称命令缩写命令说明
runr运行一个待调试的程序
continuec让暂停的程序继续运行
nextn运行到下一行
steps单步执行,遇到函数会进入
untilu运行到指定行停下来
finishfi结束当前调用函数,回到上一层调用函数处
returnreturn结束当前调用函数并返回指定值,到上一层函数调用处
jumpj将当前程序执行流跳转到指定行或地址
printp打印变量或寄存器值
backtracebt查看当前线程的调用堆栈
framef切换到当前调用线程的指定堆栈
threadthread切换到指定线程
breakb添加断点
tbreaktb添加临时断点
deleted删除断点
enableenable启用某个断点
disabledisable禁用某个断点
watchwatch监视某一个变量或内存地址的值是否发生变化
listl显示源码
infoi查看断点 / 线程等信息
ptypeptype查看变量类型
disassembledis查看汇编代码
set argsset args设置程序启动命令行参数
show argsshow args查看设置的命令行参数

一般调试中最为常用的命令,gdb 语法糖直接回车重复执行上一条命令。

命令名称命令缩写命令说明
breakb添加断点
runr运行一个待调试的程序
nextn运行到下一行(步过)
steps单步执行,遇到函数会进入(步入)
printp打印变量或寄存器值
listl显示当前正在执行代码位置附近的代码
  1. break 打断点
  • break FunctionName,在函数的入口处添加一个断点;
  • break LineNo,在当前文件行号为LineNo处添加断点;
  • break FileName:LineNo,在FileName文件行号为LineNo处添加一个断点;
  • break FileName:FunctionName,在FileName文件的FunctionName函数的入口处添加断点;
  • break -/+offset,在当前程序暂停位置的前/后 offset 行处下断点;
  • break … if cond,下条件断点;
  1. run 运行程序

启用GDB调试只是附加了一个调试文件,并没有启动这个程序,需要输入run命令(简写为r)启动这个程序

  1. next/step 步过/步入
  • next 是 单步步过(step over),即遇到函数直接跳过,不进入函数内部。

  • step 是 单步步入(step into),即遇到函数会进入函数内部。

  1. print 查看变量值
  • print param,用于在调试过程中查看变量的值;
  • print param=value,用于在调试过程中修改变量的值;
  • print a+b+c,可以进行一定的表达式计算,这里是计算a、b、c三个变量之和;
  • print func(),输出func函数执行的结果,常见的用途是打印系统函数执行失败原因:print strerror(errno)
  • *print this,在c++对象中,可以输出当前对象的各成员变量的值;
  • print arrname@arrlen,输出数组 arrname
  1. list 显示源码上下文
  • list,输出上一次list命令显示的代码后面的代码,如果是第一次执行list命令,则会显示当前正在执行代码位置附近的代码;

  • list -,带一个减号,显示上一次list命令显示的代码前面的代码;

  • list LineNo,显示当前代码文件第 LineNo 行附近的代码;

  • list FileName:LineNo,显示 FileName 文件第 LineNo 行附近的代码;

  • list FunctionName,显示当前文件的 FunctionName 函数附近的代码;

  • list FileName:FunctionName,显示 FileName 文件的 FunctionName 函数附件的代码;

  • list from,to,其中fromto是具体的代码位置,显示这之间的代码;

list命令默认只会输出 10 行源代码,也可以使用如下命令修改:

  • show listsize,查看 list 命令显示的代码行数;
  • set listsize count,设置 list 命令显示的代码行数为 count;

1.3 GDB 基础调试实例

下面结合一个例子熟悉常用的 gdb 调试命令,例子如下:

代码定义了一个静态整型变量param,并初始化为1。

然后定义了两个分别名为func1func2的函数,它接收两个参数 varval,并试图在 将 var的值修改为val。但是func1形参是值传递,函数外部无法实现参数值的修改;func2形参是引用传递,能够完成参数值修改。

最后定义了主函数main,调用func函数,将i的值改为1,并将param加上i的值。

#include<stdio.h>

static int param = 1;

void func1(int var, int val){
    var = val;
    printf("function func1 has been called!");
}

void func2(int *var, int val){
    *var = val;
    printf("function func2 has been called!");
}

int main(){

    int i = 0;
    func1(i, 1);
    func2(&i, 1);
    param += i;
    printf("function main has been called!");

    return 0;
}

首先编译该程序并启用 GDB 调试

$ gcc -g basic.c -o basic
$ gdb basic 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from basic...

使用 b 命令将断点打在 main 函数入口,并使用 r 命令运行程序

(gdb) b main
Breakpoint 1 at 0x11d7: file basic.c, line 15.
(gdb) r
Starting program: /home/randy/study/dev-tool/gdb/basic 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at basic.c:15
15      int main(){

使用 n 命令执行下一行语句,遇到函数直接跳过不进入;使用 s 命令步入函数 func1(i,1);,并使用 l 命令查看当前语句在源码中的上下文。

(gdb) n
17          int i = 0;
(gdb) 
18          func1(i, 1);
(gdb) s
func1 (var=0, val=1) at basic.c:6
6           var = val;
(gdb) l
1       #include<stdio.h>
2
3       static int param = 1;
4
5       void func1(int var, int val){
6           var = val;
7           printf("function func1 has been called!");
8       }
9
10      void func2(int *var, int val){

使用 p 命令查看 func1(var, val) 中 var 在修改前后的值,并查看在 func1 函数执行完成之后,传入的实参 i 的值未完成修改,仍为 0;

(gdb) p var
$1 = 0
(gdb) n
7           printf("function func1 has been called!");
(gdb) p var
$2 = 1
(gdb) n
8       }
(gdb) 
main () at basic.c:19
19          func2(&i, 1);
(gdb) p i
$3 = 0

使用 n 命令步过可以完成 i 值修改的函数 func2,并查看修改之后的值为 1

(gdb) 
main () at basic.c:19
19          func2(&i, 1);
(gdb) p i
$3 = 0
(gdb) n
20          param += i;
(gdb) p i
$4 = 1
(gdb) p param
$5 = 1
(gdb) n
21          printf("function main has been called!");
(gdb) p param
$6 = 2
(gdb) n
23          return 0;
(gdb) n
24      }

上述完整 gdb 调试过程如下:

$ gdb gcc -g basic.c -o basic   
$ gdb gdb basic 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
--Type <RET> for more, q to quit, c to continue without paging--
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from basic...
(gdb) b main
Breakpoint 1 at 0x11d7: file basic.c, line 15.
(gdb) r
Starting program: /home/randy/study/dev-tool/gdb/basic 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at basic.c:15
15      int main(){
(gdb) n
17          int i = 0;
(gdb) 
18          func1(i, 1);
(gdb) s
func1 (var=0, val=1) at basic.c:6
6           var = val;
(gdb) l
1       #include<stdio.h>
2
3       static int param = 1;
4
5       void func1(int var, int val){
6           var = val;
7           printf("function func1 has been called!");
8       }
9
10      void func2(int *var, int val){
(gdb) p var
$1 = 0
(gdb) n
7           printf("function func1 has been called!");
(gdb) p var
$2 = 1
(gdb) n
8       }
(gdb) 
main () at basic.c:19
19          func2(&i, 1);
(gdb) p i
$3 = 0
(gdb) n
20          param += i;
(gdb) p i
$4 = 1
(gdb) p param
$5 = 1
(gdb) n
21          printf("function main has been called!");
(gdb) p param
$6 = 2
(gdb) n
23          return 0;
(gdb) n
24      }
(gdb) n

02 GDB 调试多进程程序


2.1 多进程调试中常用的 GDB 命令

命令名称命令缩写命令说明
set follow-fork-mode设置需要跟踪的子进程
set detach-on-fork设置在程序运行时是否将调试器与进程分离
attach附加到正在运行的进程进行调试
detach从正在调试的进程中分离gdb调试器
inferior切换到另一个进程上下文进行调试
continuec继续执行程序,直到下一个断点或程序结束
backtracebt显示函数调用栈
framef切换到指定堆栈
set args设置程序启动命令行参数
show args查看设置的命令行参数
  1. set follow-fork-mode [parent/child] 指定需要跟踪的子进程

set follow-fork-mode命令必须在程序开始执行前设置才能生效。如果在程序启动后再设置该命令,则不会影响当前正在进行的调试会话。该命令 set follow-fork-mode MODE 可以指定以下三种模式:

  • parent:只跟踪父进程,fork 之后继续调试父进程,子进程不受影响
  • child:只跟踪子进程,fork之后调试子进程,父进程不受影响
  • ask:每次 fork 时询问用户选择
  1. set detach-on-fork [on/off]是否将调试器与进程分离
  • on, 调试器将与新进程分离,以便在进程创建新进程时能够安全地停止和调试新进程,原进程会继续执行,直到调试器与新进程分离为止

  • off,调试器将与新进程紧密关联,一直跟随新进程的执行,原进程不会等待新进程完成复制执行环境后才会重新启动

  1. attach/detach 附加/分离指定进程
  • attach [PID], 让 gdb 附加到已经在运行的进程上进行调试

  • detach [PID],从正在调试的进程中分离gdb调试器

  1. inferior 切换到不同的进程上下文进行调试

inferior [PID | PNAME],其中PID为进程编号,PNAME为进程名称。

  1. continue 终端程序后继续执行

当GDB触发断点或者使用 Ctrl + C 命令中断下来后,想让程序继续运行,只要输入 continue(简写为c)命令即可

  1. bt/f 显示/切换函数调用栈
  • backtrace,也可简写为 bt,用于查看当前调用堆栈
  • frame 堆栈编号,也可简写为 f 堆栈编号,用于切换到其他堆栈处
  1. set/show args 设置/查看命令行参数
  • set args args1,设置单个启动参数 args1
  • set args “-p” “password”,如果单个参数之间有空格,可以使用引号将参数包裹起来;
  • set args args1 args2 args3,设置多个启动参数,参数之间用空格隔开;
  • set args,不带参数,则清除之前设置的参数;

2.2 GDB 多进程调试实例

下面结合一个例子介绍 gdb 调试多进程程序,例子如下:

程序首先创建了一个管道,然后通过fork()函数创建了两个子进程。

在第一个子进程中,它向管道的写端写入了一条消息,然后退出;在第二个子进程中,它从管道的读端读取了这条消息,并将其打印出来,然后退出。

父进程在创建完子进程后,关闭了管道的读端和写端,并分别等待两个子进程结束。

当两个子进程都结束时,父进程才会继续执行,并打印一条消息表示所有子进程都已完成。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main() {
    int pipefd[2];  // 管道描述符数组
    pid_t pid1, pid2;  // 子进程ID变量

    if (pipe(pipefd) == -1) {  // 创建管道
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    pid1 = fork();  // 创建第一个子进程
    if (pid1 == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid1 == 0) {  // 在第一个子进程中
        close(pipefd[0]);  // 关闭读端
        char msg[] = "Hello, I'm child process 1!";
        write(pipefd[1], msg, strlen(msg));  // 向写端写入消息
        close(pipefd[1]);  // 关闭写端
        exit(EXIT_SUCCESS);
    } 

    pid2 = fork();  // 创建第二个子进程
    if (pid2 == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid2 == 0) {  // 在第二个子进程中
        close(pipefd[1]);  // 关闭写端
        char buf[1024];
        read(pipefd[0], buf, sizeof(buf));  // 从读端读取消息
        printf("This is child process 2. Received message from child process 1: %s\n", buf);
        close(pipefd[0]);  // 关闭读端
        exit(EXIT_SUCCESS);
    }

    // 父进程中
    close(pipefd[0]);  // 关闭读端
    close(pipefd[1]);  // 关闭写端
    waitpid(pid1, NULL, 0);  // 等待第一个子进程结束
    waitpid(pid2, NULL, 0);  // 等待第二个子进程结束
    printf("All child processes have completed.\n");

    return 0;
}

2.2.1 调试父进程

首先编译该程序并启用 GDB 调试

$ gdb gcc -g multi_process.c -o multi_process   
$ gdb gdb multi_process 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from multi_process...

set follow-fork-mode parent 设置仅跟踪父进程(也可以直接跳过这个步骤,默认 parent);使用 b 命令将断点打在 main 函数入口;并使用 r 命令运行程序。

(gdb) set follow-fork-mode parent
(gdb) b main
Breakpoint 1 at 0x4007f8: file multi_process.c, line 11.
(gdb) r
Starting program: /home/randy/study/dev-tool/gdb/multi_process 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at multi_process.c:7
7       int main() {

使用 n 命令单步执行,查看程序执行情况,可以看到第一个子进程在被 fork 成功之后就从 gdb 调试中分离并继续执行了 [Detaching after fork from child process 20228]

(gdb) n
16          pid1 = fork();  // 创建第一个子进程
(gdb) p pid1
$1 = 0
(gdb) n
[Detaching after fork from child process 20228]
17          if (pid1 == -1) {
(gdb) 
22          if (pid1 == 0) {  // 在第一个子进程中

同样的继续使用 n 命令单步执行,可以看到第二个子进程在被 fork 成功之后也从 gdb 调试中分离 [Detaching after fork from child process 20415]

(gdb) 
30          pid2 = fork();  // 创建第二个子进程
(gdb) p pid2
$2 = 0
(gdb) n
[Detaching after fork from child process 20415]
# 第二个子进程执行完成读取第一个子进程发送的消息并打印如下输出
This is child process 2. Received message from child process 1: Hello, I'm child process 1!
31          if (pid2 == -1) {
(gdb) 
36          if (pid2 == 0) {  // 在第二个子进程中
(gdb) 
46          close(pipefd[0]);  // 关闭读端
(gdb) 
47          close(pipefd[1]);  // 关闭写端
(gdb) 
48          waitpid(pid1, NULL, 0);  // 等待第一个子进程结束
(gdb) 
49          waitpid(pid2, NULL, 0);  // 等待第二个子进程结束
(gdb) 
50          printf("All child processes have completed.\n");
(gdb) 
All child processes have completed.
52          return 0;

2.2.2 调试第一个子进程

首先编译该程序并启用 GDB 调试

$ gdb gcc -g multi_process.c -o multi_process   
$ gdb gdb multi_process 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from multi_process...

set follow-fork-mode child 设置跟踪子进程,使用 b 命令将断点打在 main 函数入口,并使用 r 命令运行程序

(gdb) set follow-fork-mode child
(gdb) b main
Breakpoint 1 at 0x12b8: file multi_process.c, line 7.
(gdb) r
Starting program: /home/randy/study/dev-tool/gdb/multi_process 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at multi_process.c:7
7       int main() {

n 单步执行程序,直到第一个子进程 pid1=0

(gdb) n
11          if (pipe(pipefd) == -1) {  // 创建管道
(gdb) 
16          pid1 = fork();  // 创建第一个子进程
(gdb) p pid1
$1 = 0
(gdb) n
[Attaching after Thread 0x7ffff7d8c740 (LWP 5001) fork to child process 5166] # 表示gdb正在附加到一个已经fork出子进程的父进程中
[New inferior 2 (process 5166)] # 表示gdb创建了一个新的被调试进程,并将其编号设置为2,该进程的PID为5166
[Detaching after fork from parent process 5001] # 表示gdb从父进程中分离出子进程,将其作为独立的被调试进程进行调试
[Inferior 1 (process 5001) detached] # 表示gdb已经从原始的父进程中分离出来,成为一个独立的进程(或线程)控制器。由于该进程的编号为1,因此显示为Inferior 1
[Thread debugging using libthread_db enabled] # 表示gdb使用libthread_db库来进行线程调试
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Switching to Thread 0x7ffff7d8c740 (LWP 5166)] # 表示gdb正在切换到子进程的线程上进行调试
main () at multi_process.c:17
17          if (pid1 == -1) {

在子进程中 使用 n 命令单步执行并观察变量值,由于 set follow-fork-mode child 只跟踪子进程,在上一步中 [Inferior 1 (process 5001) detached] 表示 gdb 已经将父进程分离并继续执行,所以在下面的内容中第一个子进程写完消息之后,第二个子进程就收到了消息并在打印后退出了。

(gdb) n
22          if (pid1 == 0) {  // 在第一个子进程中
(gdb) 
23              close(pipefd[0]);  // 关闭读端
(gdb) 
24              char msg[] = "Hello, I'm child process 1!";
(gdb) p msg
$1 = "`\317\377\367\377\177\000\000\330J\374\367\377\177\000\000\000\000\000\000\000\000\000\000\060\334\377\377"
(gdb) n
25              write(pipefd[1], msg, strlen(msg));  // 向写端写入消息
(gdb) 
This is child process 2. Received message from child process 1: Hello, I'm child process 1!
26              close(pipefd[1]);  // 关闭写端
(gdb) 
27              exit(EXIT_SUCCESS);
(gdb) 
[Inferior 2 (process 6971) exited normally]
All child processes have completed.

2.2.3 调试第二个子进程

首先编译该程序并启用 GDB 调试

$ gdb gcc -g multi_process.c -o multi_process   
$ gdb gdb multi_process 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from multi_process...

先设置跟踪父进程 set follow-fork-mode parent 来跳过第一个子进程的 fork 操作;使用 b 命令将断点打在创建第二个子进程的 fork 函数之前;并使用 r 命令运行程序。

(gdb) set follow-fork-mode parent
(gdb) b multi_process.c:22
Breakpoint 1 at 0x40083f: file multi_process.c, line 22.
(gdb) r
Starting program: /home/randy/study/dev-tool/gdb/multi_process 
[Detaching after fork from child process 25249] # 分离第一个子进程

Breakpoint 1, main () at multi_process.c:22
22          if (pid1 == 0) {  // 在第一个子进程中

在继续执行程序前 set follow-fork-mode child 设置 gdb 调试跟踪子进程,然后再使用 n 命令单步执行 fork 创建第二个子进程,可以看到第二个子进程在被 fork 成功之后,gdb 调试就分离的父进程,并切换到第二个子进程中。

(gdb) set follow-fork-mode child
(gdb) n
30          pid2 = fork();  // 创建第二个子进程
(gdb) n
[Attaching after process 25358 fork to child process 25358]
[New inferior 2 (process 25358)]
[Detaching after fork from parent process 25245]
[Inferior 1 (process 25245) detached] # 已经分离父进程
[Switching to process 25358] # 切换到第二个子进程
31          if (pid2 == -1) {

进入第二个子进程后,就可以使用 n 命令单步执行程序,使用 p 命令查看变量值。

(gdb) n
36          if (pid2 == 0) {  // 在第二个子进程中
(gdb) p pid2
$1 = 0
(gdb) n
37              close(pipefd[1]);  // 关闭写端
(gdb) n
39              read(pipefd[0], buf, sizeof(buf));  // 从读端读取消息
(gdb) n
40              printf("This is child process 2. Received message from child process 1: %s\n", buf);
(gdb) p buf
$2 = "Hello, I'm child process 1!", '\000' <repeats 477 times>...
(gdb) n
This is child process 2. Received message from child process 1: Hello, I'm child process 1!
41              close(pipefd[0]);  // 关闭读端
(gdb) n
42              exit(EXIT_SUCCESS);
(gdb) 
[Inferior 2 (process 25358) exited normally]
All child processes have completed.

如果文章对你有帮助,欢迎一键三连 👍 ⭐️ 💬 。如果还能够点击关注,那真的是对我最大的鼓励 🔥 🔥 🔥 。


参考资料

GDB Documentation

学习使用 GDB 调试代码 | Linux 中国 - 知乎 (zhihu.com)

GDB使用详解 - 知乎 (zhihu.com)

使用 GDB 调试多进程程序 - 简书

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

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

相关文章

记录一次el-table动态添加删除列导致表格样式错误(或不聚集)问题

记录一次el-table动态添加删除列导致表格样式错误问题 需求背景出现的问题解决方案理论&#xff1a;在el-table中设置key值&#xff0c;重新赋值表格数据之后&#xff0c;更新key值&#xff0c;达到动态更新效果 需求背景 一个电商类商品管理平台&#xff08;类似shopify产品编…

2023 华为 Datacom-HCIE 真题题库 06--含解析

多项选择 1.[试题编号&#xff1a;190185] &#xff08;多选题&#xff09;如图所示&#xff0c;PE 1和PE2之间通过Loopback0接口建立MP-BGP邻居关系&#xff0c;在配置完成之后&#xff0c;发现CE1和CE2之间无法互相学习路由&#xff0c;以下哪些项会导致该问题出现? A、PE1…

初识linux之简单了解TCP协议与UDP协议

目录 一、理解源IP地址和目的IP地址 二、端口号 1. 为什么要有端口号 2. 理解端口号 3. 源端口号和目的端口号 三、初步了解TCP协议和UDP协议 1. 初步认识TCP协议 2. 初步认识UDP协议 3. 可靠传输与不可靠传输 四、网络字节序 1. 网络字节序的概念 2. 如何形成网络…

python+django电子笔记交易系统vue

编码使用python&#xff08;我的pycharm版本是2021.3.3&#xff09;&#xff0c;数据库使用mysql&#xff08;我的mysql版本5.5&#xff09;。网站点击能够跳转各个页面&#xff0c;不用部署服务器&#xff0c;本地运行即可。 题目&#xff1a;基于django的电子笔记交易系统 功…

并发编程的三大特性之有序性

有序性的概念 Java文件在被cpu执行前会进行编译成cpu可以执行的指令&#xff0c;为了提高cpu的执行效率会对其中的一些语句进行重排序。Java指令最终是乱序执行的目的是为了提高cpu的执行效率&#xff0c;发挥cpu的性能 单例模式由于指令重排可能会出现上述的问题&#xff0…

ASP.NET Core

1. 入口文件 一个应用程序总有一个入口文件&#xff0c;是应用启动代码开始执行的地方&#xff0c;这里往往也会涉及到应用的各种配置。当我们接触到一个新框架的时候&#xff0c;可以从入口文件入手&#xff0c;了解入口文件&#xff0c;能够帮助我们更好地理解应用的相关配置…

SOC与MCU的区别及汽车电子未来发展以及展望

SOC与MCU的区别及汽车电子未来发展以及展望 MCU与SOC的区别 CPU&#xff08;Central Processing Unit&#xff09;&#xff0c;是一台计算机的运算核心和控制核心。CPU由运算器、控制器和寄存器及实现它们之间联系的数据、控制及状态的总线构成。差不多所有的CPU的运作原理可…

【PHP】问题已解决:宝塔面板搭建php网站无法上传图片或是文件(保姆级图文)

目录 问题情况原因和解决方法总结 『PHP』分享PHP环境配置到项目实战个人学习笔记。 欢迎关注 『PHP』 系列&#xff0c;持续更新中 欢迎关注 『PHP』 系列&#xff0c;持续更新中 问题情况 宝塔面板搭建php网站无法上传图片或是文件。 原因和解决方法 检查你的php里是否安装…

老板让你写个PPT没有头绪?没事,ChatGPT来帮你!

文章目录 前言一、先确定写什么——准备内容二、再看看能用吗——自动生成PPT三、最后再改改——看个人喜好写在最后 前言 自从人工智能横空而出&#xff0c;它在人们的生活中产生了巨大的影响。尤其在企业办公领域&#xff0c;借助人工智能的力量&#xff0c;能够迅速产出丰富…

千乎万唤始出来,支持gpt3和gpt4支持画图,的在线gpt应用接入案例开源上线啦

了解OPEN AI 平台用户一直在说&#xff0c;这个接口要怎么对接&#xff0c;如何在体验。 由于我一直忙于接口中台开发&#xff0c;所以在线基于OPEN AI 接口实例例子就一直没有写。现在终于写完了。 基于纯HTMLCSSJS 小白也能轻松上手部署。代码简单清晰。 这里不多做其他赘述…

tensorflow及其keras如何保存模型

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

如何从宏观层面构建优秀的大语言模型

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

高阶python | 装饰器

python版本&#xff1a;3.10.0 在学习装饰器前先了解一下闭包 阿-岳同学【python技巧060】形象理解闭包&#xff0c;玩转闭包 通过视频首先可以了解到主要的三个知识点 闭包是嵌套结构内层函数有调用外层函数的变量为闭包&#xff0c;同时内层函数是闭包函数&#xff08;所…

根据DataFrame1中指定列c1的每个值a1 从DataFrame2中指定列c2中的每个值a2 找到与a1最临近的值a2,进行所在行合并

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 根据DataFrame1中指定列c1的每个值a1 从DataFrame2中指定列c2中的每个值a2 找到与a1最临近的值a2,进行所在行合并 [太阳]选择题 关于以下代码说法错误的是&#xff1a; import pandas as pd…

新手应该如何快速上手MySQL数据库?

前言 数据库是计算机系统中用于存储、管理和检索数据的系统。它允许用户访问并管理数据&#xff0c;并具有可靠、可扩展和高效的特性。 文章目录 前言1. 数据库的相关概念1.1 数据1.2 数据库1.3 数据库管理系统1.4 数据库系统1.5 SQL 2. MySQL数据库2.1 MySQL安装2.2 MySQL配置…

我在CSDN的736个日子——两年纪念日“随想”

2021-05-21~2023-05-27&#xff0c;我在 CSDN 已有 736 个日子。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简单…… 地址&#xff1a;https:/…

设计一:51单片机流水灯控制

目录 一、设计内容 二、硬件电路分析 三、仿真原理图 四、程序设计 五、仿真结果 六、思考题 作者有话说 一、设计内容 本次设计使用4个按键&#xff0c;当KEY1按下时&#xff0c;P0口所接的发光二极管&#xff08;D1~D8&#xff09;以100ms的时间间隔自下至上循环点亮3…

软考A计划-试题模拟含答案解析-卷六

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

在线Excel绝配:SpreadJS 16.1.1+GcExcel 6.1.1 Crack

前端&#xff1a;SpreadJS 16.1.1 后端&#xff1a; GcExcel 6.1.1 全能 SpreadJS 16.1.1此版本的产品中包含以下功能和增强功能。 添加了各种输入掩码样式选项。 添加了在保护工作表时设置密码以及在取消保护时验证密码的支持。 增强了组合图以将其显示为仪表图。 添加了…

chatgpt赋能python:Python成为行业中的主力之一

Python成为行业中的主力之一 Python作为一种高级编程语言&#xff0c;已经成为了行业中的主力之一。Python的功能强大&#xff0c;易于学习和使用&#xff0c;而且兼容性良好。在数据科学&#xff0c;人工智能&#xff0c;web开发等领域&#xff0c;Python已经成为了不可或缺的…