目录
一、Linux软件包管理器 — yum
1.Linux下软件安装的方式
2.如何查看Linux下的软件包
3.利用yum来安装相应的软件
4.利用yum来进行软件的卸载
5.如何实现云服务器与本地机器间的文件互传
6.注意事项
二、Linux编辑器 — vim的使用
1.vim常见的三种模式
2.vim键盘图
3.vim三种基本模式间的切换
4.vim的命令模式下相关命令
①光标移动
②删除
③复制
④替换
⑤撤销
⑥更改
⑦大小写切换
⑧跳转至指定行
5.vim底行模式下相关的命令
①行号设置
②跳转到文件中某一行
③查找字符
④保存与退出
⑤执行指令
⑥分屏操作
6.vim的配置
①配置文件的位置
②vim配置方法
三、Linux编辑器 — gcc/g++的使用
1.gcc和g++的使用
1.预处理
2.编译
3.汇编
4.链接
2.静态库与动态库
1.静态链接与动态链接的区别
2.如何实现静态链接(含安装)
3. gcc/g++常用选项
四、Linux编辑器 — gdb的使用
1.gdb的基本概念
2. gdb指令
1.常用指令汇总
2.指令操作演示
五、Linux项目自动化构建工具 — make/Makefile
1.make/Makefile的背景
2.依赖关系和依赖方法
3.如何编写Makefile文件
1.创建Makefile文件
2.编写Makefile文件
3.效果展示
4.make的原理
5.项目清理
六、Linux第一个小程序 — 进度条
1.回车换行的概念
2.行缓冲区的概念
3.进度条代码
在Linux下,安装软件有以下三种方式:
1.源码安装:获取相应软件的源代码—>编译成二进制文件—>安装
2.rpm安装:rpm是由红帽公司开发的软件包管理方式,使用rpm我们可以方便的进行软件的安装、查询、卸载、升级等工作。但是rpm软件包之间的依赖性问题往往会很繁琐,尤其是软件由多个rpm包组成时。
3.Yum安装:(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器。基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。(最常用)
[mlg@VM-20-8-centos soft]$ sudo yum list
通过yum list来查看所有的软件包
软件包名称: 主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu架构."x86_64" 后缀表示64位系统的安装包, "i686" 后缀表示32位系统安装包. 选择包时要和系统匹配."el7" 表示操作系统发行版的版本. "el7" 表示的是 centos7/redhat7. "el6" 表示 centos6/redhat6.最后一列, base 表示的是 "软件源" 的名称, 类似于 "小米应用商店", "华为应用商店" 这样的概念
也可以通过管道加grep过滤来查找想要的软件,以查找 sl\.x86_64软件为例:
指令:sudo yum install 软件名;我们这里使用的是普通用户,所以要加sudo;执行指令后需要输入普通用户的密码,然后输入“y”确认安装,当出现complete时,表明安装成功;
[mlg@VM-20-8-centos soft]$ sudo yum install sl\.x86_64//这种需要输入y来进行确认
//或
[mlg@VM-20-8-centos soft]$ sudo yum install -y sl\.x86_64//这里带 -y 可以直接安装
软件的运行效果如下:(直接输入sl就可以运行,是一个可以运动的小火车)
指令:sudo yum remove 软件名; 同样需要输入密码,输入'y'确认卸载,当出现complete时表明卸载成功;
[mlg@VM-20-8-centos soft]$ sudo yum remove sl
当我们想要将自己电脑中的文件传入云服务器时,Linux也支持拖拽式上传。
接下来介绍两个指令:rz和sz
rz:运行该命令会弹出一个文件选择窗口,从本地选择文件上传到服务器;一般小文件我们都是用rz -y来进行上传文件,除此之外还可以用rz -E。
rz -y和re -E的共同点和区别:
共同点:
都可以把文件上传到Linux中
区别:
rz -y: 把文件上传到Linux中,如果有相同文件名的文件,会将其覆盖。
rz -E: 把文件上传到Linux中,如果有相同文件名的文件,不会将其覆盖,而是会在所上传文件后面加上 .0 ,两个文件都会存在与此目录中,再次上传则会在文件名后加上 .1,以此类推。
sz:将选定的文件发送到本地机器
指令:sz 文件名
以上两个指令如果出现以下的情况:
[mlg@vm-20-8 ~]# sz -bash: sz: command not found [mlg@vm-20-8 ~]# rz -bash: /usr/bin/rz: No such file or directory
就需要安装lrzsz相关的软件包
关于 yum 的所有操作必须保证主机(虚拟机)网络畅通!!!可以通过 ping 指令验证ping www.baidu.com
Vim是一个文本编辑器。Vim具有代码编译、补完以及错误跳转等编程功能,在程序员中被广泛使用。
1、命令模式(Normal mode):
使用 Vim 编辑文件时,默认处于命令模式。在此模式下,可以使用上、下、左、右键或者 k(上)、j(下)、h(左)、l(右) 命令进行光标移动,还可以对文件内容进行复制、粘贴、替换、删除等操作。2、输入模式(Insert mode):
在输入模式下可以对文件执行写操作,类似在 Windows 的文档中输入内容。进入输入模式的方法是输入 i、a、o 等插入命令,编写完成后按 Esc 键即可返回命令模式。3、编辑模式(Command mode):
如果要保存、查找或者替换一些内容等,就需要进入编辑模式。编辑模式的进入方法为:在命令模式下按":"键,Vim 窗口的左下方会出现一个":"符号,这时就可以输入相关的指令进行操作了。指令执行后会自动返回命令模式。
当我们创建了文件并使用vim指令进行文本编辑时,默认进入的都是命令模式,只有在命令模式下输入a或i或o指令后,才可以进入插入模式,进行相应的文本编辑;文本编辑结束后,就需要保存起来,我们必须切换到命令模式下输入shift + :进入底行模式,输入wq指令进行保存;
注意:我们并不能从底行模式直接进入插入模式,反之亦然;如果当前并不知道处于什么模式下直接按Esc就一定是在命令模式下。
命令模式切换到插入模式可以三种方式:
i:输入i后进入插入模式,光标处于文本编辑的起始位置,进行插入;
a: 输入a后进入插入模式,光标处于文本编辑起始位置的下一个位置,进行插入;
o: 输入0后进入插入模式,光标会在新起一行的位置,进行插入;
命令模式切换到底行模式:
输入shift + :
插入模式或底行模式切换到命令模式:
输入Esc
按键操作 | 功能描述 |
---|---|
h | 左移一个位置 |
j | 向下一行 |
k | 向上一行 |
l | 右移一个位置 |
G |
移动到文章的最后
|
$ |
移动到光标所在行的"行尾"
|
^ |
移动到光标所在行的“行首"
|
w |
光标跳到下个字的开头
|
e |
光标跳到下个字的字尾
|
b |
光标回到上个字的开头
|
#l |
光标移到该行的第#个位置,入5l
|
gg |
进入到文本开始
|
shift+g |
进入文本末端
|
ctrl+b |
屏幕往“后”移动一页
|
ctrl+f |
屏幕往“前”移动一页
|
ctrl+u |
屏幕往“后”移动半页
|
ctrl+d |
屏幕往“前”移动半页
|
按键操作 | 功能描述 |
---|---|
x |
每按一次,删除光标所在位置的一个字符,支持nx
|
#x |
例如,「6x」表示删除光标所在位置的“后面(包含自己在内)”6个字符
|
X | 大写的X,每按一次,删除光标所在位置的“前面”一个字符 |
#x | 例如,「20X」表示删除光标所在位置的“前面”20个字符 |
dd | 剪切光标所在行,支持ndd |
#dd | 从光标所在行开始删除#行 |
按键操作 | 功能描述 |
---|---|
yw | 将光标所在之处到字尾的字符复制到缓冲区中 |
#yw | 复制#个字到缓冲区 |
yy | 复制光标所在行到缓冲区 |
#yy | 例如,「6yy」表示拷贝从光标所在的该行“往下数”6行文字 |
p | 将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完成复制与粘贴功能 |
按键操作 | 功能描述 |
---|---|
r |
替换光标所在处的字符,支持nr
|
R |
替换光标所到之处的字符,直到按下「ESC」键为止
|
按键操作 | 功能描述 |
---|---|
u |
如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次“u”可以执行多次回 复
|
ctrl + r |
撤销的恢复
|
按键操作 | 功能描述 |
---|---|
cw |
更改光标所在处的字到字尾处
|
c#w |
例如,「c3w」表示更改3个字
|
按键操作 | 功能描述 |
---|---|
~ | 完成光标所在位置向后进行大小写切换 |
n~ | 完成光标所在位置向后n个字符进行大小写切换 |
按键操作 | 功能描述 |
---|---|
ctrl+g |
列出光标所在行的行号
|
#G |
例如,「15G」,表示移动光标至文章的第15行行首
|
在使用底行模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进入末行模式。
set nu //调出行号
set nonu //取消行号
「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,
再回车,就会跳到文章的第15行。
「/关键字」: 先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按
「n」会往后寻找到您要的关键字为止。
「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直
按「n」会往前寻找到您要的关键字为止
w:保存文件
q:退出vim
!:强制
w! q! wq!
!+指令:可以在不退出vim的情况下进行指令操作;
vs 文件名:可以实现分屏,进行多个文件编辑;
ctrl+w+w:屏幕切换(当前底行模式下输入的命令并执行只与当前光标所在文件有关,和鼠标点击哪个文件无关)
新用户在使用vim编写代码时并不像在VS下有自动缩进、自动补齐、代码提示、语法高亮等;下图是我们自己手动缩进的,这里也并没有行号;
1.在目录 /etc/ 下面,有个名为vimrc的文件,这是系统中公共的vim配置文件,对所有用户都有效。2.而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:“.vimrc”。例如,/root目录下,通常已经存在一个.vimrc文件,如果不存在,则创建之。3.切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~4.打开自己目录下的.vimrc文件,执行 vim .vimrc
1.采用手动配置:在普通用户下创建一个.vimrc文件,执行vim . vimrc ,例如:设置行号(set nu)、设置语法高亮(syntax on);保存并退出,简单测试一下 vim test.c 此时行号和语法高亮都能正常显示。虽然可行,但是不推荐这样做,我们可以采用第二种做法;
2.vim的配置相对比较复杂,某些配置还需要使用到插件,我们可以直接执行下面的代码,
curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
需要输入root密码,等待片刻后,手动执行source ~/.bashrc即可;
最后就可以看到和VS差不多的功能了
在Linux中,gcc是C的编译器,g++是C++的编译器;一个程序能够执行起来,一般需要经过四个步骤:预处理、编译、汇编和链接。以下图程序为例,对这四个阶段进行在Linux中的操作进行介绍。
#include
#define NUM 100
int main()
{
printf("hello vim\n");
//printf("hello vim\n");
//printf("hello vim\n");
//printf("hello vim\n");
//printf("hello vim\n");
printf("hello vim\n");
printf("NUM = %d\n",NUM);
return 0;
}
在预处理阶段主要负责的是头文件的展开、去掉注释、宏替换、条件编译等。以#号开头的是预处理指令:#define #if #include......此阶段产生【.i文件】
在Linux中,如果想要看到预处理后的结果,可以执行下面的指令:
[mlg@VM-20-8-centos lesson1]$ gcc -E mytest.c -o test.i
-E:只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面(xx.i文件)
-o:文件输出到文件(将mytest.c文件输出到test.i文件)
此阶段完成语法和语义分析,然后生成中间代码,此中间代码是汇编代码,但是还不可执行,gcc编译的中间文件是[.s]文件。在此阶段会出现各种语法和语义错误,特别要小心未定义的行为,这往往是致命的错误。
在Linux中,当预处理完生成了test.i文件后,想看到编译产生的汇编代码,可以执行下面的指令:
[mlg@VM-20-8-centos lesson1]$ gcc -S test.i -o test.s
-S:编译到汇编语言不进行汇编和链接
-o:文件输出到文件
此阶段主要完成将汇编代码翻译成机器码指令,并将这些指令打包形成可重定位的目标文件,[.o]文件,是二进制文件。此阶段由汇编器完成。
在Linux下,可以执行下面这个指令:
[mlg@VM-20-8-centos lesson1]$ gcc -c test.s -o test.o
-c:编译到目标代码
-o:文件输出到文件
此阶段完成文件中叼用的各种函数跟静态库和动态库的连接,并将它们一起打包合并形成目标文件,即可执行文件。此阶段由链接器完成。
[mlg@VM-20-8-centos lesson1]$ gcc test.o -o test
静态库:
一般扩展名为(.a或.lib)
静态库在编译的时候会直接整合到目标程序中(理解为拷贝),所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译。
动态库:
动态函数库的扩展名一般为(.so或.dll)
与静态库被整个整合到程序中不同,动态库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。这样从产品功能升级角度方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。
一般gcc和g++默认都采用的是动态链接
静态链接的过程就已经把要链接的内容已经链接到了生成的可执行文件中,就算你在去把静态库删除也不会影响可执行程序的执行;
动态链接这个过程却没有把内容链接进去,而是在执行的过程中,再去找要链接的内容,生成的可执行文件中并没有要链接的内容,所以当你删除动态库时,可执行程序就不能运行。
所以总的来说,动态链接生成的可执行文件要比静态链接生成的文件要小一些。
我们可以执行这样的指令:
[mlg@VM-20-8-centos lesson1]$ gcc mytest.c -o test1 -static
如果出现这种情况的报错,是因为在新版本的linux 系统下安装 glibc-devel、glibc和gcc-c++时,都不会安装libc.a. 只安装libc.so. 所以当使用-static时,libc.a不能使用。只能报找不到libc了。
/usr/bin/ld: cannot find -lc
安装glibc-static sudo yum install glibc-static
选项 | 功能 |
---|---|
-E |
只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
|
-S |
编译到汇编语言不进行汇编和链接
|
-c |
编译到目标代码
|
-o |
文件输出到 文件
|
-static |
此选项对生成的文件采用静态链接
|
-g |
生成调试信息。GNU 调试器可利用该信息。
|
-shared |
此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库
|
-w |
不生成任何警告信息
|
-Wall |
生成所有警告信息
|
-O0 -O1 -O2 -O3 |
编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
|
Linux 包含了一个叫 gdb 的 GNU 调试程序. gdb 是一个用来调试 C 和 C++ 程序的强力调试器. 它使你能在程序运行时观察程序的内部结构和内存的使用情况。
1.程序的发布方式有两种,debug模式和release模式debug模式:只有在debug下才可以进行调试,因为该模式下加入了debug调试信息;release模式:去掉了这些调试信息,是不可以被调试的2.Linux下gcc/g++出来的二进制程序, 默认是release模式3.要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项
从上图中可以发现debug版本和release版本下生成的可执行程序,文件大小是不一样的,也正说明了debug确实加入了一些调试信息
指令 |
简写 |
功能 |
---|---|---|
list |
l |
显示代码,每次列出10行,然后接着上次位置往下列 |
run |
r |
运行程序 |
next |
n |
单条执行(相当于VS下的F10) |
step |
s |
进入函数调用(相当于VS下的F11) |
break |
b |
在某一行或某个函数开头设置断点 |
info break |
i b |
查看断点信息 |
finsh |
执行到当前函数返回,然后停下来等待命令 |
|
|
p |
打印变量值或表达式的值 |
set var |
修改变量的值 |
|
continue |
c |
从当前位置开始连续执行程序,遇到断点停下 |
delete breakpoints |
delete |
删除所有断点 |
delete breakpoints n |
delete n |
删除序号为n的断点,可以多个删除 |
disable breakpoints |
禁用断点 |
|
enable breakpoints |
启用断点 |
|
display |
跟踪查看一个变量,每次停下来都显示它的值 |
|
undisplay |
取消对先前设置的那些变量的跟踪 |
|
until |
跳转到指定行 |
|
breaktrace |
bt |
查看各级函数调用及参数 |
info locals |
i locals |
查看当前栈帧局部变量的值 |
quit |
q |
退出gdb |
通过下面的代码来演示Linux下调试的技巧
#include
int fun(int p)
{
int i = 0;
int sum = 0;
for(i = 0;i <= p;i++)
{
sum += i;
}
return sum;
}
int main()
{
int x = 100;
int ret = 0;
ret = fun(x);
printf("ret = %d\n",ret);
return 0;
}
查看源代码 —— list 行号 和 list 函数名,可简写为 l 行号 和 l 函数名
调试程序:run、next、step配合断点相关的指令
finish、continue 和 until的区别
在函数执行过程中,finish是用来结束当前函数
在函数执行过程中,continue是用来直接到达下一个断点(在有断点的情况下)
until是用来跳转值某一行的
查看变量:print、display 以及undisplay
退出调试是quit,可简写为q
1.会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力 。2.一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。3.makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。4.make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefifile都成为了一种在工程方面的编译方法。5.make是一条命令,makefifile是一个文件,两个搭配使用,完成项目自动化构建。
想要生成可执行程序,需要经过预处理、编译、汇编和链接,它们之间存在依赖关系和依赖方法,如下图所示:
从上图可以发现,想要得到可执行程序,我们首先要得到test.o文件,要得到test.o文件就要得到test.s文件,以此类推,只要我们写好了源代码,通过这样的依赖关系及依赖方法,最终就能得到可执行程序。
当我们有很多文件时,就需要用到make/Makefile来对这些文件的依赖关系及依赖方法进行编写,就不需要每次gcc编译每个程序。当然上图中的关系是细分到每一步的,我们在编写的过程中不需要细分到每一步。
make是依赖于makefile的,要运行make,当前目录下就必须有makefile此目录名的文件。
makefile是阐述依赖关系和依赖方法的。
makefile文件命名只允许首字母大写或小写,其余都是小写。
在编写Makefile文件时,先表明依赖关系(如A : B 表示A依赖于B)紧接着书写依赖方法
以上两种写法都可以,解释一下第二种写法:
$@: 表示依赖关系中的目标文件,也就是mytest
$^ : 表示依赖关系中的文件列表,也就是mytest.c;(不仅仅只有mytest.c可能有更多)
1. make会在当前目录下找名字叫“Makefifile”或“makefifile”的文件。2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件,并把这个文件作为最终的目标文件。3. 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。4. 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)5. 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明 make的终极任务,也就是执行文件hello了。6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
项目清理类似于VS下的清理解决方案,也就是将之前生成的所有文件清理掉,如果我们进行手动清理,那么在有多个文件的情况下,手动清理未免太过麻烦,此时我们可以将项目清理加入到Makefile文件中。
.PHONY类似于关键字,是用来修饰clean的,它是一个伪目标,表示总是被执行。
问题:为什么在使用Makefile时,只需要输入make;使用clean时,输入却是make clean?
解析:因为Makefile文件在执行是从上至下的,只输入make,它会默认执行第一个依赖关系
回车(\n):表示将光标移动到文本的行首;
换行(\r):表示将光标移动到下一行,但是相对位置并没有发生改变(列不变)
我们首先观察以下三种不同的程序运行起来有什么不同的现象
//程序1
#include
#include
int main()
{
printf("hello Makefile!\n");//带换行
sleep(3);//休眠3秒
return 0;
}
//程序2
#include
#include
int main()
{
printf("hello Makefile!");//不带换行
sleep(3);//休眠3秒
return 0;
}
//程序3
#include
#include
int main()
{
printf("hello Makefile!");//不带换行
fflush(stdout);//刷新缓冲区
sleep(3);//休眠3秒
return 0;
}
程序1:立即打印出 hello Makefile! 然后换行,休眠3秒后,程序结束;
程序2:未立即打印 hello Makefile! 休眠3秒后才显示打印内容,程序结束;
程序3:立即打印出 hello Makefile! 休眠3秒,程序结束;
比较三个程序,我们发现他们打印的时机不一样,这是由于行缓冲造成的。
什么是行缓冲?
当输入输出遇到换行符的这类缓冲定义为行缓冲。标准输入和标准输出都是行缓冲。
缓冲区刷新的条件:
1.进程结束
2.遇到\n
3.缓冲区满
4.手动刷新缓冲区fflush(stdout)
5.调用exit(0);但是还可以调用_exit(0),不刷新缓冲区。
#include
#include
#include
int main()
{
int i = 0;
char bar[102];
memset(bar, 0 ,sizeof(bar));
const char *lable="|/-\\";//类似于加载的过程
while(i <= 100 )
{
printf("[%-100s][%3d%%][%c]\r", bar, i, lable[i%4]);
fflush(stdout);//刷新缓冲区
bar[i++] = '#';
usleep(50000);
}
printf("\n");
return 0;
}