主要方法
1、attach方法
2、follow-fork-mode方法
背景知识
首先要进行调试,我们就不得不先搞清楚调试的一些基本操作。
gdb(Linux调试器)使用
(1)产生调试信息:要进行代码的调试,就需要有调试信息,要产生调试信息,就需要在源代码生成时添加-g选项;
(2)调试的开始和退出
开始调试:gdb file(file表示要进行调试的源文件)
退出调试:ctrl+d或quit
(3)调试的一些基本操作
attach调试方法
attach是GDB中中有附着到正在运行的进程中的功能实现,通过attach(pid),就可直接该进程进行调试
在介绍这种调试方法时,我们需要先对守护进程做一个简单的学习,因为attach方法对于守护进程的调试有很大的作用
守护进程
定义:守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。
简介:守护进程是一个在后台运行并且不受任何终端控制的进程,如果你了解过信号方面的知识,你就会明白信号在捕捉的时候为什么会单独留下一个‘9号’进程不能被捕获,两者其实都是从保护系统的角度所设计出来的。
守护进程创建步骤:
(1)创建子进程,终结父进程;
(2)在子进程中创建新会话;
(3)改变工作目录;
(4)重建子网掩码;
简单测试(在这段代码中我们很明显会发现父进程中调用的Div函数中有一个除0错误):
//Makefile(Makefile中的注释并不是“//”这里这是为了表示文件的信息,后面将不再做解释)
gdb:gdb.c
gcc -o $@ $^ -g -lpthread
.PHONY:clean
clean:
rm -f gdb
代码编译结果:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
int Div(int num1, int num2)
{
pit_t id=getpid();
printf("my pid is %d\n",id);
int result, diff;
diff = num1 - num2;
result = num1 / diff;
return result;
}
int main()
{
pid_t pid;
pid = fork();
if (pid <0) {
printf("fork err\n");
exit(-1);
} else if (pid == 0) {//child
int val1 = 5;
int val2 = 1;
int ret = 0;
int i=5;
while(i--) {//parent
ret = Div(val1, val2);
val2++;
val1--;
printf("cur result is %d\n",ret);
}
} else {//child
sleep(4);
wait(-1);
exit(0);
}
}
运行结果:
先让程序运行到后台:执行gdb &
调试演示:
follow-fork-mode调试方法
使用方法:
set follow-fork-mode [parent|child]
(1)parent:fork之后继续调试父进程,子进程不受影响
(2)child::fork之后继续调试子进程,父进程不受影响
子进程调试方法:
启动gdb之后:
(gdb) set follow-fork-mode child
(1)在子进程相应可能会出错的地方设置断点
(2)设置detach-on-fork参数(用于指示gdb在fork之后,gdb的进程)
设置方法设置方法:set follow-fork-mode [parent|child] set detach-on-fork [on|off]
具体设置和功能如图所示:(GDB 6.6或以上版本)
常用的一些其他方法:
查询正在调试的进程:info inferiors
切换调试的进程: inferior <infer number>
添加新的调试进程: add-inferior [-copies n] [-exec executable] ,可以用file executable来分配给inferior可执行文件。
其他:remove-inferiors infno, detach inferior
简单演示(这个我们只是走一下调试的过程本身并没有错误):
//Makefile
Gdb:Gdb.c
gcc -o $@ $^ -g -lpthread
.PHONY:clean
clean:
rm -f Gdb
#include<stdio.h>
#include<pthread.h>
void* Thread(void* arg){
pid_t id=getpid();
printf("I am a thread, my pid is %d\n",id);
}
void childfunc()
{
pid_t id=getpid();
printf("I am a child , my pid is %d\n",id);
}
void parentfunc()
{
pid_t id=getpid();
int stat;
pthread_t pt;
printf("I am a parent , my pid is %d\n",id);
stat=pthread_create(&pt,NULL,Thread,NULL);
if(stat!=0){
printf("parent process can not create thread");
}
Thread(NULL);
sleep(2);
}
int main()
{
int id=fork();
if(id<0){
perror("fork");
return -1;
}
else if(id==0){//child
childfunc();
sleep(2);
}
else{//parent
parentfunc();
sleep(2);
}
return 0;
}
运行结果:
调试演示:
(1)调试主进程,block子进程
(2)切换到子进程
(3)调试主进程中产生的两个线程
两种调试方法比较
attach子进程方法:灵活强大,但需要添加额外代码,适合于各种复杂情况,特别是守护进程;
follow-fork-mode方法:方便易用,对系统内核和GDB版本有限制,适合于较为简单的多进程系