我们平时经常在手机或电脑(Windows)上的应用商店下载各种软件,点击下载,进度条就开始走,下载完成,我们的手机或电脑上就多了一个安装包,然后执行安装包就开始安装。
实际上,安装包很大,而软件商店APP却很小,这是为什么呢?
软件商店其实就是一个软件包管理器,通过网络建立起你和服务器之间的联系,你通过它来操作,它来帮你去服务器上下载软件包,下载完成之后再由你来执行安装。
而 Linux 下如果没有图形化界面,我们该怎么操作呢?
有一个古老的方法就是下载程序的源代码进行编译,得到可执行程序。但这样太麻烦了,为什么不能像应用商店那样,把程序打包起来放服务器上,通过应用商店来下载呢。
所以 yum 就应运而生了。
下面简单介绍一下 yum 最简单的三种用法。这些都有一个大前提,那就是网络必须是畅通的!
输入yum list
指令之后会以 list 的形式将能下载的软件包列出来:
打印出的信息分三列。
第一列是软件包的名称,其中像 x86_64 表示这是64位系统的安装包,32位系统的安装包后缀为 i686 ,下载时要和系统匹配。
第二列则分别是主版本号.次版本号.源程序发行号-软件包的发行号.主机平台
,其中 el7 表示的是 CentOS7 或者是 Redhat7 ,像 el6 表示的就是 CentOS6 或 Redhat6 。
最后一列则是软件源,例如某个应用是来自小米应用商店。
而这个指令把所有能安装的包全都列出来了,
而我们要查找的不过是某一个,
所以我们需要的是按关键字检索,
所以 grep 就派上用场了:
通过命令 sudo yum install filename
即可安装软件,
但很多情况下我们都是以普通用户登录的,
而安装程序一般是要涉及系统层面的操作,
所以普通用户是没办法安装的,
需要切成 root 或通过 sudo 进行权限提升操作。
需要注意的是,yum 不能像我们的应用商店那样同时下载多个软件,只能一个个安装。
安装过程中可能会让输入 y/n 选项确认安装,-f 选项则可以跳过询问强行安装。
下面是一个简单的安装例子:
sudo yum remove filename
其中 sudo 是权限提升,因为卸载也要涉及系统层面的操作,所以需要权限提升。
可以加 -f 选项,是强制卸载的意思,中间就不会再有询问你是否继续卸载的提示。
filename 就是我们要卸载的文件名。
一个简单的卸载例子:
首先明确一个概念,我们平时写代码是在编辑器上写的,编译代码那是编译器的事情。
而 vim 是基于 vi 编辑器,增添了许多新特性的一款强大的文本编辑器。
vim 有三种常用模式:正常模式(normal mode)、插入模式(insert mode)和底行模式(last line mode),也叫命令模式(command mode)。
一开始进入 vim 之后是正常模式:
我们如果想写代码的话按 i 键或 insert 键进入插入模式才可进行正常写入:
写完代码想要退出的话按用 esc 键回退到正常模式,然后 shift + : 进入底行模式:
此时光标在底行,输入 wq 为保存退出,输入 w 为保存,输入 q 为退出, !q 表示强制退出。
以上是 vim 最基本最简单的编写代码的操作。
下面介绍一下正常模式下的一些操作方法。
正常模式下我们是无法随意编辑的,需要按 i 键或 insert 键切入插入模式。
正常模式下我们也没办法直接退出,需要先回到正常模式,再进入底行模式输入相关命令。
所以可以把正常模式简单描述为一个二叉树的节点,插入模式和底行模式是这个节点的两个子节点。
而在正常模式下可以进行很多操作,下面简单介绍一些常用的操作:
- 我们可以通过键盘上的上下左右键进行光标的移动,但原始的 vim 是通过小写字母模式下的 h、j、k、l 分别完成光标的 左、下、上、右 移动的。
- shift + g:光标移动到文本的最后
- gg:光标移动到文本的开头
- $(可能需要配合 shift 键使用):光标移动到所在行的行尾
- ^(可能需要配合 shift 键使用):光标移动到所在行的行首
- w:光标移动到下一个单词的开头
- e:光标移动到下一个单词的末尾
- b:光标回退到上一个单词的开头
- 数字n+shift+g:光标跳转到第n行的行首
- x:删除光标处的字符
- 数字n+x:删除光标所在位置往后的n个字符(包含自己)
- shift+x:删除光标前面的一个字符
- 数字n+shift+x:删除光标前面的n个字符
- dd:删除光标所在行
- 数字n+dd:删除光标所在行下面的n行(包含自己)
- yw:赋值光标所在处到行尾的字符
- 数字n+yw:复制光标所在处后n个字符
- yy:赋值光标所在行
- 数字n+yy:复制光标所在行下的n行
- p:粘贴
- 数字n+p:连续粘贴n次
- u:撤销上一次操作
- ctrl+r:撤销上一次撤销
另外,很多场景下可能还要用到批量化操作:
对于批量化选择,可以在正常模式下用 ctrl + v 进入V-BLOCK 模式,然后通过 h、j、k、l 键进行上下左右移动完成批量化选择。
对于批量化注释,可以在选中要批量化操作的区域之后按 大写的i,然后敲下两个 // (针对C/C++),完成之后 esc 退出即可:
对于批量化删除,可以在选中区域之后,按 小写d 进行删除:
对于批量化复制,选中之后按 小写y 即可。
esc 回退到正常模式下通过 shift+: 组合键可以进入底行模式。
在底行模式下也有诸多命令,这里简单介绍几种:
q:退出
q!:强制退出
w:保存
wq:保存退出
e filename:打开文件 filename 进行编辑
r filename:在当前文件中写入文件 filename 的内容
vs:再开一个窗口,窗口下仍为当前文件,通过大写的H、L完成两个窗口之间的切换
vs filename:再开一个窗口,窗口下为文件 filename ,通过大写的H、L完成两个窗口之间的切换
set nu:显示行号
s/old string/new string:将当前行第一个 old string 替换为 new string
s/old string/new string/g:将当前行的所有 old string 替换为 new string
%s/old string/new string/g:将当前文件中的所有 old string 替换为 new string
x1,x2s/old string/new string/g:将 x1 到 x2 行的所有 old string 替换为 new string
5,10 m 15:将5-10行的内容剪切到15行下面
5,10 co 15:将5-10行的内容复制到15行下面
在目录 /etc 下面有一个 vimrc 文件,在该文件中可以通过命令对 vim 进行配置,配置对所有用户都有效。
在每个用户的主目录下,也可以建立各自的 vimrc 文件,命名为 .vimrc ,用户可以在这个文件里面通过输入各种命令进行配置。
vim 的配置选项数不胜数,大家可以在网上多查一下,根据需要设置。
现在我们写好了一个C/C++代码,该怎么把它变成一个可执行程序呢?
很简单,编译生成可执行程序就完事了。
Linux 提供的 gcc/g++ 编译器,从名字来看就知道,前者是编译C语言代码的,后者是编译C++代码的,当然因为C++兼容C语言,所以也能编C。
假如我写了一个 test.c 文件,直接 gcc test.c
编译,就能生成一个 a.out 可执行文件,这是 gcc 最简单的用法。
当然如果不想默认生成 a.out ,也可以自己指定,只需要加一个-o
选项:gcc test.c -o test
。
但它的功能远不于此。
我们知道,C语言代码在生成可执行程序之前会经历预编译、编译、汇编、链接四个阶段。
至于四个阶段做了什么这里不是重点,就不祥谈了。
重点是我们可以通过 gcc 的一些选项观察到这些阶段。
经过预编译阶段会生成 .i 文件,我们可以通过 -E 选项,让编译器在执行完预编译阶段之后就停下来:gcc -E test.c -o test.i
可以用 cat 指令查看一下 test.i ,看看编译器在预编译阶段做了些什么:
一个十几行的代码变成了八百多行,实际上就是进行了头文件展开,当然还有宏替换和注释删除。
经过编译阶段会生成 .s 文件,这时C语言代码就被翻译成了汇编代码。
我们可以通过 -S 选项,让编译器在执行完编译阶段后就停下来:gcc -S test.c -o test.s
可以用 cat 指令查看一下确实是翻译成了汇编代码:
汇编阶段就是把此前翻译得到的汇编代码再次翻译,生成机器可识别的二进制文件,文件后缀为 .o 。
我们可以通过 -c 选项让编译器执行完汇编阶段就停下:gcc -c test.c -o test.o
我们可以使用 od 命令查看生成的 .o 文件,这里是以八进制展示的:
经过汇编生成的 .o 文件虽然是二进制语言,可以被机器识别,但仍无法执行:
因为还缺少最后一步 —— 链接。
在 test.c 文件中,我们调用了 printf 函数,但这个函数我们并没有定义,头文件 stdio.h 中仅仅也是对函数进行了声明,那最后该函数是如何执行的呢?
这就是链接阶段所做的动作了。
链接,是实现程序和库的链接,而这里要链接的,就是C语言的函数库。
在目录 /usr/lib64 下有这样一个文件:
这个实际上就是C语言的函数库,而这里面才有我们所调用的函数的实现。
而在链接阶段,gcc 会到这个系统默认的搜索路径下面去寻找 printf 函数的实现,这样我们的程序调用的 printf 才得以执行。
这就好比我写作业遇到不会的题了,就跑去翻习题详解,这就是一种链接,作业和答案的链接。
这样的一种链接方式叫动态链接,执行程序时遇到第三方库的文件跑去库里找对应的实现,边运行边链接。
而动态链接的库就是动态库,动态库在 Windows 下一般是以 .dll 为后缀,也有部分以 .lib 为后缀,在 linux 下则是以 .so 为后缀,以 lib 为前缀,比如上面的 libc.so。
很明显,printf 函数执行的时候我要去链接,而链接的这个过程实际上就减慢了执行效率,而且通过动态链接生成的程序可移植性也不好,如果机器上没有相应的动态库,那程序就无法运行。
当然,动态链接也有不少的优点。因为要跑去远程链接,所以要链接的文件信息和我的可执行程序是各自独立的,这样一来缩小了可执行程序的占用空间;二来降低了耦合度;三来方便了开发,写一个动态库可以让多个程序共用,而且程序后续的维护更新也不需要对程序进行二次编译,只需要更新一下动态库即可;四来实现了进程间的资源共享,进程链接时由于动态库已经被加载到了内存中,另一个进程同时链接时就不需要再次加载了。
gcc 如果不加以指定的话,生成的可执行程序默认就是动态链接的:
与动态链接相对的就是静态链接。
因为 gcc 默认生成的可执行程序是动态链接的,如果想改为静态链接则可以使用选项 -static :gcc test.c -o test -static
我们这里因为没有对应的静态库所以就失败了。
C语言标准静态库的安装可以使用命令:sudo yum install glibc-static
,这时再编译一下:
这时我们观察到了一个现象,就是静态链接的程序比动态链接的程序大得多。
这是因为静态链接会把静态库和程序打包起来,程序执行时就不需要跑去动态库链接,因此程序就大得多。
这样做有什么好处呢?
因为不需要远程连接了,进程链接时不需要加载动态库了,执行效率会相对高,而且程序不依赖于库就可以执行,可移植性大大提高。
但这样也带来了不少缺点,程序的空间会大大增加,另外,当程序维护更新时,动态链接的程序直接更新动态库即可,而静态链接的程序更新后必须重新编译,降低了开发体验。
另外,静态库是以 .a 为后缀的。
除了上面介绍的几个选项,gcc 还有一个选项 -g。
linux 默认生成的可执行程序是没办法调试的,相当于是 release 版本。
-g 选项是生成调试信息,就相当于在 debug 模式下编译,这样生成的程序是可以调试的。
以上介绍的所有选项,对 g++ 同样适用。
上面说了,gcc 有一个 -g 选项是生成调试信息,有了这个调试信息我们就可以对程序进行调试,而 gdb 就是 linux 下的调试工具。
首先先生成具有调试信息的程序:
可以看到程序的大小相比默认生成的程序要大,说明确实有调试信息。
下面就介绍一下 gdb 的使用。
gdb filename
:开始调试程序:
ctrl d 或 quit/q
:退出调试
list/l + 行号(可省略)
:打印源代码
list/l + 函数名
:列出指定函数的源代码
next/n
:逐语句执行,遇到函数不会跳到函数内部
step/s
:逐过程执行,遇到函数会跳转到函数内部
break/b + 行号
:在指定行打断点
info/i break/b
:查看断点信息
delete/d break/b num
:删除断点num
disable break/b num
:禁用断点num
enable break/b num
:启用断点num
print/p + 变量名
:打印变量的值
diplay + 变量名
:跟踪查看某变量,每次执行后都会打印该变量的值
undisplay + 变量名
:取消对某变量的追踪
run/r
:从开始执行至下一断点
continue/c
:从当前位置执行至下一断点
until + 行号
:跳转至指定行
finish
:执行到当前函数返回处
breaktrace/bt
:查看当前函数调用情况
info/i locals
:查看当前栈帧(正在执行的函数)局部变量的值
make 是一个指令,Makefile 是一个文件。
先通过一个例子来见识一下它是做什么的:
首先创建一个Makefile文件:
touch Makefile
用 vim 编辑 Makefile 文件:
保存退出之后使用 make 命令执行一下:
通过这个简单的例子我们发现通过 make 一次性完成了对多个文件的编译,且这多个文件有依赖关系。
实际上,当我们创建项目的时候,总是有多个源文件,最简单的就是函数的声明和定义分离,这样生成解决方案的时候要对多个源文件按一定顺序进行编译,这样是不是太麻烦了呢?所以 make/Makefile 就是帮我们完成这个工作的。
解释一下 Makefile 文件内代码的意义:
test:test.o
:表示 test 文件依赖于 test.o 文件,具体是怎么依赖的呢?就是下面这行
gcc test.o -o test
。上面这行我们称之为依赖关系,下面这行我们称之为依赖方法。
知道了这个,我们再了解一下 make 是怎么工作的:
首先 make 会在当前目录下找名为 Makefile/makefile 的文件,
进入文件之后,不加以指定的话 make 会将从上至下第一个文件作为目标文件,上面的 test 就是目标文件。
然后根据目标文件的依赖关系,如果目标文件所依赖的文件不存在,则以依赖文件为目标文件,去寻找新目标文件的依赖关系,以此类推…个人感觉类似于递归。
在执行过程中,如果有一层关系断掉了,可能是因为依赖文件不存在,也可能是因为依赖方法有问题,那 make 就会报错,并结束执行。
需要注意,
我们说不加以指定的话 make 会以第一个文件作为最终的目标文件,那怎么指定让 make 以其他文件作为目标文件呢?也很简单,
make + filename
就好了:
如此,对于多文件项目就可以通过一个 make 构建完成了。
会构建项目,当然也要会清理项目:
vim 编辑 Makefile 文件:
其中 .PHONY 是生成一个伪目标,即没有依赖关系的目标,这样它会有一个特性,总是可以被执行。
怎么理解总是可以被执行呢?像上面的 make 生成一次解决方案后,如果不对源码进行修改,再次 make 就不会执行:
而伪目标则可以无限执行:
保存退出后
make clean
执行:这样就完成了项目的清理。
这里只简单介绍一下具体步骤。
首先检查系统装没装git:git --version
:
如果没装的话需要先安装:sudo yum install git
创建远程仓库,这里以 Gitee 为例,可以自行设置一下初始化仓库和模板:
创建完成后将远程仓库地址克隆到本地git clone URL
:
将想上传的文件存在本地目录中,通过三板斧上传即可。
git add filename
:
git commit . -m "log"
:
这一步是提交改动,. 代表是当前目录,-m “log” 是提交日志,log 是日志内容,这个不可省略。
git push
:
这一步是同步到远程仓库,需要输入账号密码,完成这一步后就可以在远程仓库看到上传的文件了: