开发

只有程序员才能读懂的三国演义(二)

这是通过三国演义串起操作系统的原理,之前写过五篇小马创业篇,这次改编为三国演义篇。

上一篇,我们讲到,刘备三顾茅庐请来诸葛亮,从而战略,战术,政务全方位体系都建立了。

第八回:诸葛阵法复合多变,进程同步方法多样

自从诸葛亮当军师以来,排兵布阵便开始使用复合式战法,往往是多个将军带着多只部队配合作战。

诸葛亮排兵布阵风格一,可见于赤壁大战后对于华容道的安排。

孔明便与玄德、刘琦升帐坐定,谓赵云曰:“子龙可带三千军马,渡江径取乌林小路,拣树木芦苇密处埋伏。今夜四更已后,曹操必然从那条路奔走。等他军马过,就半中间放起火来。虽然不杀他尽绝,也杀一半。”云曰:“乌林有两条路:一条通南郡,一条取荆州。不知向那条路来?”孔明曰:“南郡势迫,曹操不敢往;必来荆州,然后大军投许昌而去。”云领计去了。又唤张飞曰:“翼德可领三千兵渡江,截断彝陵这条路,去葫芦谷口埋伏。曹操不敢走南彝陵,必望北彝陵去。来日雨过,必然来埋锅造饭。只看烟起,便就山边放起火来。虽然不捉得曹操,翼德这场功料也不小。”飞领计去了。

对于关羽,孔明曰:“昔日曹操待足下甚厚,足下当有以报之。今日操兵败,必走华容道;若令足下去时,必然放他过去。因此不敢教去。”云长曰:“愿依军法!”云长曰:“若曹操不从那条路上来,如何?”孔明曰:“我亦与你军令状。云长大喜。孔明曰:“云长可于华容小路高山之处,堆积柴草,放起一把火烟,引曹操来。”云长曰:“曹操望见烟,知有埋伏,如何肯来?”孔明笑曰:“岂不闻兵法虚虚实实之论?操虽能用兵,只此可以瞒过他也。他见烟起,将谓虚张声势,必然投这条路来。将军休得容情。”云长领了将令,引关平、周仓并五百校刀手,投华容道埋伏去了。

对于这次的排兵布阵,诸葛亮采用的是串联式,也即在敌人的必经之路上,依次埋伏将领,每个将领处理一批敌人,剩下的交给下一个环节。

在Linux里面,有一种类似的进程间通信机制,就是管道。

所谓的管道,就是在两个进程之间建立一条单向的通道,其实是一段缓存,它会将前一个命令的输出,作为后一个命令的输入。

管道的方式比较简单,缺点是必须事先全部安排好,如果有变动比较难灵活处理,需要依赖神机妙算,比如曹操如果没有按计划走华容道的话,也来不及通知关羽了。

军队之间通信的第二种方式就是靠人,也即靠信使。就是我们常听到的,报!!!大事不好!!!再探!!!

例如在夷陵之战的时候,信使将刘备联营七百里的图本传给诸葛亮看,可是已经晚了。所以这种方式的实时性也不好。

在Linux里面,和管道将信息一股脑儿地从一个进程,倒给另一个进程不同,消息队列有点儿像邮件,发送数据时,会分成一个一个独立的数据单元,也就是消息体,每个消息体都是固定大小的存储块,在字节流上不连续。

在Linux里面还有非常实时的一种模式,就是共享内存+信号量。

进程间通信的共享内存模型的原理是这样的。前面咱们讲内存管理的时候,知道每个进程都有自己独立的虚拟内存空间,不同的进程的虚拟内存空间映射到不同的物理内存中去。这个进程访问A地址和另一个进程访问A地址,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响。

但是,咱们是不是可以变通一下,拿出一块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,传来传去。

共享内存也有问题呀。如果两个进程使用同一个共享内存,大家都往里面写东西,很有可能就冲突了。例如两个进程都同时写一个地址,那先写的那个进程会发现内容被别人覆盖了。

