linux fork进程怎么调试

linux fork进程怎么调试,第1张

如果你想跟踪子进程进行调试,可以使用set follow-fork-mode mode来设置fork跟随模式。

set follow-fork-mode 所带的mode参数可以是以下的一种:

parent

gdb只跟踪父进程,不跟踪子进程,这是默认的模式。

child

gdb在子进程产生以后只跟踪子进程,放弃对父进程的跟踪。

进入gdb以后,我们可以使用show follow-fork-mode来查看目前的跟踪模式。

可以看到目前使用的模式是parent。

然而,有的时候,我们想同时调试父进程和子进程,以上的方法就不能满足了。Linux提供了set detach-on-fork mode命令来供我们使用。其使用的mode可以是以下的一种:

on

只调试父进程或子进程的其中一个(根据follow-fork-mode来决定),这是默认的模式。

off

父子进程都在gdb的控制之下,其中一个进程正常调试(根据follow-fork-mode来决定)

另一个进程会被设置为暂停状态。

同样,show detach-on-fork显示了目前是的detach-on-fork模式,如上图。

以上是调试fork产生子进程的情况,但是如果子进程使用exec系统函数而装载了新程序执行呢?——我们使用set follow-exec-mode mode提供的模式来跟踪这个exec装载的程序。mode可以是以下的一种:

new 当发生exec的时候,如果这个选项是new,则新建一个inferior给执行起来的子进程,而父进程的inferior仍然保留,当前保留的inferior的程序状态是没有执行。

same 当发生exec的时候,如果这个选项是same(默认值),因为父进程已经退出,所以自动在执行exec的inferior上控制子进程。

follow-fork-mode

在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。

follow-fork-mode的用法为:

set follow-fork-mode [parent|child]

parent: fork之后继续调试父进程,子进程不受影响。

child: fork之后调试子进程,父进程不受影响。

因此如果需要调试子进程,在启动gdb后:

(gdb) set follow-fork-mode child

并在子进程代码设置断点。

此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:

set detach-on-fork [on|off]

on: 断开调试follow-fork-mode指定的进程。

off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。

注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。

follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核/gdb版本限制,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,例如上图3进程系统,调试起来并不方便。

在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。follow-fork-mode的用法为:set follow-fork-mode [parentchild]parent: fork之后继续调试父进程,子进程不受影响。child: fork之后调试子进程,父进程不受影响。因此如果需要调试子进程,在启动gdb后:(gdb) set follow-fork-mode child并在子进程代码设置断点。此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:set detach-on-fork [onoff]on: 断开调试follow-fork-mode指定的进程。off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核/gdb版本限制,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,例如上图3进程系统,调试起来并不方便。Attach子进程众所周知,GDB有附着(attach)到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。例如我们要调试某个进程RIM_Oracle_Agent.9i,首先得到该进程的pid[root@tivf09 tianq]# ps -efgrep RIM_Oracle_Agent.9inobody6722 6721 0 05:57 ?00:00:00 RIM_Oracle_Agent.9iroot 7541 27816 0 06:10 pts/300:00:00 grep -i rim_oracle_agent.9i通过pstree可以看到,这是一个三进程系统,oserv是RIM_Oracle_prog的父进程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父进程。[root@tivf09 root]# pstree -H 6722通过 pstree 察看进程启动GDB,attach到该进程用 GDB 连接进程现在就可以调试了。一个新的问题是,子进程一直在运行,attach上去后都不知道运行到哪里了。有没有办法解决呢?一个办法是,在要调试的子进程初始代码中,比如main函数开始处,加入一段特殊代码,使子进程在某个条件成立时便循环睡眠等待,attach到进程后在该代码段后设上断点,再把成立的条件取消,使代码可以继续执行下去。至于这段代码所采用的条件,看你的偏好了。比如我们可以检查一个指定的环境变量的值,或者检查一个特定的文件存不存在。以文件为例,其形式可以如下:void debug_wait(char *tag_file){while(1){if (tag_file存在)睡眠一段时间elsebreak}}当attach到进程后,在该段代码之后设上断点,再把该文件删除就OK了。当然你也可以采用其他的条件或形式,只要这个条件可以设置/检测即可。Attach进程方法还是很方便的,它能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(daemon process),唯一需要的就是加入一小段代码。GDB wrapper很多时候,父进程 fork 出子进程,子进程会紧接着调用 exec族函数来执行新的代码。对于这种情况,我们也可以使用gdb wrapper 方法。它的优点是不用添加额外代码。其基本原理是以gdb调用待执行代码作为一个新的整体来被exec函数执行,使得待执行代码始终处于gdb的控制中,这样我们自然能够调试该子进程代码。还是上面那个例子,RIM_Oracle_prog fork出子进程后将紧接着执行RIM_Oracle_Agent.9i的二进制代码文件。我们将该文件重命名为RIM_Oracle_Agent.9i.binary,并新建一个名为RIM_Oracle_Agent.9i的shell脚本文件,其内容如下:[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary[root@tivf09 bin]# cat RIM_Oracle_Agent.9i#!/bin/shgdb RIM_Oracle_Agent.binary当fork的子进程执行名为RIM_Oracle_Agent.9i的文件时,gdb会被首先启动,使得要调试的代码处于gdb控制之下。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/8522353.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-17
下一篇2023-04-17

发表评论

登录后才能评论

评论列表(0条)

    保存