【Linux学习】基础开发工具
🐱作者:一只大喵咪1201
🐱专栏:《Linux学习》
🔥格言:你只管努力,剩下的交给时间!
Linux基础开发工具
- 💥软件包管理器yum
- 💢list(查看)
- 💢list(安装)
- 💢remove(卸载)
- 💥编译器vim
- 💢vim的指令
- 命令模式
- 插入模式
- 底行模式
- 💢vim的简单配置
- 💥gcc/g++
- 💢预处理
- 💢编译
- 💢汇编
- 💥链接
- 💢动态链接
- 💢静态链接
- 💥makefile和make
- 💢依赖关系
- 💢依赖方法
- 💢伪目标
- 💥Linux上的第一个程序
- 💥版本控制器git
- 💢git的操作
- 💢.gitignore文件配置
- 💥gdb
- 💥总结
在前面Linux的学习中,本喵将的都是一些命令之类的东西,命令其实就是一种工具,Linux开发人员写好的可执行程序,我们使用这些命令就相当于在执行人家的程序,也就是在使用工具。
在这篇文章中,本喵会介绍一些Linux环境下的基础开发工具,是用来帮助我们写代码的。
💥软件包管理器yum
背景知识:
- 在Linux下安装软件的时候,通常的办法就是下载好程序的源代码,然后编译,最后得到可执行程序,我们使用的就是这个可执行程序
- 但是下载源代码再编译很不方便,于是Linux社区的开发人员就把源代码编译好,将生成的可执行程序放在一个地方。
- 此时再安装软件的时候只需要直接下载这个可执行程序即可。这个可执行程序存放的地方是Linux社区的一个服务器,通过链接访问到这个服务器,再根据安装软件的信息就可以下载对应的软件。
- 我们在手机上安装app也是这个道理,通过应用商店去下载软件,而应用商店则负责与手机生成厂家的服务器进行链接,再通过网络将服务器上的可执行程序下载到手机上。
- Linux上yum的功能和应用商店是一样的,它内嵌了Linux服务器的链接,在下载软件的时候就可以直接找到对应的可执行程序并且下载。
所以说,yum就是Linux系统上的一个软件应用商店。
我们使用yum时,主要就是用到它的三个功能,list(查看),install(安装),remove(删除)。下面本喵给大家做一下具体的演示。
💢list(查看)
现在我们来安装一个工具,叫做rzsz,它的功能是可以将Windows上的文件和Linux上的文件相互上传。
- 用法:yum list | grep 软件名
- 功能:查看软件的信息
如上图中,使用yum查看软件lrzsz时,打印出来的是这个软件的信息。
- lrzsz是软件名,后缀中的x86_64表示是64位的可执行程序,再后面的数字是版本号。
这里有一个前提,就是在使用yum之前需要知道要查看的软件叫什么,安装也是这样。这个只能通过经验的积累以及根据需要查阅资料得知。
💢list(安装)
- 用法:yum install -y 软件名
- 功能:安装对应的软件
- 常用项:
-y:省去确认,如果不加的话,系统会不停询问你是否安装,加上以后表示确认安装,不用再询问我。
有些软件是普通用户没有权限安装的,此时就需要root用户来安装,或者是使用sudo进行短暂的提权。
上图中,由于本喵的系统中已经安装好了,所以系统最后提示没有安装。如果你的系统中没有,这里就是会提示complete!,表示安装完成。
此时在命令行中输入rz,就会弹出Windows的文件选择窗口。
同样的,在Linux上输入指令sz 文件名就会弹出Windows的文件路径选择窗口来选择文件的存放位置。
再演示一个本喵系统上没有的软件。
可以看到,最后系统提示安装完成了。当然在安装过程中还有许多信息,比如文件大小了,安装进度了这些本喵就不截图出来了,大家可以自己去尝试。
此时在命令行中输入sl,就会有一个小火车跑出来,这个就是软件,也就是工具sl的作用。
💢remove(卸载)
- 用法:yum remove -y 软件名
- 功能:卸载对应的软件
- 常用选项:
-y:作用和安装的时候一样。
在命令行中输入卸载指令后,软件sl就被卸载了,最后系统会提示Complete!表示卸载完成。
以上就是yum工具的基本使用。
💥编译器vim
本喵之前一直都是在使用命令行,如果我们写代码的话显然是不能在命令行上写代码的。所以我们就需要一个编辑器,就像Windows上的记事本或者是word一样,来编辑文字或者代码。
在Linux上我们使用的是vim编辑器,它可以称为编辑器的天花板,因为功能有很多,而且比较复杂。
在使用之前,首先我们需要安装一个vim编辑器,安装办法参考上文,这个文件的名字就叫做vim。
在安装好vim编辑器以后,创建一个文件,再使用vim打开,如上图。
打开以后,是这样一个界面,然后在光标处开始边界我们创建的这个文件。但是你会发现根本无法输入字符。此时我们就需要来了解一些vim编辑器的使用。
vim编辑器有12种模式,但是我们主要使用的就三种,掌握这三种就可以使用vim了。
- 命令行模式
控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode。vim一打开以后,默认的模式就是命令行模式。
- 插入模式
只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。在命令行模式下,按键盘上的i建,或者a和o都可以进入插入模式。
- 底行模式
文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。 在命令模式下,shift+: 即可进入该模式。要查看你的所有模式:打开vim,底行模式直接输入:help vim-modes。
以上就是三种模式的主要功能,无论在任何模式下,只有无脑按ESc就可以进入命令行模式,然后再从命令行模式进入插入模式或者底行模式。
注意:
插入模式和底行模式之间是不能之间转换的,必须通过命令行模式。
💢vim的指令
命令模式
打开vim时默认的就是命令模式,如果处于其他模式,只需要按ESc就可以回到命令模式。
光标移动:
命令 | 功能 |
---|---|
i | 进入插入模式,从光标当前位置输入字符 |
a | 进入插入模式,从光标当前位置的下一个位置输入字符 |
o | 进入插入模式,插入新的一行开始输入字符 |
ESc | 从其他模式进入命令模式 |
h,j,k,l | 对应光标的左下上右 |
G | 光标移动到文本的末尾 |
nG | 比如6G,光标跳转到第6行 |
gg | 光标移动到文本的开始 |
^ | 光标移动到当前行的行首 |
$ | 光标移动到当前行的末尾 |
删除文字:
命令 | 功能 |
---|---|
x | 小写x,每按一次,删除光标所在位置的一个字符 |
nx | 比如6x,删除从光标开始的6个字符 |
X | 大写X,每按一次,删除光标所在位置的前一个字符 |
nX | 比如6X,删除光标所在位置之前的6个字符 |
dd | 删除光标所在的当前行 |
ndd | 比如6dd,删除光标所在行开始的下面6行 |
复制剪切:
命令 | 功能 |
---|---|
yy | 复制光标所在行 |
nyy | 比如6yy,复制包括当前行向下数的6行 |
p | 粘贴yy复制的内容 |
np | 比如6pp,将复制内容粘贴6次 |
yw | 复制光标所在位置到行尾的字符 |
nyw | 比如6yw,复制光标所在位置开始的6个字符 |
替换:
命令 | 功能 |
---|---|
r | 替换光标所在处的字符 |
R | 替换光标所在位置的字符,直到行尾 |
撤销:
命令 | 功能 |
---|---|
u | 撤销上次执行的操作,多按几次可以撤销多个操作 |
ctrl+r | 恢复撤销 |
更改:
命令 | 功能 |
---|---|
cw | 更改光标所在位置到行尾的字符 |
cnw | 比如c6w,更改光标所在位置开始的6个字 |
插入模式
在命令模式下,按i,a,o都可以计入插入模式。
插入模式就和我们在Windows中的记事本一样,之间输入字符就可以。
底行模式
在命令模式下,按:也就是(shift + 封号)进入底行模式。
命令 | 功能 |
---|---|
set nu | 在文本中的每一行之前显式行号 |
set nonu | 去掉文本中的行号 |
vs 文件名 | 分屏,同时打开多个文件编辑,按ctrl+ww转换光标所在文件 |
wq | 保存内容,并且退出vim编辑器 |
q | 退出vim编辑器 |
w | 保存内容 |
q! | 强制退出vim编辑器 |
w! | 强制保存内容 |
/+字符 | 在文本中搜索字符 |
以上就是vim的基本操作,虽然很多,也比较难记,只要使用的多了就熟练了。
💢vim的简单配置
我们使用vim编辑器主要就是为了写代码,在Windows下的VS2019中,在使用库函头文件等内容时,会出现代码提示,而且输完一行语句以后,按回车会自动缩进,并且还有代码高亮等等,使我们写代码更加发便高效,以及美观。
但是此时Linux上的vim编辑器就没有上面的功能,在上面写代码就像在记事本上写代码一样。
但是vim编辑器是可以配置的,可以配置成和本喵使用的vs2019一样,有代码高亮,提示,缩进等内容。
在每个用户的家目录下都有一个.vimrc文件,如果没有的话就自己创建一个,在每次使用vim的时候,会从这个文件中读取配置。
而我们只需要在里面加上自己想要的配置,就可以让vim编辑器和vs2019一样。
使用vim编辑器打开.vimrc文件进行编辑,在文件中写入set nu。上面本喵讲过在底行模式中输入该指令,会使文本中的内容在前面加上行号。然后保存并退出。
再使用vim编辑器打开我们前面创建的code.c文件时,就会看到,每一行文字的前面都有行号。
配置代码中的语法高亮以及缩进,提示等时,只需要像上面一样,在家目录中的.vimrc文件中写对应的语句即可。大家可以自行上网查一下vim的配置,然后将对应的代码写出vimrc中就可以,这里本喵就不一一列举了。
这是本喵配置好后的样子。
在这里,我们只需要明白vim配置的原理是什么,原理就是启动vim的时候都会读取家目录下的.vimrc文件中的配置。至于怎么配置,这就是个时间问题,完全可以从网上查的到。
注意:
- vim的配置,是一人一份的,每个用户都可以配置自己的vim,并且不影响别人。
- 虽然每个用户用的vim都是同一个,但是配置文件.vimrc每个用户是不同的,而且在每个用户家目录下
- 所谓vim配置就是修改自己的.vimrc文件,切记不要修改不属于你的配置文件。
- 强烈不建议使用root用户来配置.vimrc
💥gcc/g++
现在我们可以使用vim来写代码了,但是写完该怎么运行呢?在Windows的时候,本喵使用vs2019之间点击运行,然后在控制台就能看到自己的代码效果了,那么在Linux系统下呢?
在本喵的文章程序环境和预处理中,本喵曾讲过,一个源文件(.c)变成可执行程序(.exe)需要经历四个步骤,预处理,编译,汇编,链接。
Windows中的vs2019是一个集成软件,它可以一键就实现这4个过程,但是在Linux中需要专门的工具来实现,就是编译器,C语言的编译器叫gcc,C++的编译器叫g++。
这是本喵用vim编辑器写好的代码。下面我们来运行它。
使用gcc编译器来编译写的code.c源文件,然后会在当前目录下生成一个可执行程序,再运行这个可执行程序就会显示出程序的执行结果。
- 用法:gcc 选项 源文件 -o 目标文件名
- 功能:将源文件编程成可执行程序
- 常用选项:
-E:从当前文件开始,进行完预处理停止,生成的文件我们一般加后缀.i。
-S:从当前文件开始,进行完编译停止,生成的文件我们一般加后缀.s。
-c:从当前文件开始,进行完汇编停止,生成的文件我们一般加后缀.o。
下面我们来回顾一下源文件到可执行程序的过程。
这个过程包括俩个大过程,分别是编译和链接,其中链接又包括预处理,编译,汇编三部分。
💢预处理
预处理的作用:
- 将头文件展开,复制到源文件中
- 去掉注释
- 进行宏替换
- 处理条件编译
将我们写好源文件只进行预处理,在当前目录下会生成一个test.i的文件。
对比俩个文件:左边的是test.c源文件,右边的是test.i进行了预处理的文件。
- 预处理后的文件,没有了包含头文件的语句,因为此时头文件被展开了,并且复制进了test.i文件中,可以看到,右边的文件我们写的部分是从841行开始的,之前的内容都是头文件展开的结果。
- 宏定义的标识符常量M被替换了了20。
- 没有宏定义SHOW,所以只保留了#else后的语句,进行了条件编译。
- 左边文件中的注释,在右边都不见了,进行了去注释。
💢编译
编译器的作用:将预处理生成的.i文件中的内容都转换成汇编语言。
将预处理后的test.i文件进行汇编,在当前目录中会生成test.s文件。
使用vim编辑器打开test.s文件后,可以看到,里面的内容是汇编代码。
💢汇编
汇编的作用:将编译生成的.s文件中的内容都转换成二进制机器码,因为计算机只能读懂机器码。
将编译生成的.s文件中的内容全部转换成二进制机器码。
使用指令od 来查看test.o中的内容,将二进制的机器码以16进制的形式显示了出来。
最后使用gcc将汇编后的.o目标文件进行链接,就会生成可执行程序test。
其中-o是指定生成文件的名字,如果没有-o选项,默认会生成a.out文件。
链接的时候,使用gcc不用加任何的选项。
如果是程序是一个C++程序,只需要把编译器gcc换成g++就可以。
💥链接
上面本喵详细讲解了程序翻译过程中编译的三部分,预处理,编译,汇编的作用,以及效果,还剩下最后一部分链接。
在上面本喵的C程序中,并没有定义printf函数的实现,而且也知识包含了stdio.h的头文件,但在最后执行的结果却是符合我们的预期的,这是怎么实现的呢?答案就是链接。
在系统中,有一个库,这个库中放着C标准库中的所有函数,通过头文件,得知使用方法后,在C程序中执行到库函数的时候,就会链接库中的库函数,库函数也是.o目标文件,将我们自己的程序和库函数链接到一起,就会生成可执行程序.exe。
系统中的C标准库名为:libc.so,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找。
链接分为动态链接和静态链接。
💢动态链接
- 上面C程序中,除了库函数printf以外,其他的内容都是我们自己的。
- 在处理这个程序的时候,我们自己的内容正常执行
- 当执行到库函数的时候由运行时的链接文件跳转到系统中的标准库中,去链接标准库中库函数的.o文件
- 当链接完成后就生成可执行程序。
动态链接的动态就体现在,用到的库函数需要跳转到动态库中去链接对应的.o文件。
其中发生动态链接的标准库就叫做动态库。
💢静态链接
- 静态库与动态库相反,它不发生跳转。
- 自己的程序内容正常执行,当执行到库函数的时候,会将标准库中对应的库函数内容全部复制到程序中库函数的地方。
- 之后再生成可执行文件。
静态链接的静态就体现在,执行库函数的时候,不发生跳转,而是将库函数中的内容复制到自己的程序中。
其中,静态链接的库就叫做静态库。静态库和动态库并不是一个库。
补充知识:
- 动态库的命名规则:libXXXXXX.so,其中前缀lib表示库,后缀.so表示动态库。去掉lib和.so后剩下的内容就是动态库的名字。
- 静态库的命名规则:libYYYYYY.a,其中前缀lib表示库,后缀.a表示静态库。去掉lib和.a后剩下的内容就是静态库的名字。
那么,Linux下程序编译成可执行程序时链接的是动态库函数静态库呢?
将源文件进行编译链接后,生成可执行程序a.out,再通过指令ldd来查看可执行程序的属性。
- 图中绿色长框中的内容表明这是一个动态库。
- 我们可以看到动态库的文件名libc.so.6,按照动态库的命名规则,去除掉前缀lib和后缀.os.6后,还剩下c,表面这是一个c语言的动态库。
- 后面括号中的16进制数字表示的是动态库在系统中的地址。
根据上面所说,链接的库是动态库,那么一定就属于是动态链接了。
再使用file来查看一下可执行文件的属性,虽然出来的信息我们不认识,但是可以看到里面有说这是一个动态链接,如上图中的绿色框。
在编译test.cd的时候,gcc不加任何选项,之间生成的就是可执行程序,而且也没有指定生成的文件名。
所以说,Linux系统中,翻译程序时,默认采用的动态链接的方式。
那么如果我就想让链接采用静态链接呢?该怎么办呢?
如上图中,在使用gcc编译源文件的时候,在指令的最后面加上-static选项,此时就是采用的静态链接的方式。
使用file查看生成的可执行程序文件的属性,可以看到,系统显示出来这是一个静态链接,如上图绿色框。
- 如果系统上没有静态库的话,需要按照一个,输入指令:
sudo yum install -y glibc-static
那么动态链接和静态链接的区别是什么呢?
- 静态链接生成的可执行程序test.a的大小是861288字节
- 动态链接生成的可执行程序test.so的大小是8360字节
- 可以看到静态链接生成的可执行程序比动态链接生成的可执行程序大很多,大100倍。
因为静态链接是将库函数复制过来,动态链接是跳转到库中去调用库函数,所以静态链接会比动态链接生成的可执行程序大的多。
说明:
- 静态库或者动态库一个系统只有一个,所有使用系统的用户都在使用同一个库。
- 静态链接后的可执行程序不会受到静态库升级或者被删除的影响,而动态库会受到影响。
- 系统为了我们编程,提供了标准库的头文件.h,和动静态库.so/.a,头文件是为了告诉我们怎么使用库函数,动静态库中提供了库函数的具体实现方法。
相比较之下,我们肯定愿意去下载一个比较小的可执行程序,所以动态链接的使用比静态链接更多。
💥makefile和make
makefile是一个文件,而make是一个工具,也就是一个可执行程序,是用来解释makefile文件中的内容然后告诉操作系统的。
在test.c源文件所在目录下创建一个makefile文件。
使用vim打开以后,插入上图中的内容,注意,第二行前面的空白必须是一个tab键,不能是俩个空格,或者四个空格。
上图中只需要输入make就完成了编译,可以看到,输入make以后,我们在makefile文件中的一部分内容被显示到了屏幕上,并且也生成了test可执行文件。不用再输入gcc test.c -o test这么长的指令了。
- makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
在上面,本喵给大家演示了预处理,编译,汇编各个阶段完成后生成的文件,需要在使用gcc的时候加上选项等,如果多个文件都需这样的操作就非常繁琐,此时就可以用make来实现。
使用vim打开makefile文件,在里面写如上内容,就是使用gcc编译器让程序的翻译过程单步执行。
此时再使用make,系统自动执行预处理,编译汇编,链接等步骤,不需要我们再一条一条的写了,而且也生成了对应的文件。
当一个工程很复杂的时候,就需要使用make来处理程序,因为各种源文件所处目录不同,编译顺序等等都不同,如过一句一句写的会效率会非常的低下,此时我们就编写一个makefile文件使用make来处理。
会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
💢依赖关系
接下来本喵告诉大家怎么写makefile文件,首先必须知道的一个概念是依赖关系。
红色框中的内容就是依赖关系,依赖方在冒号的左边,被依赖方在冒号的右边。
- test可执行程序依赖于test.o目标文件,因为只有先有了test.o,才能再有test。
- test.o目标文件依赖于test.s汇编文件,因为只有先有了test.s,才能再有test.o。
- test.s汇编文件依赖于test.i预处理后的文件,因为只有先有了test.i,才能再有test.s。
- test.i预处理后的文件依赖于test.c源文件,因为只有有了test.c,才能再有test.i。
这就是依赖关系,并且依赖关系并不是相互的,就像上图中的文件,删除掉test.i文件,其他文件也可以存在。
💢依赖方法
在知道依赖关系以后还需要知道依赖方法,也就是通过什么样的方法实现的依赖关系。
上图中红色框中的内容就是依赖方法。
- test之所以依赖test.o,是因为test是通过test.o链接以后生成的,所以此时的依赖方法就是红色框中的链接。
- test.o之所以依赖test.s,是因为test.o是通过test.s汇编后生成的,所以此时的依赖方法就是红色框中的汇编。
- test.s之所以依赖test.i,是因为test.o是通过test.i编译后生成的,所以此时的依赖方法就是红色框中的编译。
- test.i之所以依赖test.c,是因为test.i是通过test.c预处理后生成的,所以此时的依赖方法就是红色框中的预处理。
这就是依赖方法,通俗点说就是通过什么样的方法让双方产生了依赖。
在编写makefile文件的时候,依赖关系和依赖方法是必须写的,而且是一一对应的。
在上图中,make解释makefile中的内容时,采用顺序和递归相似,是从最后面向前面解释的。
同样,也可以通过makefile和make来删除文件。
在makefile文件中添加红色框中中的内容。
此时输入红色框中的指令make clean,系统就会自动执行删除命令,删除翻译过程中间的三个文件,也就是执行在makefile中新添加的内容。
💢伪目标
- 被.PHONY修饰的对象就是一个伪目标。
- 伪目标总是被执行。
怎么理解上面的话呢?
上图中,使用make来编译test.c,生成可执行文件test。然后再使用make来编译test.c时,系统显示可执行文件test已经是最新的了,并没有执行makefile中的指令,这是为什么呢?
上图中,使用指令stat test后,会显示出文件的属性,其中化绿色的三行分别是文件的访问时间,文件内容的修改时间,文件属性的修改时间。
上图中,分别查看test.c源文件和test可执行程序的属性,然后看这俩个文件的内容修改时间:
- test.c源文件的内容修改时间比test可执行程序的内容修改时间早。
- 执行make,系统没有执行makefile中的内容
上图中,使用vim打开test.c修改文件中的内容,然后再查看test.c文件的属性,此时继续比较源文件test.c和可执行程序test的内容修改时间:
- test.c源文件的内容修改时间比test可执行程序的内容修改时间早。
- 执行make,系统此时执行了makefile中的文件,也就是成功进行了编译。
通过上面图片的对比你看出什么规律了吗?
- 系统会根据test.c源文件和test可执行程序的内容修改时间来判断此时的test是不是最新的,需不需要编译。
- 当test.c的内容修改时间早于test的内容修改时间的时候,系统认为此时的test是最新的,不需要再重新编译。
- 等test.c的内容修改时间晚于test的内容修改时间的时候,系统认为此时的test不是最小的,需要重新进行编译。
可以发现,系统会自行判断是否需要执行编译,以此来减少系统的工作。
上图中,原本是有test可执行程序的,执行了make clean以后,test被删除了,然后继续make clean,可以看到系统仍然在执行删除指令。
- 因为clean是被.PHONY修饰的对象,它是一个伪目标。
- 此时,系统就不会自己判断是否需要执行该指令了。
- 只有make一次,就会执行一次。
同样的,在makefile中用.PHONY修饰编译,再make的时候,系统也不会自行判断是否编译,而是根据你输入make的次数来执行对应的编译次数。
在makefile中,用.PHONY来修饰test。
此时即使test已经存在,并且没有修改过test.c,不停的make就会不停的执行编译,系统不再自行判断。
说明:
- make [选项]:如果加选项就会make对应的依赖关系和依赖方法,如果不加选项只使用make就默认从第一个依赖关系和依赖方法开始make,直到生成可执行程序停下来。
- 有些依赖关系是可以为空的,比如clean。
- makefile文件名还可以写成Makefile。
以上就是伪目标的意义和作用。
💥Linux上的第一个程序
现在已经会使用编辑器,而且也会使用编译器,接下来就可以实现我们在Linux上的第一个程序了。
在test.c中写这样一个程序,打印hello world,然后延迟三秒。
将生成的可执行程序执行后,屏幕上直接打印出了hello world!字符串,然后等待3s后又出现了下一行的命令行。也就是在绿色框中的内容出现以后,等待3s后才出现的蓝色框中的内容。
此时的现象是符合我们预期的,下面做一些修改。
将打印字符串后面的换行符删除。
此时执行可执行程序后,发现绿色框和蓝色框在等待3s以后同时出现。
为什么会这样呢?只是少了一个\n啊,效果就有这么大的差别吗?
系统中是有一个缓冲区的,本喵在这里仅做简要介绍。
- 在程序执行到打印语句的时候,会先将打印的数据放在缓冲区
- 待缓冲区放满数据或者程序运行结束以后,将缓冲区中的数据发送到显示器上。
这就是上面程序会在3s后同时显示打印语句和命令行的原因。
- 有\n的时候,当缓冲区中接受到\n的时候
- 就会将\n所在这一行的数据立刻打印到屏幕上,不再等待缓冲区满或者是程序执行结束。
这就是最上面程序中,打印语句先打印,等待3s后命令行再显示的原因。
除了\n以外,还有办法让缓冲区的数据直接刷新到显示器上:
在打印语句后面加上图红色框中的代码,当程序执行到这一句的时候,就会立刻将缓冲区中的数据刷新到显示器上。
此时的执行结果就是在绿色框出现3s后,蓝色框才出现。
在知道了缓冲区以后,还需要知道一个东西。
上面程序中,在打印语句中加回车符号\r以后,看程序运行的效果:
此时就会在一个地方开始进行计数,从0倒10。
- \r是回车的意思,表示回到当前行的首位置。
- 在屏幕上这一行每打印出一个字符后,屏幕上的光标就会又回到首位置。
- 在等待1s以后,就会在光标的位置继续打印一个字符。
- 原本首位置的字符就会被新的字符覆盖,所以看到的现象就是从0到10计数。
利用回车符号和缓冲区的原理,我们就可以写出一个进度条的程序。
上面是test.c文件和进度条的头文件的代码。
最重要的是process.c中的代码。
- 在循环中,将数组中的元素打印出来,并且每次打印后都在数组中增加一个元素。
- 因为打印的时候使用了回车\r,所以每次数组中的内容都会覆盖屏幕上原来的内容。
- 通过fflush立刻刷新缓冲区中的数据到屏幕上
- 使用usleep延时0.05s,这样整个进度条就可以在5s内打印完。
再看makefile文件中的内容:
将多个源文件一起进行编译,如图中的红色框中的。
之前我们写的代码,执行结果单纯就是我们在代码中所写的逻辑,而这个进度条的代码:
- 在执行我们代码的同时,系统的运行机制也会影响代码的效果。
- 系统缓冲区的刷新快慢,以及使用\r后在屏幕上的显示原理,再加上代码的逻辑,它们共同决定着代码的最终效果。
💥版本控制器git
git是我们现在最常用的版本控制器。它是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。 也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。它会记录一个文件的修改之前,修改之后等多个版本,再也不用担心找不到文件的最开始版本了。
💢git的操作
创建本地仓库:
在gitee上创建一个仓库,将仓库的地址复制下来。
在命令行中输入git clone 复制的仓库地址,如上图中的红色框,此时在我们的系统上就会创建一个本地仓库,这个仓库是和gitee上的远端仓库同步的。
此时在我们刚刚的目录下就多了一个目录,如上图中的红色框,这个目录就是本地仓库,所以说,仓库的实质就是一个目录。
三板斧第一招:git add:
- 用法:git add [文件名]
- 将文件放在创建好的本地仓库中
使用该指令以后,就将蓝色框中的文件全部放在了本地仓库中,其他文件是仓库中原来就有的。
这里使用的文件名是一个点,git会自动判断,将当前目录下,并且在本地仓库中没有的文件放进本地仓库中。
**三板斧第二招:git commit **
- 用法:git commit -m ‘提交日志’
- 功能:将文件中的改动提交到本地仓库,会自动更新已经放在本地仓库中的内容。
- 注意:-m后的单引号中的提交日志一定不要乱写。
执行上图中红色框中的指令后,会出现提示,需要配置一下git的邮箱用户等信息,照着提示来就行,本喵这里就不演示了。此时就将绿色框中的五个文件最终版本提交了本地仓库。
**三板斧第三招:git push **
- 用法:git push
- 功能:将本地仓库中的内容和gitee上远端仓库的内容保持一致。
执行该指令以后,会让你输入用户名和密码,然后就成功将本地仓库中的文件提交到了远端仓库。
此时在远端的仓库就可以看到我们的文件。
创建一个新的文件test.txt,然后使用三板斧将这个文件同步到远端仓库,如上图中所示。
可以看到,此时远端仓库中只增加了这一个文件。
查看提交记录:git log:
- 用法:git log
- 功能:显示向远端仓库的提交记录,并且是按照时间顺序倒着显示。
可以清楚的看到我们的提交记录,从这里也可以看出,commit的日志一定不能瞎写。
将远端仓库内容拉到本地仓库:git pull
- 用法:git pull
- 功能:将远端仓库中的内容拉到本地仓库,并且将不同的内容自动更新。
在远端仓库创建一个文件,当然也可以是另外一个人上传的文件,如上图中的红色框。
在系统上创建一个新的文件,如上图所示。
此时将这个新的文件使用三板斧上传到远端仓库的时候,就会提示有冲突,无法上传,这是因为,远端仓库中有修改的内容,本地仓库还没有和远端同步,所以需要先同步,也就是将远端仓库中的改动拉到本地仓库。
执行完git pull指令以后,远端仓库中的文件就被拉到了本地仓库中,如上图中蓝色框中所示。
此时本地仓库和远端仓库就保持一致了,所以此时才能将那会无法上传的文件git_pull上传。
此时再次push就成功了。
在远端仓库中可以看到,git_pull确实被上传上去了,如上图中的红色框。
删除,重命名:git rm/mv 文件名:
- 用法:git rm 文件名
- 功能:删除仓库中的文件
使用红色框中的指令删除文件,在提交改动并提交到远端仓库,如图中的蓝色框。
可以看到,远端仓库中那个文件就没有了。
- 用法:git mv 文件原名字 修改后的文件
- 功能:和mv一样,只不过这里修改的是仓库中的文件名。
使用指令修改文件名字,然后再提交改动并且提交到远端。
可以看到,此时远端仓库中的文件名字就被修改了。
以上就是git的常规操作,目前这些东西已经够我们用了。
💢.gitignore文件配置
在本地仓库中,有一个.gitignore的隐藏文件,如上图中红色框中所示。
这个文件的作用是什么呢?
在上传一个文件的时候,这个文件中有各种类型的文件,而我们上传的只是源代码之类的有用的文件,如果一个文件一个文件上传的话又非常的繁琐,而.gitignore文件就是用来解决这个问题的。
打开这个文件后,在文件的最上方加红色框中的内容,作用是忽略后缀为.sln的文件,这个后缀的文件就不上传到远端仓库中。
在本地仓库中创建上图红色框中的文件。
使用三板斧将文件提交到远端仓库。
可以看到,此时远端仓库中只有一个后缀为.c的文件,那个后缀为.sln的文件没有传到远端仓库。
.gitignore的作用就是用来配置哪些文件类型是上传的,哪些文件类型是不上传的。
💥gdb
调试是我们在写程序过程中非常重要的一环,可能大部分时间都是在调试,之前在Windows下的VS2019,只需要按一下F10等快捷键或者点一下命令窗口的选项就可以进行调试,同样的,在Linux下也是可以调试代码的,用到的调试器叫做gdb。
写一段这样的代码用来调试。
使用g++编译器编译以后,生成一个mytest可执行程序,我们要调试的就是这个可执行程序。
此时使用gdb来调试的时候,发现调试不了,如绿色框中显示,这是什么原因呢?
背景知识:
- 程序的发布方式有两种,debug模式和release模式
- Linux gcc/g++出来的二进制程序,默认是release模式
- 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项
所以说,要想调试,调试的可执行程序必须是debug版本的。
将编译成debug版本的可执行程序语句我们放在Makefile文件中,如上图。
此时make完以后,当前目录下就有release版本喝debug版本,我们来看它们有什么不一样。
上图是查看release版本的可执行程序,可以看到,可执行程序表现为16进制的编码,具体是什么意思现在不需要知道。
在查看debug版本的可执行程序,我们发现,debug版本比release多了一些信息,如上图中的绿色框,虽然我们不知道它是干什么的,但是可以看到有debug的字样,说明多的这些信息就是调试信息。
debug版本的可执行程序比release版本多了一些调试信息。
下面我们就可以调试了,切记,调试的是debug版本的可执行程序。
可以看到,此时就可以调试了。
使用gdb调试同样是使用各种指令来调试。
指令 | 全称 | 功能 |
---|---|---|
b | breakpoints | 打一个断点 |
info b | info break | 查看当前断点 |
d | delete breakpoints (n) | 删除断点 |
如上图的演示,打断点的时候,b后面加行号,删除断点的时候d后加断点的序号。
指令 | 全称 | 功能 |
---|---|---|
r | run | 调试运行 |
n | next | 逐过程调试 |
s | step | 逐语句调试 |
c | continue | 运行到下一个断点处停止 |
在程序中加一些打印语句。
- 在程序中打3个断点,分别是在15行,18行,21行处
- 使用run后,运行到第一个断点处停止,也就是运行到了15行
- 使用c后,运行到了下一个断点处停止,俩个断点之间有俩条打印语句,并且执行了,如上图中的绿色框所示。
在15行处停止以后,使用next便只执行一条语句,也就是第16行的语句。
在15行停止后,使用step就成功进入到了函数中。
指令 | 全称 | 功能 |
---|---|---|
bt | breaktrace | 查看函数堆栈 |
finish | 执行完当前函数 | |
until+n | 跳转到指定行 |
- 使用step进入函数后,使用next逐过程调试,发现在函数的第6行到第8行之间反复循环
- 使用bt指令后,可以看到函数的栈中调用
- 使用until 9跳到第9行就跳出了循环,因为第9行是没有用的语句,所以之间到了第10行的返回语句处
在进入函数以后,使用finish指令,之间就执行完了该函数,并且返回了对应的值,如上图中的绿色框。
指令 | 全称 | 功能 |
---|---|---|
p | 临时查看变量值 | |
display | 常显示变量 | |
undisplay | 取消变量常显示 |
在函数中,使用p加变量名后,只会临时显示一次,在后面执行的过程中就不再显示了。
使用display加变量名后,此时变量就成了常显示了,不会在执行的过程中消失。
- 每设置一个变量的监视窗口都会产生一个窗口编号
- 使用undisplay加编号就可以取消掉常显示的变量,在后面的执行中被取消掉的变量就不再显示了,如上图中蓝色框所示。
- 查看窗口信息中也能看到此时只有一个变量sum了,如上图中的绿色框。
最后要说的就是,使用q也就是quit的简写来退出gdb调试器。
💥总结
通过上面两万字左右的讲解,已经把我们在使用Linux时用到的常用工具全部介绍了,如果掌握了这些就可以像使用Windows一样来使用Linux,然后就可以进行相关的系统编程,学习Linux系统的特性了。这篇文章本喵也是当作一个记录本来写的,当使用到某些工具的用法忘记了就可以翻开看看,毕竟这是一个熟能生巧的过程,希望可以给大家一点帮助。