当然,和共享内存配合的,有另一种保护机制,使得同一个共享的资源,同时只能被一个进程访问叫信号量。

信号量其实是一个计数器,主要用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

我们可以将信号量初始化为一个数值,来代表某种资源的总体数量。对于信号量来讲,会定义两种原子操作,一个是P操作,我们称为申请资源操作。这个操作会申请将信号量的数值减去N,表示这些数量被他申请使用了,其他人不能用了。另一个是V操作,我们称为归还资源操作,这个操作会申请将信号量加上M,表示这些数量已经还给信号量了,其他人可以使用了。

例如,你有100元钱,就可以将信号量设置为100。其中A向你借80元,就会调用P操作,申请减去80。如果同时B向你借50元,但是B的P操作比A晚,那就没有办法,只好等待A归还钱的时候,B的P操作才能成功。之后,A调用V操作,申请加上30元,也就是还给你30元,这个时候信号量有50元了,这时候B的P操作才能成功,才能借走这50元。

所谓原子操作(Atomic Operation),就是任何一块钱,都只能通过P操作借给一个人,不能同时借给两个人。也就是说,当A的P操作(借80)和B的P操作(借50),几乎同时到达的时候,不能因为大家都看到账户里有100就都成功,必须分个先来后到。

共享内存+信号量这种模式类似八门金锁阵,好处是配合紧密,不是和一个人战斗,而是和所有人。但是信号量的控制非常重要,要有个人站在最高处控制,在极端场景下,信号量设计不好容易死锁。比如赵云破阵,如从东南角上生门击人,往正西景门而出,其阵必乱。

上面的这些机制,基本常规状态下的工作模式,如果发生了异常怎么办?例如出现线上系统故障,这个时候,什么流程都来不及了,不可能发邮件,也来不及开会,所有的架构师、开发、运维都要被通知紧急出动。所以,7乘24小时不间断执行的系统都需要有告警系统,一旦出事情,就要通知到人,哪怕是半夜,也要电话叫起来,处理故障。是不是应该还有一种异常情况下的工作模式。

当然应该有,我们可以建立像操作系统里面的信号机制。信号没有特别复杂的数据结构,就是用一个代号一样的数字。Linux提供了几十种信号,分别代表不同的意义。信号之间依靠它们的值来区分。这就像咱们看警匪片,对于紧急的行动,都是说,‘1号作战任务’开始执行,警察就开始行动了。情况紧急,不能啰里啰嗦了。

信号可以在任何时候发送给某一进程,进程需要为这个信号配置信号处理函数。当某个信号发生的时候,就默认执行这个函数就可以了。这就相当于咱们运维一个系统应急手册,当遇到什么情况,做什么事情,都事先准备好,出了事情照着做就可以了。

这种信号模式,诸葛亮经常使用,例如火烧博望坡的时候,诸葛亮是这样布置的。孔明令曰:“博望之左有山,名曰豫山;右有林,名曰安林:可以埋伏军马。云长可引一千军往豫山埋伏,等彼军至,放过休敌;其辎重粮草,必在后面,但看南面火起,可纵兵出击,就焚其粮草。翼德可引一千军去安林背后山谷中埋伏,只看南面火起,便可出,向博望城旧屯粮草处纵火烧之。关平、刘封可引五百军,预备引火之物,于博望坡后两边等候,至初更兵到,便可放火矣。”又命:“于樊城取回赵云,令为前部,不要赢,只要输,主公自引一军为后援。各须依计而行,勿使有失。”

第九回:孙刘联盟赤壁破曹,网络通信多机合作

诸葛亮辅佐刘备打了几个胜仗,但是曹操实力太强,还是敌不过,最终要按照隆中对中计划的一样,和孙权联合攻抗曹操。

但是两个集团的合作能否成功呢?这依赖于双方的合作协议是如何谈的。

在Linux里面,服务器之间的协作要靠网络协议。

这其实就像机器与机器之间合作,一台机器将自己想要表达的内容,按照某种约定好的格式发送出去。当另外一台机器收到这些信息后,也能够按照约定好的格式解析出来,从而准确、可靠地获得发送方想要表达的内容。这种约定好的格式就是网络协议。

