linux学习6,没有图形界面和鼠标,还能怎么找bug?gdb工具介绍
sinye56 2024-10-29 16:19 4 浏览 0 评论
前面几节,我们一起下载和编译了 linux 内核,也利用 busybox 制作了文件系统让编译好的内核成功跑起来了。本节介绍一下 linux 下的单步调试工具,方便我们后续调试 linux 内核。
linux 下程序开发不可避免遇到 bug
在程序开发中,调试和修复 bug 通常会占相当比例的时间,应该没有程序员能够一次性写出绝对完美的代码,除非程序只需要打印 hello world。在《C语言入门第12节,看书都懂,真让写代码却不知道如何入手咋办》一节,我们介绍过“增量式”开发,使用这种模式开发过程中,如果发现了错误,能够在很小的代码范围内查找并解决。然而不幸的是,并不是所有情况下都是“增量式”开发,比如接手别人的有 bug 的代码时,或者帮程序媛妹子们查找 bug 时,bug 可不是一下子就能找到的。
有些 bug 固然一眼就能看出,例如手误写错了初值。但是,有些 bug 可不容易看出,即使最终发现原来辛辛苦苦找到的 bug 原来如此低级。
程序员可能花了一周时间才发现,造成程序产生巨大错误的原因是,自己把应该是 a<=b 语句写成 a<b 了。
有时候找 bug 我们会希望,如果程序能够每执行一行,就停下来一次就好了,这样我们就能够知道这一行代码的执行结果究竟对不对了。程序员们通常称这种调试方法为“单步调试”。事实上,这并不难做到,相信习惯使用 IDE 开发的朋友们都应该使用过单步调试。
很多程序员从 windows 开发转向 linux 开发,最大的不适应就是惯用的 IDE 没法使用了,甚至连图形界面都没了,鼠标居然也用不上了。于是又去找能够在 linux 下使用的 IDE,最好还能和 windows 下的 IDE 功能一样。其实,在哪种环境下开发,只是习惯问题,如果我们一开始就是在 linux 这种没有界面,不用鼠标的环境下做程序开发,突然让在 windows 下开发,肯定也是不适应的。既然坚定了自己的嵌入式开发生涯,就没必要一直迁就于 windows。何况,并不是所有的 linux 环境都允许我们安装 IDE 的。
linux 下开发也能单步调试吗
linux 没有界面,鼠标也几乎没用,也能单步调试吗?答案显然是肯定的,GDB 工具的调试功能绝对不亚于各种 IDE。
gdb 就是一个调试工具,一般来说,GDB主要帮助你完成下面四个方面的功能:
- 启动程序,可以按照我们自定义的要求随心所欲的运行程序。
- 可让被调试的程序在指定的调置的断点处停住。(断点可以是条件表达式)
- 当程序被停住时,可以检查此时程序中所发生的事。
- 可以改变程序的某些行为,将一个 bug 产生的影响修正从而测试其他 bug。
gdb 工具的使用
我们还是从实例出发,介绍 GDB 工具的使用。现在需要开发一个程序:
要求先定义一个 square 函数用于求整数的平方值,然后在 main 函数中打印从 0 到 9 的平方值
这个要求是容易实现的,请看下面的 C 语言程序:
#include <stdio.h> int square(int i) { int s; s = i*i; return s; } int main() { int j, k; for(j=1; j<10; j++){ //不小心把j=0写成j=1了 k = square(j); printf("square(%d) = %d\n", j, k); } return 0; }
因为手误,程序员小明不小心把j=0写成j=1了。现在,我们使用 GDB 工具来单步调试这个程序,首先,编译这个程序:
# gcc t.c -g
-g 参数可以在编译过程中保留调试信息(主要是各种符号),便于调试。使用 GDB 的方式是简单的:
# gdb a.out
这样,我们就进入了 gdb 的环境中:
输入 run,也可以输入缩写 r,按回车:
(gdb) run Starting program: /lccRoot/C/tmp/a.out square(1) = 1 square(2) = 4 square(3) = 9 square(4) = 16 square(5) = 25 square(6) = 36 square(7) = 49 square(8) = 64 square(9) = 81 [Inferior 1 (process 20996) exited normally] (gdb)
可以看出,gdb 的 run 命令和它的字面意思一样,就是让程序跑起来。可以看出,程序少输出了一条内容。现在使用“单步调试”法来定位错误。首先,需要下断点(程序运行时,遇到断点会停下来),因为假设我们并不知道错误在哪里,所以断点就下在程序入口,也即 main 函数处:
(gdb) b main Breakpoint 1 at 0x40054b: file t.c, line 14. (gdb) r Starting program: /lccRoot/C/tmp/a.out Breakpoint 1, main () at t.c:14 14 for(j=1; j<10; j++){ (gdb) l 9 10 int main() 11 { 12 int j, k; 13 14 for(j=1; j<10; j++){ 15 k = square(j); 16 printf("square(%d) = %d\n", j, k); 17 } 18 (gdb)
下好断点后,输入 r 让程序跑起来。能够发现,这次程序并没有直接运行到最后,而是停在了第 14 行,输入 l(list),可以把第 14 行附近的代码打印出来。接着,我们输入 n(next),就可以一行一行的执行代码了。
(gdb) n 15 k = square(j); (gdb) n 16 printf("square(%d) = %d\n", j, k); (gdb) n square(1) = 1 14 for(j=1; j<10; j++){ (gdb)
到这里,发现程序输出的是 square(1) = 1,这显然是不对的,对照一下代码,错误的位置就很明显了。gdb 的单步调试不仅能发现错误,还能帮助我们理解代码:
(gdb) n 15 k = square(j); (gdb) s square (i=2) at t.c:6 6 s = i*i; (gdb) p s $4 = 32767 (gdb) p i $5 = 2 (gdb) n 7 return s; (gdb) p s $6 = 4
执行到 k=square(j); 时,输入 s(step into)就可以进入 square 函数的代码块,这时程序停在了 s=i* i; 处,输入 p s,可以把此时内存里 s 的值打印出来。发现 s 居然等于 32767,打印出 i,等于 2,这是为什么呢?因为此时 s=i* i; 还没有执行,s 作为局部变量没有定义初值,所以此时它的值是未知的。输入 n,程序执行完 s=i* i;,发现 s 等于 4,正常了。
关于 C 语言的局部变量,可参考《局部变量为何只能在函数中使用,必须初始化吗》一节。
输入 bt:
(gdb) bt #0 square (i=2) at t.c:7 #1 0x000000000040055e in main () at t.c:15 (gdb)
可以看出,此时程序是从 main 函数进入 square 函数的。gdb 的功能显然还不止于此,本节仅作为抛砖引玉的作用。以后我们调试 linux 内核时,会继续使用 GDB 的。
GDB 的常用命令
gdb 全速运行
- (gdb)run,也可只输入 r,程序会开始运行,停在第一个断点处。
- (gdb)continue,也可只输入 c,程序继续运行,停在下一个断点处。
gdb 添加搜索代码源文件路径
- (gdb)directory [path],也可输入 dir [path],将 path 添加到搜索目录中。
gdb 打印代码
- gdb + 可执行程序即可进入 gdb 调试:
- 输入 l 可以打印出代码,如果想输出指定行号的代码,可以加上数字,例如 l 5。
gdb 断点
下断点有两种方式:
* (gdb)break(也可只输入b) + 函数名
* (gdb)break + 行号
* (gdb)break + 条件。这个命令必须在变量i被定义之后才会成功运行,为了解决这个问题,首先在变量 i 被定义的后一行设置中断,然后使用run命令运行程序,程序暂停后就可以使用watch i==99设置断点了。例如 break 7 if i==99, watch i==99
查看断点
- 显示当前gdb的断点信息:(gdb) info break
- 删除指定的某个断点:(gdb) delete breakpoint 1
- 该命令将会删除编号为1的断点,如果不带编号参数,将删除所有的断点:(gdb) delete breakpoint
- 禁止使用某个断点:(gdb) disable breakpoint 1,该命令将禁止断点1,同时断点信息的 (Enb)域将变为 n
- 允许使用某个断点:(gdb) enable breakpoint 1,该命令将允许断点1,同时断点信息的 (Enb)域将变为 y
- 清除源文件中某一代码行上的所有断点:(gdb)clear number
- >注:number 为源文件的某个代码行的行号
变量检查赋值编辑
- whatis:识别数组或变量的类型
- ptype:比whatis的功能更强,他可以提供一个结构的定义
- set variable = value:将值赋予变量
- print variable = value or p variable = value : 除了显示一个变量的值外,还可以用来赋值
单步执行编辑
- next 不进入的单步执行
- step 进入的单步执行如果已经进入了某函数,而想退出该函数返回到它的调用函数中,可使用命令finish
函数调用编辑
- call name 调用和执行一个函数
- (gdb) call gen_and_sork(1234,1,0)
- (gdb) call printf(“abcd”)
- =4
- finish 结束执行当前函数,显示其返回值(如果有的话)
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
相关推荐
- Linux两种光驱自动挂载的方法
-
环境:CentOS6.4西昆云服务器方式一修改fstab文件/etc/fstab是系统保存文件系统信息?静态文件,每一行描述一个文件系统;系统每次启动会读取此文件信息以确定需要挂载哪些文件系统。参...
- linux系统运维,挂载和分区概念太难?在虚机下操作一次全掌握
-
虚拟机的好处就是可以模拟和学习生产环境的一切操作,假如我们还不熟悉磁盘操作,那先在虚机环境下多操作几次。这次来练习下硬盘扩容操作。虚拟机环境:centos8vm11linux设备命名规则在linux中...
- Linux 挂载 NFS 外部存储 (mount 和 /etc/fstab)
-
mount:手工挂载,下次重启需再重新挂载,操作命令:mount-tnfs-ooptionsserver:/remote/export/local/directory上面命令中,本地目录...
- 在Linux中如何设置自动挂载特定文件系统(示例)
-
Linux...
- Linux环境中的绑定挂载(bind mount)
-
简介:Linux中的mount命令是一个特殊的指令,主要用于挂载文件目录。而绑定挂载(bindmount)命令更为特别。mount的bind选项将第一个目录克隆到第二个。一个目录中的改变将会在...
- Linux挂载CIFS共享 临时挂载 1. 首先
-
如何解决服务器存储空间不足的问题?大家好,欢迎回来。在上一期视频中,我为大家介绍了如何利用Linux挂载来扩容服务器存储空间。这一期视频,我将以Linux为例,教大家如何进行扩容。群辉使用的是Linu...
- Linux 硬盘挂载(服务器重启自动挂载)
-
1、先查看目前机器上有几块硬盘,及已挂载磁盘:fdisk-l能够查看到当前主机上已连接上的磁盘,以及已经分割的磁盘分区。(下面以/dev/vdb磁盘进行分区、挂载为例,挂载点设置为/data)df...
- linux 挂载磁盘
-
在Linux中挂载硬盘的步骤如下:...
- 笨小猪教您Linux磁盘挂载
-
本教程针对Linux系统比较熟悉或者想学习Linux基础的用户朋友,本教程操作起来比较傻瓜式,跟着步骤就会操作,本文使用的工具是XShell同时多多注意空格(文中会有提示)。【问答】什么是磁盘挂载?答...
- Linux 磁盘挂载和docker安装命令
-
本篇给大家介绍Linux磁盘挂载和docker安装的相关内容,Linux服务器的操作是一个手熟的过程,一些不常用的命令隔断时间就忘记了,熟话说好记性不如烂笔头,还需在平时的工作中多练习记录。...
- Linux设置开机自动挂载分区
-
有时候,我们在安装完Linux系统之后,可能在使用过程中添加硬盘或者分区进行使用,这时候就需要手动把磁盘分区挂载到某个路径,但是开机之后就会消失,需要重新挂载,非常麻烦,那么我们应该如何设置开机自动挂...
- 在linux挂载一个新硬盘的完整步骤
-
以下是在Linux中挂载新原始磁盘的完整步骤,包括分区、创建文件系统以及使用UUID在/etc/fstab中启动时挂载磁盘:将新的原始磁盘连接到Linux系统并打开电源。运行以下命令,...
- Linux系统如何挂载exFAT分区
-
简介:Linux系统中不能像Windows系统那样自动识别加载新设备,需要手动识别,手动加载。Linux中一切皆文件。文件通过一个很大的文件树来组织,文件树的根目录是:/,从根目开始录逐级展开。这些文...
- Linux系统挂载硬盘
-
fdisk-l查看可挂载的磁盘都有哪些df-h查看已经挂载的磁盘...
- WSL2发布,如何在Win10中挂载Linux文件系统
-
WSL2是最新版本的架构,它为Windows子系统提供支持,使其能够在Windows上运行ELF64Linux二进制文件。通过最近的更新,它允许使用Linux文件系统访问存储在硬盘中的文件。如果你...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- 关闭防火墙linux (53)