4.Linux调试器-gdb使用
4.1 背景
程序的发布方式有两种,debug模式和release模式
Linux gcc/g++出来的二进制程序,默认是release模式
要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项
4.2 开始使用
gdb binFile 退出: ctrl + d 或 quit 调试命令:
list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
list/l 函数名:列出某个函数的源代码。
r或run:运行程序。
n 或 next:单条执行。
s或step:进入函数调用
break(b) 行号:在某一行设置断点
break 函数名:在某个函数开头设置断点
info break :查看断点信息。
finish:执行到当前函数返回,然后挺下来等待命令
print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
p 变量:打印变量值。
set var:修改变量的值
continue(或c):从当前位置开始连续而非单步执行程序
run(或r):从开始连续而非单步执行程序
delete breakpoints:删除所有断点
delete breakpoints n:删除序号为n的断点
disable breakpoints:禁用断点
enable breakpoints:启用断点
info(或i) breakpoints:参看当前设置了哪些断点
display 变量名:跟踪查看一个变量,每次停下来都显示它的值
undisplay:取消对先前设置的那些变量的跟踪
until X行号:跳至X行
breaktrace(或bt):查看各级函数调用及参数
info(i) locals:查看当前栈帧局部变量的值
quit:退出gdb
4.3 理解
首先我们建立一个test.c文件,然后进行编译,代码如下:
但是我们要知道的是,在gcc或g++环境下默认生成的是动态链接release可执行文件,也就是说,他无法进行调试
我们可以在编译时加上-g,即指令gcc test.c -o testdebug -g
我们可以通过readelf -S 文件名的方式来查看两个可执行文件有何不同
默认生成的test可执行文件readelf -S test
生成的debug文件readelf -S testdebug
可以看到第二个文件是包含调试信息的,而第一个没有,所以第二个文件才能使用gdb进行调试
第一步我们输入指令gdb testdebug开始调试
需要注意的是gbd会记录最近一条命令,如果命令无变化,可以直接回车
比如我们先显示所有代码从头开始打印输入l 0
输入指令b 行号打断点
输入指令info b查看断点
输入指令d 断点编号删除断点,记住不是行号而是查看断点中的num编号
然后我们输入指令r开始调试,如果没设置断点,则直接运行结束
然后我们在执行函数前打一个断点,输入指令s为逐语句,输入n为逐过程
输入指令bt查看当前调用堆栈
输入指令p 变量名打印变量内容,或者指令p &变量名查看地址
如果你想常显示输入指令display 变量名,取消常显示输入指令undisplay
输入指令finish当前函数直接跑完再停下来
或者还可以输入指令until 行号直接跳转到某一行
再比如下面我们多打几个断点,执行命令c直接运行到下一个断点停下来
执行命令disable/enable 断点编号能关闭/打开断点
最后执行命令quit退出gdb调试工具
所有基础gdb调试命令就这些啦!
5.Linux项目自动化构建工具-make/Makefile
5.1 背景
会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
5.2 理解
首先我们先在c文件的同目录下建立一个Makefile文件(首字母大小写均可)
输入touch Makefile
我们打开Makefile文件输入下面信息后保存退出
1 tests:test.c 2 gcc test.c -o tests 3 .PHONY:clean 4 clean: 5 rm -f tests
这段代码中第一行test是我们自己定义的Makefile命令,指向test.c文件,其为依赖关系,只有执行对应的指令,才会执行相对应的依赖方法,第三行中的.PHONY为伪目标定义,使得clean命令可以重复执行。
下面我们看如何使用这两个makefile命令
如果我们直接输入make则直接默认执行第一条命令
当然我们也可以使用make tests
我们再执行第二条命令make clean
在这里我们需要注意的是,如果重复使用第一条命令,则会出现下面的情况
这是因为我们的tests文件修改时间晚于test.c文件,所以它默认tests文件不必重新生成
如果我们对test.c文件进行修改,再调用第一条命令,则可以重新调用
可以看到此时test.c文件最近修改时间比tests文件要更晚,此时我们再调用make指令
5.3 实例代码
首先我们建立三个文件,如下:
test.h
1 #pragma once 2 #include<stdio.h> 3 4 extern void show();
test.c
1 #include"test.h" 2 void show() 3 { 4 printf("hello linux!\n"); 5 }
code.c
1 #include"test.h" 2 3 int main() 4 { 5 show(); 6 return 0; 7 }
我们再将Makefile文件修改一下,如下:
1 test:test.o code.o 2 gcc -o tests test.o code.o 3 test.o:test.c 4 gcc -c test.c -o test.o 5 code.o:code.c 6 gcc -c code.c -o code.o 7 8 .PHONY:clean 9 clean: 10 rm -f *.o tests
保存后退出
我们输入命令make
执行命令make clean
最开始的test命令下对应的是两个.o文件,但是目录下并没有,那么它会继续向下寻找依赖关系,执行依赖方法。
5.4 原理
make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么,
1.make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2.如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“tests”这个文件,并把这个文件作为最终的目标文件。
3.如果tests文件不存在,或是tests所依赖的后面的test.o及code.o文件的文件修改时间要比tests这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成tests这个文件。
4.如果tests所依赖的test.o和code.o文件不存在,那么make会在当前文件中找目标为test.o和code.o文件的依赖性,如果找到则再根据那一个规则生成test.o和code.o文件。(这有点像一个堆栈的过程)
5.当然,你的C文件和H文件是存在的啦,于是make会生成 test.o和code.o文件,然后再用 test.o和code.o 文件声明make的终极任务,也就是执行文件tests了。
6.这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
7.在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
8.make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
5.5 项目清理
1.工程是需要被清理的
2.像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
3.但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
4.可以将我们的 tests 目标文件声明成伪目标,测试一下。
5.6 Linux第一个小程序-进度条
首先我们创建Makefile文件并进行编辑保存
1 proc:proc.c 2 gcc -o proc proc.c 3 .PHONY:clean 4 rm -f proc
创建proc.c文件
#include <stdio.h> #include <unistd.h> #include <string.h> int main() { int i = 0; char bar[102]; memset(bar, 0 ,sizeof(bar)); const char *lable="|/-\\"; while(i <= 100 ){ printf("[%-100s][%d%%][%c]\r", bar, i, lable[i%4]); fflush(stdout); bar[i++] = '#'; usleep(10000); } printf("\n"); return 0; }
保存好执行make
运行proc
6.Linux上git的使用
首先我们在命令行输入git -v查看是否安装git
若没有安装先执行sudo yum install -y git
因为作者使用的是GitHub托管代码,如果是gitee方法也大同小异
首先我们先建立一个项目仓库,用于测试
这两个选项都是用于建立新项目仓库
接下来我们拷贝该仓库的本地链接
下面我们回到Linux,进行第一步操作git clone 刚刚复制的仓库链接
接下来我们访问同名目录
现在我们可以将自己的代码上传,第一步上传代码到本地
git add test1.c
再写提交日志
git commit -m "这是一个测试代码"
再进行push上传,输入git push
需要注意的是,在push过程中,要输入用户名和密码,但是现在GitHub不支持密码方式push,所以我们需要创建一个令牌,用令牌代替密码即可成功push(getee用户不需要)
令牌设置
往下滑动可以找到
因为我这是创建过的,所以可能有些不同,没创建过的应该只有一个按钮
有可能直接就是下面的页面
记得复制好,放到记事本或者其他可以记录的地方,下一次进来就看不见了
下次上传直接用这个令牌就行啦
还有一种其他的情况,如果在别的设备上我们更改了GitHub上该仓库内容,可能我们直接进行push会碰到下面的情况
GitHub的仓库有了另一个文件,但是我的本地仓库并没有,此时我们建立一个新文件上传,看看会发生什么
可以看到我们上传失败了,这是因为本地仓库和平台出现了不匹配的情况,也就是发生了冲突,这个时候我们直接无脑
git pull
然后我们再进行上传
关于.gitignore文件我们可以自己进行编辑修改,主要目的就是在以目录文件形式上传时,过滤掉不需要的文件
我添加两个后缀名为.x和.X的信息,下面我们创建一个目录,包含一些文件
我们可以看到再进行commit命令时,本地已经将.x和.X文件忽略了
我们再看GitHub仓库同样没有上传
结语
有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!
Linux两万两千多字的文章,花了我很多时间和精力!!!
制作不易,如有不正之处敬请指出
感谢大家的来访,UU们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!