Linux:gdb调试多进程

时间:2021-11-07 08:30:41

gdb是Linux下的一款调试工具。我们的进程需要调试的话,那么在编译的时候需要加上-g选项,这个选项是增加调试信息。如果不加,则无法调试。如果有core文件的话,可以用core文件与进程一起调试。

下面我们先来介绍一下gdb下的基本指令

  • list(l)行号:显示源代码。一次显示10行
  • list(l)函数名:列出某个函数的源代码
  • r或run:运行程序
  • s或step:进入函数调用
  • breaktrace(bt):查看函数调用栈
  • info(i) locals:查看当前栈帧局部变量的值
  • info break :查看断点信息
  • finish:执行到当前函数返回,然后停下来等待命令
  • print(p):打印表达式的值
  • set var:修改变量的值
  • quit(q):退出gdb
  • break(b) 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • continue(c):从当前位置开始连续而非单步执行程序
  • run(r):从开始连续而非单步执行程序
  • delete(d)break:删除所有断点
  • delete(d)break n:删除序号为n的断点
  • disable break:禁用断点
  • enable break:启用断点
  • info(i) break:参看当前设置了断点
  • next(n):单条执行

调试多进程

gdb下默认调试的时候只调试主进程。但是在gdb下只要对follow-fork-mode与detach-on-fork这两个选项进行设置,这时候就可以对多进程进行调试。首先我们先写一个多进程代码,作为测试用例。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
  pid_t pid = fork();
  if(pid < 0) {
    printf("fork erro\n");
  }

  if(pid == 0) { // child
    printf("i am child, my pid is %d, my father pid is %d!\n",getpid(), getppid());
  } else {//father
    printf("i am father, my pid is %d!\n",getpid());
    wait(NULL);
  }

  return 0;
}

gcc编译的时候需要加上-g选项。
Linux:gdb调试多进程
接下来,我们打开gdb进行调试。
Linux:gdb调试多进程
利用指令list来查看代码。一次只能显示10行,按下回车则可以显示后面的。我们这时候可以利用show指令先看一下刚才说的follow-fork-mode与detach-on-fork的默认选项是什么。
Linux:gdb调试多进程

我们发现,默认的follow-fork-mode与detach-on-fork选项分别设置为parent与on,这样的意思为调试时不分离进程,只调试主进程。

follow-fork-mode detach-on-fork 所起到的效果
parent on 只调试主进程
child on 只调试子进程
parent off 同时调试两个进程,gdb跟主进程,子进程阻塞在fork位置
child off 同时调试两个进程,gdb跟子进程,主进程阻塞在fork位置

接下来我们利用set将follow-fork-mode与detach-on-fork的选项分别设置为child与off,这样设置的话就可以对子进程调试了。
Linux:gdb调试多进程
接下来分别在父子进程处用break(b)打下两个断点,然后run(r)运行代码。这时候代码会在第一个断点处停下。我们这里选择在第九行打一个断点,也就是fork之后。fork完毕后,会创建一个子进程。此时我们用info inferiors这个指令,可以查看现在程序中的多个进程,其中gdb会默认给它们编号,这个编号便于我们在调试的时候对进程进行切换。由于我们follow-fork-mode与detach-on-fork选项分别设置的是child与off,所以这个时候gdb在调试的时候跟随子进程。如果gdb跟随某个进程,那么info inferiors查看的时候,这个进程编号前会有*这表示gdb跟子进程。
Linux:gdb调试多进程
此时,我们可以看到在这个时候*在编号为2的进程旁边, 也就是说编号2是子进程。其实说通俗点,就是*在谁前面,现在gdb调试的就是谁。可以利用inferiors加编号,来切换要调试的进程。
Linux:gdb调试多进程
我们发现,这个时候*到了编号为1的进程旁边,也就是说现在调试的是主进程。如果能够切换进程了,那么这个时候利用我们前面gdb的基本操作就可以随心所欲的调试进程了。(本人表示还是有点难度的。。。。一般调试都用printf大法!!!)


欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!