Linux环境基础开发工具使用(下)
Linux调试器-gdb使用
Ⅰ.背景
Linux下的程序调试与在Windows下的调试的思路是一样的,但是方法不一样。Windows下调试程序是在图形化界面进行的,而Linux下的调试是命令行调试。
● 程序的发布方式有两种,debug模式和release模式
● Linux gcc/g++出来的二进制程序,默认是release模式,该模式下无法进行调试。如果硬进行gdb调试的话,可能会出现下图所示状况
(gdb) l
No symbol table is loaded. Use the "file" command.
● 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上
-g
选项。例如
gcc -g test.c -o test_g
注意:
第一次使用gdb之前,需要安装gdb,指令是
sudo yum install -y gdb
debug模式发布的二进制程序比release模式发布的二进制程序所占空间小
其中test是release模式,test_g是debug模式
面试题:Linux下的默认行为有哪些?
gcc:默认行为
1.默认是动态链接的
2.默认是release模式vim:默认行为
默认打开就是命令模式
Ⅱ. gdb基本操作
💡tips:gdb会默认记录最近依次使用的指令,按回车可以再次执行该条指令
●
gdb binFile
:进入gdb调试二进制程序binFile
●gdb binFile 退出
: ctrl + d 或 quit 调试命令:
●list/l行号
:显示binFile源代码,接着上次的位置往下列,每次列10行。回车可以查看下10行。
●list/l 函数名
:列出某个函数的源代码。
●r或run
:运行程序。
●n 或 next
:单条执行。相当于VS上的逐过程调试
●s或step
:进入函数调用 。相当于VS上的逐语句调试
●break(b) 行号
:在某一行设置断点
●break 函数名
:在某个函数开头设置断点
●info break或者info b
:查看断点信息。
●finish
:执行到当前函数返回,然后挺下来等待命令
●print(p)
:打印表达式的值,通过表达式可以修改变量的值或者调用函数
●p 变量
:打印变量值。
●set var
:修改变量的值
●continue(或c)
:从当前位置开始连续而非单步执行程序
●run(或r)
:从开始连续而非单步执行程序
●delete breakpoints
:删除所有断点
●delete breakpoints n或者d n
:删除序号为n的断点
●disable breakpoints
:禁用断点
●disable breakpoints n
:禁用序号为n的断点
●enable breakpoints
:启用断点
●enable breakpoints n
:启用序号为n的断点
●info(或i) breakpoints
:参看当前设置了哪些断点
●display 变量名
:跟踪查看一个变量,每次停下来都显示它的值
●undisplay
:取消对先前设置的那些变量的跟踪
●until X行号
:跳至X行
●breaktrace(或bt)
:查看各级函数调用及参数,查看调用的堆栈
●info(i) locals
:查看当前栈帧局部变量的值
●quit
:退出gdb
注意:
调试程序完毕,或者调试中间过程,调试痕迹很重要,当调试一个大的项目,可能有几十个断点,某个断点调试完了,不要直接把它删除,而是暂时禁用它,等程序没有任何问题了,再把所有断点删除。
Linux项目自动化构建工具-make/Makefile
Ⅰ.背景
- ● 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力 ●
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
● makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
● make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual
C++的nmake,Linux下GNU的make。可见,makefile都成为了一 种在工程方面的编译方法。
● make是一条命令,makefile是一个文件,makefile内部要写的是依赖关系和依赖方法,两个搭配使用,完成项目自动化构建。
Ⅱ.基本使用
假如我现在编写了一个 test.c 文件,需要编译文件生成可执行程序:
方式一:直接使用 gcc 命令:
gcc test.c -o test
方式二:可以用 make 命令:想要使用 make 命令,需要创建一个 makefile 文件,如何创建 makefile 文件呢?先看看make命令的使用实例,见见猪跑!
Makefile文件:
1 test:test.c
2 gcc test.c -o test
3
4 .PHONY:clean
5 clean:
6 rm -f test
执行:
[hutao@hecs-414761 ~]$ make
gcc test.c -o test
这样也可以形成test这个可执行二进制程序
Ⅲ.依赖关系和依赖方法
依赖关系
● 上面的文件
test
,它依赖test.c
依赖方法
gcc test.c -o test
,就是与之对应的依赖关系
原理
● make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么,
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“
test
”这个文件,并把这个文件作为最终的目标文件。 - 如果
test
文件不存在,或是test
所依赖的后面的test.c
文件的文件修改时间要比test
这个文件新(可以用touch
测试),那么,他就会执行后面所定义的命令来生成test
这个文件。 - 如果
test
所依赖的test.c
文件不存在,那么make
会在当前文件中找目标为test.c
文件的依赖性,如果找到则再根据那一个规则生成test.c
文件。(这有点像一个堆栈的过程) - 当然,你的C文件和H文件是存在的啦,于是make会生成
test.c
文件,然后再用test.c
文件声明make
的终极任务,也就是执行文件test
了。 - 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么
make
就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make
根本不理。 make
只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。- 如果make后不加目标文件名,则会按照makefile中从上到下的第一个目标文件。
多文件编译
在一般的工程项目中,有着许多的源文件,面对着众多的源文件,可能会面临一些问题:
- 在执行依赖方法时,若不小心将源文件与生成的文件的位置写错或写反时,可能会将源代码直接覆盖掉了,从而找不到源代码了。
- 一个中大型的项目有着非常多的文件需要进行编译,每次需要重新生成可执行程序都要将各个文件的关系理清,然后分别进行编译,gcc/g++的指令也随之变长,这是一件非常麻烦的事,而且容易出错。
针对多文件的项目,就需要使用 make/makefile 了,这将降低编译文件的出错率,提高工作效率。
Ⅳ.makefile文件的创建与使用
1.在当前目录下创建一个名字叫“Makefile”或“makefile”的文件
make
是依赖于makefile
的,要运行make
,当前目录下就必须要有名为makefile
的文件。makefile
是阐述依赖关系和依赖方法的。makefile
文件命名只能是makefile
/Makefile
两种命令方法。
2.编写 makefile/Makefile 文件并保存。依赖关系与依赖方法相互搭配,依赖方法必须以 Tab
键开头。
3.编写好 makefile 后,若需要生成可执行程序,直接在命令行中输入 “make” 即可
Ⅴ.项目清理
- 工程是需要被清理的
- 像
clean
这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean
”,以此来清除所有的目标文件,以便重编译。 - 但是一般我们这种
clean
的目标文件,我们将它设置为伪目标,用.PHONY
修饰,伪目标的特性是,总是被执行的,不受其他因素限制
Ⅵ.关键字.PHONY
.PHONY
:用该关键字修饰的是一个伪目标,该目标总是被执行的,不受其他条件的越苏。上图中,生成可执行程序的依赖关系和依赖方法没有被 .PHONY
修饰,因此 make
命令执行生成了可执行程序之后,若源文件没有被修改过的话,再次执行 make
命令是不会执行成功的。
而需要被清理的可执行程序用 .PHONY
修饰了,所以每次执行 make clean
时,都是执行写入的依赖方法。
❓那么问题来了,makefile 是如何识别 exe/bin 是新的还是旧的,源文件有没有被更改过呢?
使用 stat
命令可以查看程序或文件的三个时间:
1. Access
表示最近一次访问该文件的时间
2. Modify
表示文件最近一次被修改的时间
3. Change
表示文件属性更改的最近时间
注意:
- 当你修改文件内容的时候,可能会导致该文件的大小也改变了,此时
Modify
时间和Change
时间会同时改变 - 当你访问一次某个文件时,它的
Access
时间可能不会改变。因为在我们进行文件操作的时候,访问文件的次数是要多余该文件的次数的,如果每次访问都要修改Access
时间的话,Access
时间就被修改的太频繁了,而修改Access
时间会访问磁盘,如果多次修改就会降低效率。因此,只有当你在短时间内连续多次访问该文件,它的Access
时间才会改变。
可执行程序是由源文件生成的,所以源文件的 Modify 时间要早于可执行程序的 Modify 时间,因此,gcc/g++ 只需要对比源文件与可执行程序的最近修改时间(Modify),便可知道程序是否需要重新生成。若可执行程序的 Modify 时间早于源文件的 Modify 时间,说明源文件被修改过,则需要编译程序生成新的可执行程序,否则不需要重新进行编译。
注意:若可执行程序是最新的,不能再次生成新的可执行程序。若需要再次生成新的可执行程序,可以使用 touch 文件名更新文件/程序的三个时间为当前执行命令的最新时间。
touch test.c // 将该文件的三个时间更新为当前的最新时间
Ⅶ.makefile
的推导规则
以程序生成的四个阶段:预处理、编译、汇编、链接为例:
当前文件夹中只有test.c和makefile这两个文件
makefile文件内容:
当执行make
指令时,会默认致生成第一个目标文件test
,而文件test
依赖文件test.o
,文件test.o
依赖文件test.s
,文件test.s
依赖文件test.i
,文件test.i
依赖文件test.c
,这样就会一直往下找,然后依次生成每个所依赖的文件