现在业内知名的有两种网络协议模型,一种是OSI的标准七层模型,一种是业界标准的TCP/IP模型。它们的对应关系如下图所示:

下面我们来解析一下网络协议。

我们先从第三层网络层开始,因为这一层有我们熟悉的IP地址,所以这一层我们也叫IP层。

连接到网络上的每一个设备都至少有一个IP地址,用于定位这个设备。无论是近在咫尺的、你旁边同学的电脑,还是远在天边的电商网站,都可以通过IP地址进行定位。因此,IP地址类似互联网上的邮寄地址,是有全局定位功能的。

就算你要访问美国的一个地址,也可以从你身边的网络出发,通过不断地打听道儿,经过多个网络,最终到达目的地址,和快递员送包裹的过程差不多。打听道儿的协议也在第三层,我们称为路由协议。将网络包从一个网络转发给另一个网络的设备,我们称为路由器。

总而言之,第三层干的事情,就是网络包从一个起始的IP地址,沿着路由协议指的道儿,经过多个网络,通过多次路由器转发,到达目标IP地址。

IP层是网络协议里面的核心层,承上启下。

对于刘备集团和孙权集团来讲,首先双方建立联系的,就是诸葛亮和鲁肃。这两个人都是双方中流砥柱的人才,也是制定战略的人才。就像两台机器要通信事先双方知道对方的IP地址一样,诸葛亮和鲁肃互相闻名已久。

鲁子敬愿亲到江夏,搬请诸葛亮共议破曹大计。孙权因刘备两次打败曹操,急于摸清曹军兵力,同意鲁肃去江夏探听虚实。诸葛亮于是决定只身入吴,要凭三寸不烂之舌,去说孙、曹两家互相吞并。

从第三层,我们往下看。第二层是数据链路层。有时候我们简称为二层或者MAC层。所谓MAC,就是每个网卡都有的唯一的硬件地址(不绝对唯一,相对大概率唯一即可,类比UUID)。这虽然也是一个地址,但是这个地址是没有全局定位功能的。

就像给你送外卖的小哥,不可能根据手机尾号找到你家,但是手机尾号有本地定位功能的,只不过这个定位主要靠“吼“。外卖小哥到了你的楼层就开始大喊:“尾号xxxx的,你外卖到了!“

MAC地址的定位功能局限在一个网络里面,也即同一个网络号下的IP地址之间,可以通过MAC进行定位和通信。从IP地址获取MAC地址要通过ARP协议,是通过在本地发送广播包,也就是“吼“,获得的MAC地址。

由于同一个网络内的机器数量有限,通过MAC地址的好处就是简单。匹配上MAC地址就接收,匹配不上就不接收,没有什么所谓路由协议这样复杂的协议。当然坏处就是,MAC地址的作用范围不能出本地网络,所以一旦跨网络通信,虽然IP地址保持不变,但是MAC地址每经过一个路由器就要换一次。

所以第二层干的事情,就是网络包在本地网络中的服务器之间定位及通信的机制。

我们再往下看第一层,物理层。这一层就是物理设备。例如,连着电脑的网线,我们能连上的WiFi。

鲁肃和诸葛亮各管各的属下,这不消说。到了东吴的地盘,诸葛亮也是一切听鲁肃安排,因为江东的地理,人物,政治,权力格局,也是只有鲁肃最清楚的。

到了东吴,诸葛亮也是遍访东吴人物,摸清各自主战主和,最终敲定关键要劝说的人物——周瑜。这就相当于ARP通过广播获取目标MAC了。

从第三层往上看,第四层是传输层,这里面有两个著名的协议,TCP和UDP。尤其是TCP,更是广泛使用,在IP层的代码逻辑中,仅仅负责数据从一个IP地址发送给另一个IP地址,丢包、乱序、重传、拥塞,这些IP层都不管。处理这些问题的代码逻辑写在了传输层的TCP协议里面。

