Linux环境基础开发工具的使用(apt, vim, gcc, g++, gbd, make/Makefile)
什么是软件包
在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序.
但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安 装程序)放在一个服务器上, 通过包管理器可以很方便的获取到这个编译好的软件包, 直接进行安装.
软件包和软件包管理器, 就好比 "App" 和 "应用商店" 这样的关系.
Linux 软件包管理器 apt
在linux下安装软件的方式大概有以下三种:
1)下载到程序的源代码,自行进行编译,得到可执行程序
2)获取rpm安装包,通过rpm命令进行安装(为解决安装包的依赖关系)
3)通过apt进行安装软件(经常使用的方式)
认识apt
apt(Advanced Package Tool)是Linux下非常常用的一种包管理器. 主要用于在 Ubuntu 、 Debian 和相关 Linux 发行版上
注意:一个服务器同一时刻只允许一个apt进行安装,不能在同一时刻安装多个软件
因为apt是从服务器上下载RPM包,所以在下载时必须联网,可以通过ping指令指令判断当前云服务器是否联网
查找软件包
使用apt list命令,它会罗列出可以下载的所有软件
这里我们以查找lrzsz为例
lrzsz可以将Windows当中的文件上传到Linux中,也可以将LInux当中的文件下载到Windows中,实现云服务器和本地机器之间进行学习胡川
使用apt list | grep lrzsz来得到lrasz软件信息
由于包的数量太多,所以我们可以使用grep指令筛选出我们所关注的包,这里我们以lrzsz为例
此时就会显示与lrasz相关的软件包
在ubantu上这里会报错,apt 没有稳定的 CLI 接口。在脚本中谨慎使用。
安装软件
指令:sudo apt install 软件名
apt会自动找到都有那些软件包需要下载,这时候敲 “y” 确认安装
注意事项:
1)安装软件时由于需要想系统目录中写入内容,一般需要sudo或者切换到root账户下才能完成
2)yum安装软件只能一个一个安装,正在使用apt安装一个软件的过程中,如果再尝试安装另一个软件,apt会报错
如何实现本地机器和云服务器之间的文件互传
如何已经安装了lrzsz,这里就顺便说一下lrzsz如何使用
指令: rz -E
通过该指令可选择需要从本地机器上传到云服务器的文件
指令:sz 文件名
该指令可以将云服务器上的文件下载到本地机器的指定文件夹
卸载软件
指令:sudo apt remove 软件名
apt会自动卸载该软件,这时候敲“y”确认卸载
注意:以上卸载安装指令在软件名前加上 -y 的意思是不需要询问直接卸载
Linux编辑器 - vim
vim的基本概念
vim在我们做开发的时候,主要解决我们编写代码的问题,本质上是一个多模式的文本编辑器
我们这里主要介绍vim最常用的三种模式:命令模式,插入模式,底行模式
1.命令模式(Normal mode)
在命令模式下,我们可以控制屏幕光标的移动,字符,字或行的删除,复制粘贴,剪贴等操作
2.插入模式(Insert mode)
只有在插入模式下才能进行文字的输入,该模式是我们使用最频繁的编辑模式
3.底行模式(Command mode)
在底行模式下,我们可以将文件保存或退出,也可以进行查找字符串等操作。在底行模式下我们还可以直接输入vim help-modes查看当前vim的所有模式
vim下各模式的切换
指令:vim 文件名
进入vim后默认是命令模式(普通模式),要输入文字需切换到插入模式
命令模式 切换至 插入模式
1)输入 i :当前光标处进入插入模式
2)输入 a :在当前光标的后一位置进入插入模式
3)输入 o :在当前光标处起一行进入插入模式
命令模式 切换至 底行模式
1)插入模式或是底行模式切换至命令模式都是直接按一下Esc键即可
vim命令模式下各指令汇总
【移动光标】
1)按 k : 光标上移
2)按 j : 光标下移
3)按 h : 光标左移
4)按 l :光标右移
5)按 gg : 定位到光标到最开始行
6)按shift + g(G): 定位光标到结尾行
7)按 n + shift + g(nG): 定位光标到任意行
8)按shift + $:定位光标到当前行结尾
9)按shift + ^ :定位光标到当前行开始
10)按w , e, b:光标按照单词进行行内跨行移动
w:光标从左往右,从上到下的跳到下一个字的开头
e:光标从左往右,从上到下的跳到下一个字的结尾
b:光标从右到左,从上到下的跳到下一个字的开头
【删除,复制,剪切】
11)按(n)yy:复制光标所在行(n行)
12) 按(n)dd:剪切,删除
13) 按(n)p: 粘贴(重复行)到光标所在行下一行
【撤销】
14) 按u:撤销
15)按ctrl + r:撤销之前的撤销
【大小写转换】
16)按shift + ~:大小写转换
按n + shift + ~:大小写转换,从光标所在位置一直往后n个字符进行大小写转换
【替换】
17)按(n)r:对光标之后的所有字符进行批量化替换
18)按shift + R:替换模式,对内容进行整体的替换 -> 第四种模式
19)按(n)x:对光标字符之后的字符进行删除
【更改】
21)按cw:将光标所在的位置开始到字尾的字符删除,并进入插入模式
22)按cnw:将光标所在位置开往后的n个字删除,并进入插入模式
【翻页】
23)按Ctrl+b:上翻一页
24)按Ctrl+f:下翻一页
25)按Ctrl+u: 上翻半页
26)按Ctrl+d: 下翻半页
vim底行模式个指令汇总
先使用shhit + ;
【保存退出】
1)w :保存文件
2)q :退出vim,如果无法离开vim,可在q后面跟一个!表示强制退出
3)wq :保存退出
【行号设置】
4)set nu:显示行号
5)set nonu:取消行号
【分屏指令】
6)vs 文件名:实现多文件的编辑
7)Ctrl + w + w:光标在多屏幕下进行切换
【执行指令】
8) !+指令:在不退出vim的情况下,可以在指令前面加上!就可以执行Linux的指令,例如查看目录,编译当前代码等
vim的简单配置
【配置文件的位置】
1)在目录/etc/下面,有个名为vimrc的文件,这是系统中公共的配置文件,对所有用户都有效
2)在每个用户的主目录/home/xxx下,都可以自己建立私有的配置文件,命名为“vimrc”,这是该用户的私有配置文件,仅对该用户有效
例如:普通普通用户在自己的主目录下建立了“.vimrc”文件后,在文件当中输入set nu指令并保存
,下一次打开vim的时候就会自动显示行号
Linux编译器 - gcc/g++
gcc/g++的作用
gcc和g++分别是GUN的C和C++的编译器
gcc和g++在执行的时候一般有以下四个步骤:
语法:gcc/g++ 选项 文件
常用选项:
1)-E 只进行预处理,这个不生成文件,你需要把他重定向到一个输出文件里面(否则将把预处理后的结果打印到屏幕上)
2)-S 编译到汇编语言,不进行汇编和链接,即只进行预处理和编译
3)-c 编译到目标代码
4)-o 将处理结果输出到指定文件,该选项后紧跟输出文件名
5)-static 此选项对生成的文件采用静态链接
6)生成调试信息(若不携带该选项则默认生成release版本)
7)-shared 此选项将尽量使用动态库,生成文件较小
8)-w不生成任何警告信息
9)Wall 生成所有警告信息
10)-O0/-O1/-O2/-O3 编译器优化选项的四个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
预处理 (进行宏替换)
预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
预处理指令是以#号开头的代码行。
实例: gcc –E test.c –o test.i
选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
编译(生成机器可识别代码)
在这个阶段中,gcc/g++首先检查代码的规范性,是否有语法错误等,以确定代码的实际要做的工作,在检查无误之后,将代码翻译成汇编语言
用户可以使用-S选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码
实例: gcc –S test.i –o test.s
汇编(生成机器可识别代码)
汇编阶段是把编译阶段生成的“xxx.s”文件转成目标文件
使用-c选项就可以得到汇编代码转换为“xxx.o”的二进制目标代码
实例: gcc –c test.s –o test.o
连接(生成可执行文件或库文件)
在成功完成以上步骤之后,就进入了链接阶段
链接的主要任务就是将生成的各个“xxx.o”文件进行链接,生成可执行文件
gcc/g++不带-E, -S, -c选项时,就默认生成预处理,编译,汇编,链接全过程的后的文件
若不使用-o选项指定生成文件的文件名,则默认生成的可执行文件名为a.out
实例:gcc test1.c -o test
可以看到test直接就生成了可执行文件,链接后生成的也是二进制文件
在这里涉及到一个重要的概念:函数库
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而 没有定义函数的实现,那么,是在哪里实“printf”函数的呢? 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到 系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函 数“printf”了,而这也就是链接的作用
函数库一般分为静态库和动态库两种
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态 库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。 gcc hello.o –o hello
gcc默认生成的二进制程序,是动态链接的,这点可以通过file 命令验证。
动态库
优点:省空间(磁盘的空间,内存的空间),bin体积小,加载速度块
缺点:依赖动态库,程序可移植性较差
静态库
优点:不依赖第三方库,程序的可移植性较高
缺点:浪费空间
gcc和g++默认生成的二进制程序是动态链接的,我们可以使用file指令进行查看
其次我们还可以使用ldd指令查看动态链接的可执行文件所依赖的库
其中/lib/x86_64-linux-gnu/libc.so.6就是当前云服务器当中的C标准库
如果我们需要使用静态链接我们可以加上-static选项
这时生成的可执行文件就是静态链接了
我们可以查看相同的源代码,使用不同的链接方式,所生成的可执行文件大小
可以看到动态链接确实比较节省空间,静态链接比较浪费空间
在Linux下:静态库(.so),动态库(.a)
在windows下:静态库(.lib),动态库(.dll)
Linux调试器-gdb
1. 背景
程序的发布方式有两种,debug模式和release模式
Linux gcc/g++出来的二进制程序,默认是release模式
要使用gdb调试,必须在源代码生成二进制程序的时候, 加上-g选项
对同一份源代码分别生成其release版本和debug版本的可执行程序,并通过ll指令可以看到,debug版本发布的可执行的大小比release版本发布的可执行文件大小要大一点,其原因就是在debug版本中包含了更多的调试信息
gdb命令汇总
【进入gdb】
指令:gdb 文件名
【退出gdb】
退出: ctrl + d 或 quit 调试命令:
【调试】
r或run:运行程序。
n 或 next:单条执行。
s或step:进入函数调用
until 行号:跳转至指定行
finish:执行完当前正在调用的函数后停下来(不能是主函数)
continue或c:运行到下一个断点处
set var 变量=x:修改变量的值为x
【显示】
list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
list/l 函数名:列出某个函数的源代码。
print/p 变量:打印变量的值
print/p &变量:打印变量的地址
print/p 表达式:打印表达式的值,通过表达式可以修改变量的值
display 变量:将变量的地址加入常显示
undisplay 编号:取消指定编号变量的常显示
bt:查看各级函数调用及参数
info/i locals:查看当前栈帧当中的局部变量的值
【断点】
break(b) 行号:在某一行设置断点
break 函数名:在某个函数开头设置断点
info break :查看断点信息。
delete breakpoints:删除所有断点
delete breakpoints n:删除序号为n的断点
disable breakpoints:禁用断点
enable breakpoints:启用断点
Linux项目自动化构建工具-make/Makefile
make/Makefile的重要性
- 会不会写Makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
- 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,Makefile定义了一系列的 规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂 的功能操作
- Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编 译,极大的提高了软件开发的效率。
- make是一个命令工具,是一个解释Makefile中指令的命令工具,一般来说,大多数的IDE都有这个命 令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,Makefile都成为了一 种在工程方面的编译方法。
- make是一条命令,Makefile是一个文件,两个搭配使用,完成项目自动化构建。
依赖关系和依赖方法
在使用make/Makefile之前我们首先应该理解各个文件之间的依赖关系以及它们之间的依赖方法
依赖关系:文件A的变更会影响到文件B,那么就称文件B依赖于文件A
例如:test.o是由test.c文件通过预处理,编译以及汇编之后生成的二进制文件,即test.c的改变会影响到test.o,所以我们可以得到test.o文件依赖于test.c文件
依赖方法:如果文件B依赖于文件A,那么通过文件A得到文件B的方法,就是文件B依赖于文件A的依赖方法
例如:test.o依赖于test.c,而test.c通过 gcc -c test.c -o test.o 指令就可以得到test.o,那么test.o依赖于test.c的依赖方法就是 gcc -c test.c -o test.o
多文件编译
当你的工程当中有多个源文件的时候,应该如何进行编译生成可执行文件呢?
我们可以直接使用gcc指令对多个源文件进行编译,进而生成可执行程序
进行多文件编译的时候一般不使用直接生成可执行文件,而是先用每个源文件各自生成自己的二进制文件,然后再将这些二进制文件通过链接生成可执行程序
为什么让这样做?
若是直接使用源文件生成可执行程序,那么其中一个源文件进行了修改,再生成可执行程序的时候就需要将所有的源文件进行编译链接,重新生成一份新的可执行程序
我们若是先用每个源文件各自生成自己的二进制文件,那么其中一个源文件进行修改,就只需要重新编译生成该源文件的二进制文件,然后再将这些二进制文件通过链接生成可执行程序即可
注意:编译链接的时候不需要加上头文 件,因为编译器通过源文件的内容可以知道所需头文件的名字,而通过头文件的包含方式(“尖括号”包含和“双引号”包含),编译器可以知道应该从何处取寻找所需头文件
随着源文件个数的增加,我们每次重新生成可执行程序时,所需输入的gcc指令的长度与个数也会随之增加。这时我们将需要使用make和Makefile了,有了它们两的加入,这将大大的减少我们的工作量
make和Makefile的使用方法
步骤一:在源文件所在的目录下创建一个名为Makefile/makefile的文件
步骤二:编写Makefile
Makefile的编写格式是,先写出文件的依赖关系,然后下一行写出这些文件的依赖方法
根据自己需求可以写一个,也可以写多个
编写完Makefile之后保存退出,然后再命令行当中执行make指令便可以生成可执行程序,以及该过程产生的中间产物
Makefile文件的简写方法:
1.$@: 表示依赖关系的目标文件(冒号的左侧)
2.$^: 表示依赖关系中的依赖文件列表(冒号右侧全部)
3.$<: 表示依赖关系中依赖列表的第一个依赖文件(冒号左侧第一个)
使用以上符号简化上述操作:
说明:gcc/g++携带-c选项时,若不指定输出文件的文件名,则默认输出文件名为 xxx.o,所以这里也可以不用指定文件名
make原理:
1.make会在当前目录下寻找名字为“Makefile”或者“makefile”的文件
2.如果找到,它会找文件当中的第一个目标文件,在上面的例子中,它会找到mytest这个文件,并把这个文件当作最终的目标文件
3.如果mytest文件不存在,或是mytest所依赖的后面的test.o和main.o文件的文件修改时间比mytest文件新(如果比mytest老,make的操作就不会执行),那么它就会执行后面的依赖方法来生成mytest文件
4.如果mytest所依赖的test.o文件不存在,那么make会在Makefile文件中寻找目标为test.o文件的依赖关系,如果找到则在根据其依赖方法生成test.o文件(类似于堆栈的过程)
5.如果你的test.c文件和main.c文件是存在的,make会根据依赖方法生成test.o文件和main.o文件,然后使用这两个文件形成最后的可执行文件mytest
6.make会一层一层的取找文件的依赖关系,直到编译出第一个目标文件
7.在寻找的过程中,如果出现错误,例如最后被依赖的文件找不到,那么make就会直接退出并报错
项目清理
在我们每次生成可执行程序前,都将上一次生成的可执行程序时生成的一系列文件进行清理,除了我们手动清理之外,我们还可以借助 make 帮助我们进行清理,这时我们可以将项目清理的指令加入到Makefile文件当中
像clean这种,没有被第一个目标文件直接或间接关联,那么你直接使用make它不会自动执行,所以我们可以显示使用make执行
注:一般将clean这种目标文件设置为伪目标,使用.PHONY修饰,伪目标的特性是:总是被执行
Linux第一个小程序 - 进度条
行缓冲区的概念
首先我们来感受一下行缓冲区的存在,在LInux当中以下代码的运行结果如何?
对于以上代码,它的输出为:hello world,然后休眠三秒之后结束运行
我将第6行末尾的\n删除之后,其实代码的运行结果就已经改变了,现在的运行结果:先休眠3秒然后才会将hello world输出出来。休眠的3秒钟其实hello world被放到了行缓冲区中,这种现象其实就证明了行缓冲区的存在
显示器对应的是行刷新,即当缓冲区当中遇到‘\n’或是缓冲区被写满才会被打印出来,而在第二份代码当中并没有‘\n’,所以字符串hello world先被写到缓冲区当中去了,然后休眠3秒后,直到程序结束时才将 hello world 打印到显示器当中
\r 和 \n
\r: 回车,使光标回到本行行首
\n: 换行,使光标下移一格
在实际当中,其实我们通常使用的Enter键就等价于 \n + \r
接下来我们来做一个有趣的实验
既然\r是使光标回到本行行首,那么如果我们向显示器上写了一个数之后再让光标回到本行行首,然后再写一个数,不就相当于将前面一个数据给覆盖了吗?
注意:我们可以使用\n刷新缓冲区,也可以使用fflush函数刷新缓冲区,即将缓冲区当中的数据刷新到当前显示器当中
对此我们可以编写一个倒计时的程序
在以上代码的最后,我们使用printf("\n");进行刷新操作,不然最后的数据0会被命令行给刷新掉
进度条代码及效果展示
效果展示: