Linux系列
文章目录
- Linux系列
- 前言
- 一、gdb的使用背景
- 二、gdb的使用
- 总结
本篇主要针对小白讲解,可以很多地方比较咯嗦
前言
GDB是Linux下一款强大的调试工具。GDB可以调试C、C++、Java等语言,对于在Linux下工作的程序员来说,GDB是必不可少的调试工具。
本篇我将以c程序,进行调试和讲解
一、gdb的使用背景
相较于Windows下的VS编译器中的点击调试,gdb调试器因不具备图形化界面,故而只能采用纯命令行的形式进行调试,这样也就意味着学习难度要高于VS.
在学习gdb的使用之前,我们首先要知道,只有【debug】环境下编译的可执行程序才可以被调试,而在Linux中gcc编译器默认编译环境为【release】,要编译生成【debug】版的可执行程序,我们可以使用gcc编译器配合‘-g’选项,生成【debug】文件。
二、gdb的使用
下面为test.c文件中的代码,我接下来使用它讲解,在有不同场景需求时,会进行更换,大家仔细看一下调用逻辑
1 #include<stdio.h>2 void func2()3 {4 printf("Hello Linux\n");5 }6 void func1()7 {8 int i,sum=0;9 for(i=0;i<10;i++)10 {11 sum+=i;12 }13 func2();14 printf("这是func1\n");15 }16 int func(int x)17 {18 func1(); 19 return x+100;20 }21 int main()22 {23 int a=0;24 printf("%d\n",a);25 int b =func(a);26 printf("%d\n",b);
下面为Makefile文件中的指令
1 test:test.c2 gcc -o $@ $^ -g3 .PHONY:clear4 clear:5 rm -rf test
生成【debug】文件开始调试。
进入调试阶段
语法:gdb 可执行程序
显示代码信息
语法l(list缩写) 行号/函数名-----每次显示十行
从上面两次执行结果可以看出,这个指令并不是从我们给的行号开始显示,而是在给的行号的上下一共显示十行。
指定函数显示。
若想从代码开始行显示,我们只需要‘l 0’即可:
我们可以看到当第一次执行,第二次再执行就可以不输入指令,直接回车即可,这是因为gdb自动记录最近一条指令.
启动程序
语法:r(run)
图中为程序执行结果,因为没有设置断点,所以程序执行到结束。
设置断点
语法:b(breakpoint) 行号/函数名----指定在某一行打断点
这里我在代码的23、25、26行设置了断点。
查看断点
语法:info b
上面为我们设置的断点信息。
删除指点断点
语法:d 要删除断点编号
删除全部断点
语法:d breakpoints
启用/禁用断点
语法:disable 编号-----禁用断点
这里可以看到,我重新设置的断点编号是从4开始,而不是从1,这是因为我们没有重新启动gdb,所以编号会从上次的编号继续向下。
对比指令执行前可以发现,【Enb】从y状态变为n状态。
这里依然可以配合breakpoints使用,大家可以自己尝试
语法:enable 编号----启用断点
调试
语法:n------逐过程调试(不会进入函数体内部)
为了更好的体现这个效果我,我将编号7的断点删除了
r执行到达第一个断点处。
此时可以看到程序执行到断点处,并没有进入func函数内部,而是直接向下执行。
语法:s----逐语句调试,会进入调用函数内部
当使用s执行调试时,程序会进入调用函数内部。
显示/追踪变量信息
语法:p 变量名-----将变量信息显示在显示器上
也可以配合取地址符,显示变量地址。
语法:display 变量名 ----追踪显示变量(执行任何一条指令后,都将指定的变量显示出来)
语法:undisplay 编号------取消常显示
刚刚取消了忘截图,又重新设置的,所以编号变为了2
此时再执行其他指令,i就不会常显示了。
查看函数调用
语法:bt----可以看到函数逐级调用的过程
将上图和我们调试的代码结合起来可以看出,bt命令显示出的编号,和我们代码逻辑中函数调用顺序是相反的,而这种情况就是一个压栈的过程。你可以自己尝试,整个函数调用链都是遵循这个逻辑的,所以我们就可以使用这个命令来查看栈帧的创建情况。
修改变量值
语法:set var 变量名=指定值
例如:当我们在一个循环中,想查看当i=1000时的情况,而让程序一点一点跑就太麻烦了,这是我们就可以使用这个命令,进行指定设置。
for(i=0;i<10;i++){sum+=i;}
可以看到我们通过指令,直接将i的值设置为了5.
指定行调转
语法:until 行号-----------这里的行号指的是,代码所处行
当前调试程序执行至第九行。
1 #include<stdio.h>
2 void func2()
3 {
4 printf("Hello Linux\n");
5 }
6 void func1()
7 {
8 int i,sum=0;
9 for(i=0;i<10;i++)
10 {
11 sum+=i;
12 }
13 func2();
14 printf("这是func1\n");
15 }
16 int func(int x)
17 {
18 func1();
19 return x+100;
20 }
21 int main()
22 {
23 int a=0;
24 printf("%d\n",a);
25 int b =func(a);
26 printf("%d\n",b);
27 return 0;
28 }
我们直接程序跳转的13行处,执行func2函数:
强制执行函数
语法:finish--------强制执行所处函数,并返回到调用该函数的位置
为了演示,我们重新开始调试。
此时函数执行至func1函数,我们使用命令将该函数强制执行完。
可以看到此时,程序将func1函数强制执行完毕,并返回至调用处等待指令。
跳过结点
语法:c--------当我们在调试时,想要从一个断点直接跳转到下一个断点想对断,可以使用这个命令
大家结合上面代码的调用逻辑分析
这是当前所设置的断点,我期望从23行代码,直接运行至8行,中间的程序不让他显示的执行。(这是使用n或s命令无法办到的)
总结
当大家对指令比较熟悉时,gdb的使用还是比较简单的,以上是我个人,经常使用的命令,是对标VS下编译的调试功能来介绍的,如果这不能满足你的要求,可以搜一些更高级的用法。