我们常说,TCP是可靠传输协议,也是难为它了。因为从第一层到第三层都不可靠,网络包说丢就丢,是TCP这一层通过各种编号、重传等机制,让本来不可靠的网络对于更上层来讲,变得“看起来”可靠。哪有什么应用层的岁月静好,只不过是TCP层在负重前行。

对于东吴来讲,两个比鲁肃更加重要的两个人物,就是张昭和周瑜,这两个一个主战,主张和刘备达成协作,是周瑜,是TCP,也即定向建立连接,一个主和,主张投降曹操,是张昭,是UDP,也即和谁通信都行。

接下来,诸葛亮要和周瑜建立TCP连接了,也即三次握手,智激周瑜。

传输层再往上就是应用层,例如,咱们在浏览器里面输入的HTTP,Java服务端写的Servlet,都是这一层的。

孙权就是应用层了。

二层到四层都是在Linux内核里面处理的,应用层例如浏览器、Nginx、Tomcat都是用户态的。内核里面对于网络包的处理是不区分应用的。

从四层再往上,就需要区分网络包发给哪个应用。在传输层的TCP和UDP协议里面,都有端口的概念,不同的应用监听不同的端口。例如,服务端Nginx监听80、Tomcat监听8080;再如客户端浏览器监听一个随机端口,FTP客户端监听另外一个随机端口。

应用层和内核互通的机制,就是通过Socket系统调用。所以经常有人会问,Socket属于哪一层,其实它哪一层都不属于,它属于操作系统的概念,而非网络协议分层的概念。

操作系统对于网络协议的实现模式是这样的:二到四层的处理代码在内核里面,七层的处理代码让应用自己去做。两者需要跨内核态和用户态通信,就需要一个系统调用完成这个衔接,这就是Socket。

如果公司想要和其他公司沟通,我们将请求封装为HTTP协议,通过Socket发送到内核。内核的网络协议栈里面,在TCP层创建用于维护连接、序列号、重传、拥塞控制的数据结构,将HTTP包加上TCP头,发送给IP层,IP层加上IP头,发送给MAC层,MAC层加上MAC头,从硬件网卡发出去。

最终网络包会被转发到目标服务器,它发现MAC地址匹配,就将MAC头取下来,交给上一层。IP层发现IP地址匹配,将IP头取下来,交给上一层。TCP层会根据TCP头中的序列号等信息,发现它是一个正确的网络包,就会将网络包缓存起来,等待应用层的读取。

应用层通过Socket监听某个端口,因而读取的时候,内核会根据TCP头中的端口号,将网络包发给相应的应用。

第十回:赤壁大胜又得荆益,设备众多统一管理

联合了孙权之后,果真赤壁大胜曹操,诸葛亮趁机占领了荆州,并以荆州为根基,进一步占领了益州,隆中对中的计划算是完成了一半。

还记得隆中对怎么说的嘛?

若跨有荆、益,保其岩阻,西和诸戎,南抚夷越,外结好孙权,内修政理;天下有变,则命一上将将荆州之军以向宛、洛,将军身率益州之众出于秦川,百姓孰敢不箪食壶浆,以迎将军者乎?诚如是,则霸业可成,汉室可兴矣。

现在“跨有荆、益,保其岩阻”问题解决了,接下来就是“西和诸戎,南抚夷越,外结好孙权,内修政理”,由于刘备算是从夹缝里面打出一片自己的天下,所以导致面临的环境比较复杂,周围各个方面的关系都要照顾好。

这有点像一家公司要做To B的生意,发现客户多种多样,众口难调,不同的地域不一样,不同的行业不一样。如果你不懂某个地方的规矩,根本卖不出去东西;如果你不懂某个具体行业的使用场景,也无法满足客户的需求。怎么办呢?一般公司采取的策略就是建立生态,设置很多代理商,让各个地区和各个行业的代理商帮你屏蔽这些差异化。你和代理商之间只要进行简单的标准产品交付就可以了。

诸葛亮自有一套自己的办法,从他七擒孟获就可以看出来。

作为Linux操作系统,需要管理的设备也是复杂多样的。我们能举出来的,例如键盘、鼠标、显示器、网卡、硬盘、打印机、CD/DVD等等,多种多样。这样,当然方便用户使用了,但是对于操作系统来讲,却是一件复杂的事情,因为这么多设备,形状、用法、功能都不一样,怎么才能统一管理起来呢?我们一层一层来看。

第一层,用设备控制器屏蔽设备差异。

CPU并不直接和设备打交道,他们中间有一个叫作设备控制器(Device Control Unit)的组件。例如,硬盘有磁盘控制器、USB有USB控制器、显示器有视频控制器等。这些控制器就像代理商一样,他们知道如何应对硬盘、鼠标、键盘、显示器的行为。

你的代理商往往是小公司。控制器其实有点儿像一台小电脑。他有他的芯片,类似小CPU,执行自己的逻辑。他也有他的寄存器。这样CPU就可以通过写这些寄存器,对控制器下发指令,通过读这些寄存器,查看控制器对于设备的操作状态。

CPU对于寄存器的读写,可比直接控制硬件,要标准和轻松很多。这就相当于你和代理商的标准产品交付。

第二层,用驱动程序屏蔽设备控制器差异。

虽然代理商机制能够帮我们屏蔽很多设备的细节,但是从上面的描述我们可以看出,由于每种设备的控制器的寄存器、缓冲区等使用模式,指令都不同。对于咱们公司来讲,就需要有个部门专门对接代理商,向其他部门屏蔽代理商的差异,成立公司的渠道管理部门。

那对于操作系统来讲,渠道管理部门就是用来对接各个设备控制器的设备驱动程序。

这里需要注意的是,设备控制器不属于操作系统的一部分,但是设备驱动程序属于操作系统的一部分。操作系统的内核代码可以像调用本地代码一样调用驱动程序的代码,而驱动程序的代码需要发出特殊的面向设备控制器的指令,才能操作设备控制器。

设备驱动程序中是一些面向特殊设备控制器的代码。不同的设备不同。但是对于操作系统其他部分的代码而言,设备驱动程序应该有统一的接口。就像下面图中的一样,不同的设备驱动程序,可以以同样的方式接入操作系统,而操作系统的其他部分的代码,也可以无视不同设备的区别,以同样的接口调用设备驱动程序。

第三,用中断控制器统一外部事件处理。

马哥听了恍然大悟:“原来代理商也是五花八门,里面有这么多门道啊!”

鲁肃说:“当咱们对接的代理商多了,代理商可能会有各种各样的问题找到我们,例如代理商有了新客户,客户有了新需求,客户交付完毕等事件,都需要有一种机制通知你们公司,这种通知机制就是中断,那操作系统就需要有一个地方处理这个中断,既然设备驱动程序是用来对接设备控制器的,中断处理也应该在设备驱动里面完成。

然而,中断的触发最终会到达CPU,会中断操作系统当前运行的程序,所以操作系统也要有一个统一的流程来处理中断,使得不同设备的中断使用统一的流程。

一般的流程是,一个设备驱动程序初始化的时候,要先注册一个该设备的中断处理函数。咱们讲进程切换的时候说过,中断返回的那一刻是进程切换的时机。中断的时候,触发的函数是do_IRQ。这个函数是中断处理的统一入口。在这个函数里面,我们可以找到设备驱动程序注册的中断处理函数Handler,然后执行他进行中断处理。

第四,用文件系统接口屏蔽驱动程序的差异。

对接了这么多代理商,如果咱们内部的工程师要和他们打交道,有没有一种统一的方式呢?当然应该了,我们内部员工操作外部设备,可以基于文件系统的接口,制定一个统一的标准。

其实文件系统的机制是一个非常好的机制,咱们公司应该定下这样的规则,一切皆文件。

所有设备都在/dev/文件夹下面,创建一个特殊的设备文件。这个设备特殊文件也有inode,但是他不关联到硬盘或任何其他存储介质上的数据,而是建立了与某个设备驱动程序的连接。

有了文件系统接口之后,我们不但可以通过文件系统的命令行操作设备,也可以通过程序,调用read、write函数,像读写文件一样操作设备。

对于块设备来讲,在驱动程序之上,文件系统之下,还需要一层通用设备层。比如,咱们讲的文件系统,里面的逻辑和磁盘设备没有什么关系,可以说是通用的逻辑。在写文件的最底层,我们看到了BIO字眼的函数,但是好像和设备驱动也没有什么关系。

是的,因为块设备类型非常多,而Linux操作系统里面一切是文件。我们也不想文件系统以下,就直接对接各种各样的块设备驱动程序,这样会使得文件系统的复杂度非常高。所以,我们在中间加了一层通用块层,将与块设备相关的通用逻辑放在这一层,维护与设备无关的块的大小,然后通用块层下面对接各种各样的驱动程序。

第十一回:巧取汉中刘备称王,虚拟化后更加灵活

公元209年,“刘备”夺取“荆南四郡”,再到214年拿下益州,219年将曹操打败夺取汉中,从此蜀国进入最为鼎盛的时期。

建安二十四年(公元219年)秋,刘备在诸葛亮和文臣武将的推尊之下,于沔阳登坛受贺,晋位汉中王。并立公子刘禅为王世子。封诸葛亮为军师,总理军国重事。封关羽、张飞、赵云、马超、黄忠为五虎大将。

看似一帆风顺的刘备集团,也暗藏危机。因为刘备创业可谓南征北战,跑遍整个中国,所以蜀汉集团成分也比较复杂。

第一派:元老派,主要人员:关羽、张飞、赵云、孙乾、简雍、糜氏兄弟。特点:这是刘备最信任的一个派系,早期就跟随刘备东奔西跑、颠簸流离,不论遇到什么困难都不离不弃。他们来自北方各地,是蜀汉政权的元老勋臣。

第二派:荆州派,主要人员:诸葛亮、黄忠、魏延、蒋琬,费祎、马氏兄弟。特点:这是刘备在荆州时招揽的一批人,特别是刘表去世后,许多荆州氏族加入刘备阵营,这些人组成蜀汉政权的荆州派系。这个派系以诸葛亮为首,文武均衡,掌握着蜀汉政权的核心力量,甚至可以说,荆州派系支撑着蜀汉运行。

第三派:东州派,主要人员:法正、李严、许靖、董和、孟达、董允。特点:这是刘焉入蜀时带去益州的一批人,同时刘焉还把南阳、三辅一带数万户入蜀的流民收编为“东州兵”。因为有较强的军事力量,东州派在蜀汉的地位全面压制益州派,他们成为蜀汉政权的中层权力掌握者。

第四派:益州派,主要人员:黄权、李恢、马忠、谯周、王平。特点:这些人是益州本土豪族,是益州真正的主人。然而,因为蛋糕有限,益州派成为打压的对象,特别是失去荆州后,矛盾更加严重。虽然这个派系处于最底层,但是强龙不压地头蛇,没有人敢轻视他们的力量。

应该如何管理这么复杂的一个体系呢?当然是要发挥各自的作用,每个派系找个代表人物了。

这在司马懿五路伐蜀的时候,诸葛亮的应对可以看出来。

老臣先知西番国王轲比能引兵犯西平关。臣料马超积祖西川人氏,素得羌人之心,羌人以超为神威天将军。臣已先遣一人星夜驰檄,令马超紧守西平关,伏四路奇兵,每日交换,以兵拒之。此一路不必忧矣。

又南蛮孟获兵犯四郡,臣亦飞檄遣魏延领一军左出右入,右出左入,为疑兵之计。蛮兵惟凭勇力,其心多疑,若见疑兵,必不敢进。此一路又不足忧矣。

又知孟达引兵出汉中。达与李严曾结生死之交,臣回成都时,留李严守永安宫。臣已作一书,只做李严亲笔,令人送与孟达。达必然推病不出,以慢军心。此一路又不足忧矣。

又知曹真引兵犯阳平关。此地险峻,可以保守。臣已调赵云引一军守把关隘,并不出战。曹真若见我军不出,不久自退矣。

只有东吴这一路兵,未必便动:如见四路兵胜,川中危急,必来相攻;若四路不济,安肯动乎?臣料孙权想曹丕三路侵吴之怨,必不肯从其言。虽然如此,须用一舌辩之士,径往东吴,以利害说之,则先退东吴,其四路之兵何足忧乎?邓芝陈说吴、蜀、魏三国之势,论证吴蜀联合之利、吴魏联合之弊。孙权从其言,决心与蜀结盟抗魏并遣使张温入蜀答礼。

蜀国其实就是得了大公司病——不灵活。

这里面的不灵活,就像Linux服务器,越来越强大的时候,无论是计算、网络、存储,都越来越牛。例如,内存动不动就是百G内存,网络设备一个端口的带宽就能有几十G甚至上百G。存储在数据中心至少是PB级别的,自然也有不灵活的毛病。

资源大小不灵活:有时候我们不需要这么大规格的机器,可能只想尝试一下某些新业务,申请个4核8G的服务器试一下,但是不可能采购这么小规格的机器。无论每个项目需要多大规格的机器,公司统一采购就限制几种,全部是上面那种大规格的。

资源申请不灵活:规格定死就定死吧,可是每次申请机器都要重新采购,周期很长。

资源复用不灵活:反正我需要的资源不多,和别人共享一台机器吧,这样不同的进程可能会产生冲突,例如socket的端口冲突。另外就是别人用过的机器,不知道上面做过哪些操作,有很多的历史包袱,如果重新安装则代价太大。

按说,大事情流程严禁没问题,很多小事情也要被拖累走整个流程,而且很容易出现资源冲突,每天跨部门的协调很累人,历史包袱严重,创新没有办法轻装上阵。

很多公司处理这种问题采取的策略是成立独立的子公司,独立决策,独立运营。这种办法往往会用在创新型的项目上。

Linux也采取了这样的手段,就是在物理机上面创建虚拟机。每个虚拟机有自己单独的操作系统、灵活的规格,一个命令就能启动起来。每次创建都是新的操作系统,很好地解决了上面不灵活的问题。

在物理机上的操作系统看来,虚拟机是一个普通的应用,他和Excel一样,只能运行在用户态。但是对于虚拟机里面的操作系统内核来讲,运行在内核态,应该有高的权限。

要做到这件事情,第一种方式,完全虚拟化。其实说白了,这是一种“骗人”的方式。虚拟化软件会模拟假的CPU、内存、网络、硬盘给到虚拟机,让虚拟机里面的内核自我感觉良好,感觉他终于又像个内核了。在Linux上,一个叫作qemu的工具可以做到这一点。

qemu向虚拟机里面的客户机操作系统模拟CPU和其他的硬件,骗客户机,GuestOS认为自己和硬件直接打交道,其实是同qemu模拟出来的硬件打交道,qemu会将这些指令转译给真正的硬件。由于所有的指令都要从qemu里面过一手,因而性能就会比较差。

第二种方式,硬件辅助虚拟化。可以使用硬件CPU的Intel-VT和AMD-V技术,需要CPU硬件开启这个标志位(一般在BIOS里面设置)。当确认开始了标志位之后,通过内核模块KVM,GuestOS的CPU指令将不用经过Qemu转译,直接运行,大大提高了速度。qemu和KVM融合以后,就是qemu-kvm。

第三种方式称为半虚拟化。对于网络或者硬盘的访问,我们让虚拟机内核加载特殊的驱动,重新定位自己的身份。虚拟机操作系统的内核知道自己不是物理机内核,没那么高的权限。他很可能要和很多虚拟机共享物理资源,所以学会了排队。虚拟机写硬盘其实写的是一个物理机上的文件,那我的写文件的缓存方式是不是可以变一下。我发送网络包,根本就不是发给真正的网络设备,而是给虚拟的设备,我可不可以直接在内存里面拷贝给它,等等等等。

