序言
Linux下安装软件的常见方式主要有三种,源代码安装(下载到程序的源代码,自行进行编译,得到可执行程序),rpm包安装(获取rpm安装包,通过rpm命令进行安装。(未解决软件的依赖关系)),包管理器安装。其中包管理器最为好用,不同版本的Linux不一样,Centos上主要用yum,Ubuntu上主要用apt。yum和apt都是包管理器,就是两款软件,对应到Linux系统里就是两个指令,包管理器就相当于是手机上的应用商店。
Linux软件生态
如何评估一款操作系统的好坏?操作系统被设计出来之后,最重要的事情是什么?
操作系统设计出来的目的一定是要让更多的人用。操作系统会形成自己的圈子,会有特定的人群去使用。如何让使用的人越来越多?那就是让这个圈子越来越好,假设是Centous,比如在论坛发各种企业里遇到过的问题,踩过的坑,那这样一来小公司看到之后肯定也愿意去使用Centous,因为大公司把遇到过的坑都发在了论坛上。总之,圈子越来越完善,使用的人就会越来越多,论坛,社区,相关软件配套的文档,资料......甚至为了解决对应群体的一些问题去开发出相关的软件出来,ls,mkdir这种指令就是这种情景下出现的产物!这些都是为了让越来越多的人去使用操作系统。面向不同人群的操作系统有自己对应的圈子,每个圈子里都在干着这样的事情,去维护自己的用户群体,形成他们心目中的刻板印象。
为什么会有人免费为特定的社区开发软件?还发布,还提供云服务器让你下载?
下图中的软件包服务器也是云服务器,软件工程师将软件写好之后打包并且放在软件包服务器的特定目录下边,然后用户用包管理器发出对应的需求,包管理器就会去软件包服务器里去查找并发出下载请求,软件包服务器就会下载对应软件返回到你的云服务器,总之就是软件不是在你的云服务器上的,而是在另一台服务器上。
有了上边的简单认知,接下来去回答上述问题,首先写操作系统的人为了让自己的操作系统被更多人用到,肯定是愿意去开发软件的,而且不同的操作系统之间存在着竞争,他们就愿意去开发更多的软件去完善自己的生态,目的还是为了更多的用户。
那为什么软件包不止发布在自己的系统上,还发布到了其他操作系统?从开发者的角度,自己开发出来的软件肯定愿意让自己的软件去让更多的人用到,不同的社区,不同的操作系统就意味着有更多的人。
那这些操作系统的那么多的软件包服务器需要花钱吗?每种操作系统都有自己的用户,被企业广泛使用,那企业肯定是希望被自己使用的操作系统不停的在更新,操作系统软件的开发者万一哪天不干了,资金不足了,那这些使用他们的企业或者个人怎么办?企业自己开发软件需要的成本远远比提供几台云服务器多,因此,他们会主动向这些开发者提供云服务器。他们彼此之间是互利互惠的。
包管理器怎么知道应该去哪一个云服务器下载软件?在你的云服务器上会内置配置文件,里边一定包含了里边软件对应的软件包服务器的超链接url(公网IP)。
镜像问题。
操作系统都是外国人写的,包管理器里配置文件对应的云服务器的url都是国外的,因此下载软件的时候会非常的慢,甚至都因为墙的问题不让你去访问,这就导致不稳定。所以国内的各种组织,社区,公司,像清华,阿里,就将国外的那些软件包服务器镜像到国内来,说白了就是将国外服务器上的软件包拷贝到国内的软件包服务器里,并且你使用的云服务器里的配置文件里指向国外的链接变成指向国内的,所以就会加速我们去安装软件,将国外的配置文件替换成国内的配置文件称为镜像(软件)源配置,这些事情都是由你对应云服务器的公司搞完了。这种软件源配置很依赖国外的软件源的丰富程度,Centous在24年停更的意思就是其对应的软件包不再更新,版本不再去维护了,里边特有的软件不再更新了,供给端停滞了。
软件包依赖问题。
yum和apt会自动给我们提供一个功能叫自动解决依赖关系,依赖关系是什么?一个软件会用到其他的库,就比如你自己写的C语言用到了C标准库里的代码,那得需要得把对应得库得安装到你的软件里才行,不然C语言就跑不起来。总之就是你的软件依赖了别人的库,在安装软件的时候得把对应得库也得安装才行,在安装软件的时候,yum和apt会自动帮我们把那些软件依赖的库也安装上。
正是因为有依赖问题,文章最开始说的其他两种安装软件的方式就比较麻烦与易出错了,windows将所有的软件包包括依赖关系都打包到了一个文件里了,并且用图形化界面的方式,所以就比较方便。
开发工具
yum具体操作
搜索软件,yum list | grep 软件名(list罗列目标的软件包服务器所有的软件包)。安装软件,yum install -y 软件名,Ubuntu里就用apt,-y的意思就是不要给我安装提示了。安装软件的时候一定是用root权限,sudo提权或者用root账号。(卸载软件,yum remove 软件名)。
补充知识:在你目标的软件包服务器上有一些软件叫做基础软件,它们是官方的并且比较稳定,开发者将软件写好发布到不同的操作系统上的时候不会放在标准的软件包服务器上,而是会放在另一台软件包服务器上,这个叫做扩展软件包服务器,当时间久了之后,bug少了之后,有些合适的软件就会迁移到基础软件包服务器上。
编辑器vim
vim是编辑器,只用来写代码!
补充知识:IDE叫做集成开发软件,就是将编辑代码,编译,运行,调试这些功能集成在一起,在Linux系统下,这些开发工具都是独立的,写代码---vim,编译代码---gcc/g++,调试---gdb、cgdb,构建工具---makefile、make、cmake,提交代码---git。
vim的多模式
vim里主要有三种模式,分别是命令模式、插入模式、底行模式,其中命令模式是核心,先来看一个总览。vim打开文件的时候,如果这个文件不存在会直接帮我们创建一个新文件,但只有保存退出了,该文件才会真实存在在当前目录里。
命令模式
大家可以先回忆一下windows里word的使用方式,打开了word之后直接往里边输入东西,然后往往还经常能用到ctrl+c,ctrl+v,这里的ctrl+c,ctrl+v本质上就是命令,命令可以增加编辑效率,命令的背后是自动化和批量化,ctrl+c自动把内容全部就复制了,ctrl+a可以选择所有的内容,命令越多,效率就会越高。
光标移动:
shift+g = G: 进入文本末端
gg: 进入到文本开始
n(数字)(下文都拿n代替) + shift + g: 定位到任意一行
(以下两个也叫做锚点)
shift + 4 = $: 定位到当前行结尾
shift + 6 = ^: 定位到当前行的开始
上下左右键可以进行光标定位
hjkl也可以控制光标定位(h是左、j是下、k是上、l是右)
补充:
为什么代表这个含义?hjkl在键盘里是连着的,h在四个按键的最左边表示左,l在四个按键的最右边表示右,j表示jump往下,k表示king往上。
为什么有了上下左右键还要有hjkl,原因就是老式的键盘是没有方向键的(如下图),选择hjkl就是因为就在手指头下,比较快,然后现在为了前后兼容,就把之前hjkl的功能保留了下来,所以光标移动就有了两种方式。
w(word):以单词为单位自左向右移动
b:以单词为单位自右向左移动
n+w,n+b就可以一次性移动过n个单词
复制粘贴剪切:
yy:赋值,n+yy,一次性复制n行
p:粘贴到当前行的下一行,n+p,一次性粘贴n次
dd:删除当前行,n+dd,删除n行,dd的时候已经相当于剪切了,再p就粘贴出来了
(有了下边两条指令,只要你没有退出vim,随时可以撤销,一旦退出就无法撤销了)
u:撤销操作
ctrl+r:对u的撤销进行撤销---又一次编辑操作
~:大小写快速转换
r:替换光标所在位置的一个字符,n+r替换n个字符(有了这个命令就不用从命令模式切换到插入模式了)
shift+r = R,进入替换模式,进行批量化的内容替换
x:删除光标所在的字符,n+x,向右删除n个连续字符
shift+x=X,向左删除,n+shift+x,向左删除连续n个字符
批量化注释/去注释:
注释:
1.ctrl+v(进入视图模式)
2.hjkl选择要注释的区域(n+h/j/k/l,批量选中,上文说的光标定位也可以直接拿来用,不止只有hjkl这种选择区域的方式)
3.shift+i = I(进入插入模式,因此视图模式和插入模式是可以直接相互转化的)
4.//
5.Esc(将第2步里选中的注释就直接全部注释掉了)
去注释
1.ctrl+v
2.hjkl选择区域
3.d
注:以上方式除了可以批量化注释和去注释,也可以批量化插入和删除其他你想的东西。当然也可以用视图模式+批量选中之后结合上边其他的命令组合起来达到任何你想要的效果。
底行模式
注意:在底行模式输入内容回车之后需要再次输入:之后才能输入其他的内容或Esc退出底行模式。
以下三条指令加!的情况一般是当用vim打开的文件的拥有者没有写权限的时候用,本质就是权限问题,保存就是写,没有写权限的话就无法正常保存退出了。
w!:[强制]保存
q!:[强制]退出
wq!:[强制]保存并退出
ZZ:退出(命令模式下)
如果vim打开文件,突然终端退出,vim形成临时文件,默认在当前路径下有一个.swp临时文件(ls -la可以看见),把这个文件删了就可以正常打开vim,否则在打开文件的时候会先弹出来下边这个东西。
set nu
set nonu
/key + n:匹配搜索,key是你想在当前文件里查找的关键词,按n是匹配到下一个key
!shell指令:使用场景,在不退出vim的情况下直接对代码进行编译和运行
%s/dst/src/g:(s是替换的意思,g是全部的意思,将所有的dst替换成src)
vs 文件名:分屏操作,光标在哪,哪个文件就可以被编辑,ctrl+ww,切换光标到哪一个分屏(如果该文件不存在会自动帮我们创建一个),注意最下面的提示栏只有一个,光标在哪就表示是哪个文件正在被编辑。(分屏操作支持多文件代码)
当vim退出时候,光标在第n行,当再次打开的时候,还在第n行
vim 源文件 +n:vim打开源文件,+n表示直接定位到第n行,是从第一行开始往下算的
插入模式
不用多说,就是编辑。补充:从命令模式到插入模式除了i还有a和o
i:进入插入模式之后光标不动
a:进入插入模式之后光标向右移动一个字符
o:进入插入模式之后光标向下移动新起一行
简单vim配置原理(了解即可)
注意以下的配置是在普通账号下,vim是系统中一条基本的指令,用which vim就可以知道,打开vim的时候,vim默认会去当前用户的家目录下查找一个配置文件叫.vimrc,在启动的时候,vimrc的配置项作为vim启动的原始配置文件,也就是说就自动把vimrc里的配置项都给做了。因此,对vim做基本配置的本质就是把配置项写进/home/XXX/.vimrc配置文件里。没有.vimrc就自己建一个。配置后,vim只会在当前账号下有效。
补充:
有了vim的知识就可以去解决无法sudo的问题了。一定是使用root账号将用户添加到白名单里,并且由于sudoer文件的权限问题,这个可以自行查看,必须要强制保存退出才行。
编译器gcc/g++
区别
gcc是C编译器,只能用来进行编译C语言。g++是一个C++/C语言编译器,不过其在编译C语言的时候是直接把C语言当成C++来编的。因此编译C语言还是用gcc比较好。
使用
两者选项完全一样,以gcc作为演示。程序的翻译过程一共有4步,预处理,编译,汇编,链接。为了用gcc体现出来这些过程,我创建了一个文件夹叫code,里边创建了一个code.c文件去演示。
1.预处理:分别是头文件展开,宏替换,去注释,条件编译。
gcc 文件名,会直接编译成一个文件名.out的可执行文件,这个名字是默认的。如果想自己指定名称,就可以用-o去指定名字,gcc就会把结果编译到-o指定的文件里。./的意思是当前路径,在Linux里执行命令的前提是先找到这个命令,系统指令可以直接找到,当前指令找不到就需要指定路径。另外gcc编译的时候是一步到位的,直接就变成二进制可执行程序了,那我想看到各个阶段的过程怎么办?
-E:gcc -E code.c -o code.i,.i文件是约定的存放预处理之后的文件内容的,-E的意思是程序开始翻译(从源文件变成可执行程序),做完对源程序的预处理就停下来。
头文件展开:就是把头文件里的内容拷贝到源文件的头部。那为什么不直接把声明和定义放一起?因为C/C++都是以库的形式呈现内容的,说白了就是只想给你看声明不想给你看定义,因此就头文件源文件分开,起到一定的保护效果。
条件编译(下图):本质是对代码做裁剪,就比如下边#define了VERSION1,那么main函数里就只会保留打印我是version1版本的功能那句话,红色圈出部分就被裁剪掉了,为什么会有条件编译这个功能?一般来说,软件都有免费版和收费版,公司里不可能分开来维护,这时候就用条件编译的方式,不就可以轻松限制免费版本的一些功能了吗,而在维护的时候其实就只要维护收费版就可以了。条件编译还可以防止头文件被重复包含(图2)。
//图2
2.编译:将C语言翻译成汇编语言。
-S:gcc -S code.i -o code.s,(也可以从code.c开始编译),-S的意思是程序开始翻译,将C语言翻译成汇编语言就停下来。
3.汇编:将汇编语言翻译成二进制文件(.o文件,全称是可重定位目标二进制文件)。
-c:gcc -c code.s -o code.o,-c的意思是程序开始翻译,当我们把汇编语言翻译成二进制就停下来。
4.链接:
gcc code.o -o code
以上就是编译的全部操作,记忆方式,键盘左上角,ESc,对应的文件.iso。
重谈翻译过程
问题1:为什么要把C语言翻译成汇编语言?
这跟历史有关,在最一开始的时候,程序员都是采用二进制编程的方式,为什么计算机是二进制的,为什么二进制就可以被CPU执行被CPU运算?在计算机里的硬件一般都是采用高低、强弱这种方式来表示数据,处理数据的,用二进制表示最为方便。CPU内部在设置硬件电路的时候是会去刻画自己的指令集的,指令集的意思就是将一连串的指令集合在一起,说白了就是一个程序,比如从座位上取水这个程序就是由指令集构成,站起,走路,拿水,回来,递水杯。这些指令集最终会被设计成二进制的,所以二进制有的代表指令,有的代表数据。翻译语言的本质就是转化成CPU能够识别的指令集。
用二进制写代码的方式太过于复杂且易出错,因此就诞生了汇编语言,将本来用二进制表示的指令/数据用英文单词的方式去表示,比如0101->mov。有了汇编语言就需要编译器了,至此,编译器诞生了,这时候的编译器的作用就是将汇编语言->二进制。随着时代的发展,人们想要更简便的编程方式,更接近人类的自然语言,C语言就此诞生,其也需要编译器,但是它就不是一下子编译成二进制了,历史永远是向前兼容的,自然就是将C语言先变成汇编语言,然后再由汇编语言到二进制,这个工作别人已经搞完了,正所谓站在巨人的肩膀上。
所以问题1的回答就是,历史是由汇编语言发展而来的,C语言翻译回去是逆向历史的过程。
大家发现了没,发明一门语言就得有对应的编译器(是软件),那如果现在想发明出了一门语言,语法层面如何保障?每种语言都需要用应的编译器,语法并非凭空产生,而是要去对应其编译器的翻译规则,所以语法的本质,就是编译器的翻译规则,编译器早在造出来的时候就内置了语法规则。所以是先有语言还是先有编译器?是先有语言后有编译器,因为编译器是需要翻译规则的,这个规则就是语法。那是先有汇编语言,还是先有用汇编语言编写的汇编编译器?发明出了汇编语言之后,不可能是用汇编语言写汇编编译器,就算写完了谁来编译这款软件呢?因此一开始是拿二进制去写的第一款用来编译汇编语言的编译器,之后再用汇编语言写了一款编译器来编译自己,从而代替了第一代的编译器,这种叫做编译器自举。(其他语言也有类似的情况)
问题2:.o二进制文件能否直接运行?
答案是不能。.o文件也叫做可重定位目标二进制文件,其无法直接运行,原因跟链接有关。
以C语言为例,链接就是把函数调用和标准库里的函数实现关联起来的过程。ldd可以查看我的可执行程序(code)依赖哪些库。下图中圈出部分就是C语言标准库,在开发C语言的过程中,需要有编译器,头文件(声明),库文件(实现)。.o文件里只是把我自己写的代码编译成了二进制文件,但是里边并没有对应库函数的实现,只有声明,没有定义,所以就无法运行。
gcc是专门用来编译C语言,它能找到库和头文件在哪里,因此你就算没有指明要链接哪个库,头文件和标准库都是在固定路径下,gcc能找到。
库
库分为静态库和动态库。
静态库 动态库
Linux .a .so
Windows .lib .dll
(Linux系统不关心文件后缀,只是内核不关心,但是编译器,链接器还是关心的。)
Linux系统里对库的名字是有要求的,libname.so/.a,去掉.so/.a和lib剩下的就是库的名字,例如上文所说的C标准库,libc-2.17.so,c(-2.17指的是它的版本)就是C标准库的名字。
问题1:什么是动静态库?(感性认识)
动态库:程序在执行过程之中通过地址链接跟库发生关联,库函数跳转到库里执行之后再返回程序往后执行,这个通过地址跟程序链接起来的库就叫做动态库。动态库的特点就是一旦库缺失,所有相关程序就无法运行了。动态库链接的是关联关系(地址),链接的时候对应函数更改的是地址库地址。
静态库与静态链接:把库中的方法(我们需要的),直接拷贝到你的程序中,叫做静态链接。静态库和静态链接,一旦链接成功,可执行程序不再需要静态库。
总结:
操作证明
gcc默认编译,采用动态链接的方式。编译程序,采用动态库和动态链接是最佳实践。file code查看详细类型。
接下来我们想用静态链接的方式,静态库和动态库是两种不同的文件,首先得保证C静态库安装了(默认是没有安装的)。由下图可知,静态库和动态库编译链接出来的可执行文件大小差了100倍。