Linux基础开发工具
1. Linux软件包管理器yum
1.1 软件包
在Linux中下载软件,有很多种方法,如:将需要下载的软件的源代码下载下来,编译得到可执行程序。
但是这种方法太麻烦了,于是有些人把常用的软件根据各种不同的环境编译成不同的版本,存储在一个服务器上,方便大家根据需求下载安装后就能直接使用。
而软件包管理器yum就类似于我们手机中的应用商店,链接对应的服务器来获取我们需要的软件,这也意味着我们的Linux系统中肯定存放着目标服务器的地址。
1.2 yum相关操作
【注意】关于yum的所有操作必须保证主机(虚拟机)的网络通畅,可以通过ping指令验证,如ping www.baidu.com。
1.2.0 关于lrzsz
我们下面的演示以lrzsz软件为例,这个软件的用于windows机器和Linux机器通过XShell传输文件,安装完毕后可以通过拖拽文件的方式上传文件。
1.2.1 查看软件列表
语法结构:
yum list
演示:
我们使用管道和grep指令可以实现查找我们想要的软件包。

其中上面显示的信息分别是:
-
lrzsz:软件包名称
-
x86_64:后主表示64位系统的安装包,i686则表示32位系统的安装包,选择安装包时要与当前系统匹配
-
0.12.20-36:主版本号.次版本号.源程序发行号-软件包发行号
-
el7:操作系统发行版的版本,el7表示CentOS7/redhat7,el6则表示CentOS6/redhat6
-
base:表示软件源的名称
1.2.2 下载软件
语法结构:
[sudo] yum install [-y] 软件名
注意:
-
sudo:如果是普通用户则需要提权
-
-y:确认安装,后续不在询问,如果不加这个选项,后面yum找到软件后会询问你是否安装这个软件
-
yum安装软件时只能装完一个后再装下一个,否则会报错。
演示:

1.2.3 卸载软件
语法结构:
[sudo] yum remove [-y] 软件名
- -y选项与安装软件中意义相同。
演示:

1.3 安装扩展软件源
在base软件源是基本软件源,里面都是一些非常稳定的软件。
我们还可以安装扩展软件源,里面可以找到一些有趣的软件,比如sl(用来打印小火车)。
安装扩展软件源:
sudo yum install -y epel-release
1.4 yum本地配置
yum会根据/etc/yum.repos.d/路径下的配置文件来构成自己的下载链接,在我们安装好的CentOS系统中,软件链接都是指向CentOS的官网,也就是指向国外服务器,这样会比较慢,而且现在SentOS官方也已经不再维护了,我们也就下载不了软件。
此时我们就可以更改软件源的配置。
切换软件源为阿里云:
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
随后再清除缓存、生成缓存、升级更新即可:
yum clean all
yum makecache
yum -y update
2. Linux编辑器-vim
在我们安装系统后,会有一个vi编辑器,而vim就是vi的升级版,兼容vi的所有指令,还有一些新的特性,比如语法加亮等。
2.1 vim的基本概念
在vim中有很多种模式,我们最常使用的模式分别是:命令模式(command mode)、插入模式(insert mode)、底行模式(last line mode),各模式功能如下:
-
命令模式
控制屏幕的光标移动,字符、字、行的删除,移动赋值某区段进入insert mode下,或者到last line mode下。
-
插入模式
在命令模式下在键盘上按i就可以进入插入模式,只有在插入模式下在科研输入文字,按ESC键就可以回到命令模式。
-
底行模式
在命令模式下输入shift+;就可以进入底行模式,文件的保存或退出、文件替换、查找字符串、列出行号等操作。
进入vim后,在底行模式下输入help vim-modes就可以查看所有模式。
2.2 vim的基本操作
2.2.1 进入vim
vim 文件名
如果文件不存在,且你进行操作后保存了该文件,则会创建该文件。
进入vim界面时处于命令模式下。
2.2.2 命令模式切换插入模式
- 键盘上按i/a/o
2.2.3 命令模式切换底行模式
- 键盘上按shift+;实际上就是输入:
2.2.4 插入模式/底行模式切换命令模式
- 键盘按ESC键
2.2.5 保存/退出vim
最前面有一个:代表进入底行模式,输入后回车执行。
- :w:保存文件
- :q:退出vim
- :wq:保存并推出
- 在命令后加上!代表强制执行
2.3 vim命令模式命令集
2.3.1 切换到插入模式
- i:进入插入模式后,光标从当前位置进行输入
- a:进入插入模式后,光标从下一个位置开始输入
- o:进入插入模式后,插入新的一行,从行首开始输入
2.3.2 移动光标
-
基本移动:h(左)、j(下)、k(上)、l(右),或使用键盘上的上下左右键。
-
进入到文件开始:gg
-
移动到文件末尾:G
-
移动到行尾:$
-
移动到行首:^
-
跳到下一个字(单词)开头:w
-
跳到下一个字(单词)结尾:e
-
跳到上一个字(单词)开头:b
-
移动到该行的第n个位置:nl,如5l、10l
-
ctrl+b屏幕往下移动一页
-
ctrl+f屏幕往上移动一页
-
ctrl+u屏幕往下移动半页
-
ctrl+d屏幕往上移动半页
2.3.3 删除文字
- 删除光标所在位置的字符:x
- 删除包含光标位置在内的后面n个字符:nx,如5x、10x
- 删除光标所在位置前面的字符:X
- 删除不包含光标所在位置的前面n个字符:nX,如5X、10X
- 删除光标所在行:dd
- 删除包含光标所在行内往后的n行:ndd,如:5dd、10dd
2.3.4 复制
- 复制光标所在之处到字(单词)尾的字符:yw
- 复制包含光标所在字往后的n个字:nyw,如:5yw、10yw
- 复制光标所在行:yy
- 复制包含光标所在行的往下n行:nyy,如5yy、10yy
- 将复制的内容粘贴到光标所在位置:p
2.3.5 替换
- 替换光标所在处的字符:r
- 替换光标所到之处的字符,直到按下ESC键为止:R
2.3.6 撤销上一次操作
- 撤销上一次操作:u
- 恢复撤销:ctrl+r
2.3.7 更改
- 更改光标所在处的字到字尾处:cw
- 更改n个字:cnw,如,c5w,c10w
2.3.8 跳到指定行
- 列出光标所在行行号:ctrl+g
- 跳到第n行:nG,如:5G,10G
2.4 vim底行模式命令集
2.4.1 列出行号
- :set nu
2.4.2 跳转到某一行
- :n,n是一个数字,如5n,10n
2.4.3 查找字符
- :/关键字,从前往后找匹配的关键字,按n继续向下查找
- ?/关键字,从后往前找匹配的关键字,按n继续向上查找
2.4.4 保存文件
- :w
2.4.5 退出vim
- :q,退出
- :wq,保存并退出
2.5 在vim中批量注释代码
- 键盘输入ctrl+v,进入Visual Block模式。
- 使用上下方向键选中多行代码的开头。
- 按shift+i进入插入模式,输入注释符号(例如://)。
- 按esc键退出,注释会自动应用到所有选中行。
2.6 vim配置
2.6.1 配置文件的位置
- /etc/vimec是系统中公共的vim配置文件,对所有用户都有效
- 每个用户的家目录下都可以建立自己的私有配置文件,命名为.vimrc
2.6.2 常用配置项
- 设置语法高亮:syntax on
- 显示行号:set nu
- 设置缩进的空格数为4:set shiftwidth=4
更多配置项不在这里介绍,可以自行上网查找。
这里推荐一个一键配置vim的方法:VimForCpp: 快速将vim打造成c++ IDE
3. Linux-C语言编译器-gcc使用
3.1 直接使用
3.1.1 安装gcc
sudo yum install -y gcc
3.1.2 编写代码

3.1.3 编译

我们直接编译这份代码是没法通过的,因为我们安装的gcc是比较老的版本,我们可以通过gcc -v指令查看gcc版本。

这个版本默认下是不支持在for循环的参数中直接定义变量的,这时我们加上一个编译条件-std=c99就可以了,这意味着以c99的标准来编译这份文件。

在默认情况下生成的文件都是以a.out命名。
我们可以加上-o选项,然后加上想要命名的名字,就可以由我们自己来命名。

3.1.4 运行

3.1.5 补充
对于gcc来说,他只能编译固定后缀的文件,也就是.c文件。

gcc是c语言的编译器,gcc无法编译c++代码,而c++也有对应的编译器-g++,对于g++的使用,和gcc几乎是一模一样的,而c++代码的源文件后缀通常有三种:.cpp和.cc和.cxx。
对于g++想要支持更高的标准也可以加上编译选项-std=c++11。
关于g++的安装:sudo yum install -y gcc-c++
g++同时也是可以编译c语言的(c++兼容c语言,这很好理解)。
使用g++ --version可以查看g++的版本。

3.2 编译过程
- 预处理
- 宏替换、文件包含、条件编译、去注释等。
- 指令:
gcc -E hello.c -o hello.i,可以把编译停留在预处理完后,形成预处理完的文件。 - 预处理后的文件后缀为.i。
- 关于条件编译,在代码中,我们可以使用条件编译来实现一份代码编译出不同的版本,我们在编译时,也可以加上-D选项,再紧跟着需要添加的宏,即:
gcc -DV1 test.c,这样就是在编译时往代码中定义了一个D1宏。
- 编译
- 检查代码规范性、是否有语法错误,确认无误后把经过预处理的代码翻译成汇编语言。
- 指令:
gcc -S hello.i -o hello.s,可以把编译停留在预处理完成后,形成汇编文件,也可以使用.c文件。
- 汇编
- 将汇编语言翻译成二进制目标文件。
- 指令:
gcc -c hello.s -o hello.o,把编译停留在二进制目标文件,二进制目标文件不是可执行文件,无法执行,还需要经过后面的链接。
- 链接
- 将二进制目标文件形成而可执行文件
- 指令:
gcc hello.o -o hello
3.3 动静态链接
3.3.1 链接是什么?
把我们的程序和库结合的过程。
对于每个语言,都会有自己的标准库,里面是官方规定的一些我们常用的公共函数,比如说printf()。
我们可以通过ldd 可执行文件命令来查看该可执行文件链接了哪些库。

我们可以查看这些链接指向的路径,这里主要看第二个。

而这里指向的libc-2.17.so就是C标准库。
这里也就说明,我们安装C语言开发环境其实就是:安装C标准库+C头文件+编译器。
库分为动态库和静态库,在Linux下动态库的后缀为.so,静态库的后缀为.a,在Windows下动态库的后缀为.dll,静态库的后缀为.lib
关于库的命名,上面我们的可执行程序链接的库是libc.so.6,其实这个库的名字就是去掉前缀lib,以及去掉.和后面的后缀,在这里剩下c,也就是c标准库。
3.3.2 为什么要有库
为了我们可以"站在巨人的肩膀上"开发,向printf函数,如果我们自己实现是非常麻烦的,现在有了现成的,可以提高我们的开发效率。
3.3.3 动态链接、静态链接
和动态库链接就是动态链接,和静态库连接就是静态链接。
动态链接:
在编译的时候不把库中的方法拷贝到我们自己的程序中。
我们的程序是要在内存中执行的,而动态库也会被加载一份到内存中,当我们的程序使用的是动态链接时,库中的代码不会拷贝到我们的程序中,而是我们在执行自己的程序的时候,执行到了动态库中的代码时,就跳转到动态库那份内存运行,运行完成后再回到自己的程序对应的内存中去执行剩下的代码。
对于动态库来说,全局只会有一份,所有的动态链接的程序都是链接这一份动态库,只要这一份动态库出问题,这一大批程序都会挂掉。
静态链接:
在编译时把库中的方法拷贝到我们自己的程序中。
此时,我们的程序一样是在内存中执行的,但是,它不再关心外部的库,只需要在自己的内存执行就行了。
对于静态链接的代码,由于静态库的方法拷贝到了我们自己的程序中,所以哪怕外面的库出现了问题,也不影响这份代码的运行。
gcc默认使用动态链接
在C语言中默认提供了动态库,gcc默认形成的程序都是采用动态链接。
我们使用file命令也可以看到可执行程序使用的是动态库。

gcc使用静态链接进行编译
在编译时加上-static选项即可。

在使用-static选项时可能会出现上面的报错,我们需要安装静态库的编译环境yum install glibc-static。
c++的静态库的编译环境安装:yum install libstdc++-static
3.3.4 动静态库的优缺点
对于动态库来说,只存在一份,丢失了就会出问题,但是它不拷贝到我们的程序当中,占用的空间小。
对于静态库来说,程序一旦形成,与库无关,哪怕库没了也可以运行,但是因为每份静态链接的代码都要拷贝一份,所以占用的空间大。
| 动态库 | 静态库 | |
|---|---|---|
| 优点 | 节省资源(所占空间小) | 一旦形成,与库无关 |
| 缺点 | 不能丢失 | 浪费资源 |

我们可以看到静态链接的程序占用的空间比动态链接的程序占用的空间是大非常多的。

我们也可以通过ldd或file命令看出来,这是静态链接。
3.3.5 静态链接的应用场景
如果你,希望你的程序具有非常强的跨平台性,也就是直接把你的可执行程序拷贝过去马上就能用,无论的对方的电脑是否有对应的库,这时就可以使用静态链接。
4. Linux项目自动化构建工具-make/makefile
4.1 直接使用
创建makefile文件:

执行make命令:

添加清理功能:

执行清理功能:

4.2 原理
make是一个命令,makefile是一个文件,makefile是文件的名字,无论大小写都可以。
在上面的演示中:
- 第一行
mytest:test.c是依赖关系,:左边的是目标文件,右边的是依赖文件列表。 - 第二行
gcc -o mytest test.c -std=c99,是依赖方法,依赖方法前面必须要有一个缩进(Tab)。 - 第三行
.PHONY:clean,无论clean是否执行过,都强制执行一次 - 第四行
clean:只有目标文件,没有依赖文件,也就是说执行这个方法不需要依赖文件。 - 第五行
rm -f mytest,clean的方法。
在执行make命令是,它会在当前目录下寻找makefile文件,执行依赖方法写的命令,依赖文件列表就是需要提供的文件,目标文件就是用于识别make执行哪个方法,默认情况下执行第一个方法,如果有指定的目标文件,就执行指定的方法。

当执行过一次make后,如果在第二次make前如果没有对相关文件做出修改,则会直接拦截。

加上.PHONY后,无论两次make中间是否修改过文件,都强制执行,不会被拦截。
**拦截的目的:**当一个项目中有非常多的文件,我们修改可能只会修改其中的一部分,在重新编译的时候,我们肯定只希望编译我们修改过的文件,而不是全部都重新编译,这时make就会帮我们拦截下不需要重新编译的文件,提高工作效率
**拦截的原理:**对于每个文件都有自己的ACM时间,make就是对比源代码和可执行程序的修改时间,如果源代码的修改时间早于可执行程序,那么就说明在可执行文件形成后,源代码没有进行过修改,反之,则认为源代码发生过修改。
证明:

在上面操作中,没有修改test.c中的代码,只是用touch命令更新了test.c的修改时间,再执行make,我们可以发现make没有拦截。
4.3 我们想要的makefile

makefile也可以以上面的方式编写,其中$@代表目标文件,$^代表依赖文件列表。

makefile也可以通过上面这种形式来链式调用方法,make时会执行第一个方法,发现缺少code.o文件,然后他会在后面的列表中找是否有生成code.o文件的方法,如果有,就执行,以此类推。

同时,makefile也是支持变量的,用法如下。

我们在执行make时,会显示执行的命令,如果不想让它显示出来,在命令的前面加上@就行。

5. Linux调试器-gdb使用
5.1 安装
sudo yum install -y gdb
5.2 debug模式和release模式
- debug:我们在开发的过程中,一般使的时debug模式,debug模式下生成可执行程序的时候,会给该程序添加调试信息,使得这个程序可以被调试。
- release:我们发布的程序一般就是release模式下编译出来的程序,这个模式下生成的可执行文件不会被添加调试信息,无法被调试,同时根据不同的编译器,还有可能会对代码做一定的优化。
我们使用gcc默认编译程序时采用release模式,如果想要在debug模式下编译,需要加上选项-g。

从上图中可以看出来,在debug可执行程序确实是要比release可执行程序要大上一些,说明它确实在里面加了些什么东西。
那么debug模式下加了什么东西呢?我们可以通过readelf -S 可执行程序来查看可执行程序的一些信息。
在查看debug程序时我们可以看到,其中确实有一些debug信息,而release下是没有的。

5.3 gdb的使用(常用指令)
-
进入调试:gdb 可执行程序
-
退出:q/quit+回车
-
查看源代码:l/list 文件名:行号/函数名,查看源代码指定文件的指定函数/指定行,查看指定位置时,将只会打印十行,并且会将查看的位置尽量显示在十行的中间,方便查看上下文,如果想要查看紧接着后面的代码,直接按回车即可
-
运行程序:r/run,开始运行程序直到碰到断点,如果没打断点,将会把程序直接跑完
-
打断点:b/break 行号/函数名,在指定行或函数开始处打断点,如果把断点打在空行,将不会有任何效果
-
查看断点:i b/info breakpoints
-
删除断点:d/delete [id]:删除序号(可以使用i b查看到断点信息)为n的断点,如果不指定删除某一个断点,则删除所有断点
-
禁用断点:disable [id]
-
启用断点:enable [id]
-
单步执行(逐过程,碰到函数不进入):n/next
-
单步执行(逐语句,碰到函数进入):s/step
-
打印变量的值:p/print 变量
-
常显示某变量的值:display 变量,每次停下来都显示该变量的值
-
取消常显示:undisplay 编号
-
运行至下一个断点处:c/continue
-
执行完当前函数就停下来:finish
-
执行到某一行:until 行号
-
查看代码当前的局部变量的值:i/info local
-
调试时修改某变量的值:set var 表达式(如 i=100)
-
查看各级函数调用及参数:bt/breaktrace