网络半虚拟化方式是virtio_net,存储是virtio_blk。客户机需要安装这些半虚拟化驱动。客户机内核知道自己是虚拟机,所以会直接把数据发送给半虚拟化设备,然后经过特殊处理(例如排队、缓存、批量处理等性能优化方式),最终发送给真正的硬件。这在一定程度上提高了性能。

第十二回:夷陵大败刘备托孤,数据中心操作系统

然而刘备的全盛时期持续太短了,并没有像诸葛亮规划的一样,等到了统一全国的时机。

随着关羽走麦城失荆州,刘备兴兵伐吴兵败夷陵,创业未半而中道崩出。后来,诸葛亮规划的隆中对中局势已经不存在,为了不负先帝托付,诸葛亮六出祁山,姜维九伐中原,终不能成功。

诸葛亮规划的一统江山,也只有五丈原临终的梦中实现了。

不过,我们的操作系统故事还需要写下去。

对于单台Linux服务器,最重要的四种硬件资源是CPU、内存、存储和网络。面对整个数据中心成千上万台机器,我们只要重点关注这四种硬件资源就可以了。如果运维数据中心依然像的运维一台台物理机的前辈一样,天天关心哪个程序放在了哪台机器上,使用多少内存、多少硬盘,每台机器总共有多少内存、多少硬盘,还剩多少内存和硬盘,那头就大了。

对于数据中心,我们需要一个调度器,将运维人员从指定物理机或者虚拟机的痛苦中解放出来,实现对于物理资源的统一管理,这就是Kubernetes,也就是数据中心的操作系统。

对于CPU和内存这两种计算资源的管理,我们可以通过Docker技术完成。

容器实现封闭的环境主要要靠两种技术,一种是看起来是隔离的技术,称为namespace。在每个namespace中的应用看到的,都是不同的 IP地址、用户空间、进程ID等。另一种是用起来是隔离的技术,称为cgroup,即明明整台机器有很多的 CPU、内存,但是一个应用只能用其中的一部分。

另外,容器里还有镜像。也就是说,在你焊好集装箱的那一刻,将集装箱的状态保存下来的样子。就像孙悟空说“定!”,集装箱里的状态就被“定”在了那一刻。然后,这一刻的状态会被保存成一系列文件。无论在哪里运行这个镜像,都能完整地还原当时的情况。

通过容器,我们可以将CPU和内存资源,从大的资源池里面隔离出来,并通过镜像技术,在数据中心里面实现计算资源的自由漂移。

没有操作系统的时候,汇编程序员需要指定程序运行的CPU和内存物理地址。同理,数据中心的管理员,原来也需要指定程序运行的服务器以及使用的CPU和内存。现在,Kubernetes里面有一个调度器Scheduler,你只需要告诉它,你想运行10个4核8G的Java程序,它会自动帮你选择空闲的、有足够资源的服务器,去运行这些程序。

对于存储,无论是分布式文件系统和分布式块存储,需要对接到Kubernetes,让Kubernetes管理它们。如何对接呢?Kubernetes会提供CSI接口。这是一个标准接口,不同的存储可以实现这个接口来对接Kubernetes。是不是特别像设备驱动程序呀?操作系统只要定义统一的接口,不同的存储设备的驱动实现这些接口,就能被操作系统使用了。

对于网络,也是类似的机制,Kubernetes同样是提供统一的接口CNI。无论你用哪种方式实现网络模型,只要对接这个统一的接口,Kubernetes就可以管理容器的网络。

至此,整个操作系统的机制就讲完了。

我还没有学会写个人说明!

腾讯健康码16亿亮码背后的 Elasticsearch系统调优实践

上一篇

人工智能和创造它的人类一样有偏见吗?

下一篇

你也可能喜欢

只有程序员才能读懂的三国演义(二)

长按储存图像,分享给朋友

ITPUB 每周精要将以邮件的形式发放至您的邮箱


微信扫一扫

微信扫一扫