相信组装过电脑的朋友都知道,我们的电脑最主要的几个零件是:CPU、内存、硬盘。但我们实际使用的时候,我们并不会主动跟硬件打交道,而是和显示器上显示的操作系统打交道。
那么问题来了,操作系统到底是怎么操作CPU、内存、硬盘,让其实现我们的功能的呢?操作系统与硬件之间的层级结构是怎样的?
这里的操作系统,我们默认说的是 Linux 操作系统。
了解过 Linux 的朋友会知道,其实 Linux 系统的整个系统结构如下面所示:
它由内核、系统调用、Shell、库函数、应用几个部分构成。看着是不是有些晕头转向叻,没关系。下面我将用极其简单的口水文跟你介绍这几个层级,让你看完之后印象深刻。
硬件的好基友:内核
紧挨着硬件的是内核,只有内核才能操作硬件,一般我们也叫它内核空间。
大学学过模拟电路和数字电路的同学都知道,计算机里任何的运算到最后都是 1 0 数字,最终通过数字电路的来进行运算。例如我们要计算一个加法(4+5),对于我们来说就是简单地一个运算,但对于计算机来说,它并不知道 4 是什么,5 是什么,它只知道 1 和 0。并且我们一步就可以算出来的加法,计算机可能要经过无数次运算。一个简单的加法尚且如此,更不用说更加复杂的算法运算了。
举上面这个例子主要是想表达硬件操作的复杂性,以及机器码对于人类的不友好。所以为了对外屏蔽这些硬件细节,就有了内核空间这一层东西。
内核空间是一个虚拟的空间,其直接与硬件打交道。除了内核空间,其他任何模块都无法与硬件直接接触,都需要通过内核空间来操作硬件。所以说内核空间是硬件的好基友,任何人要见它,都得经过我。
这么一个设置也有一个好处,那就是保证了硬件的稳定。试想一下,如果谁都能操作硬件。你弄一下,我弄一下,那么硬件估计就被弄残了。
系统的基石:系统调用
挨着内核兄弟的就是系统调用了,系统调用是操作系统的最小单位,任何操作都是由一个个系统调用组成的。这就像我们的汉字,无论这个字多复杂,它都是由点、横、撇等组成。而系统调用之于操作系统,就像是笔画之于汉字。
集大成者:库函数
如果说系统调用是笔画,那么库函数就是汉字的偏旁了。我们记汉字不可能记住它的所有笔画,但我们能记住它由哪一些偏旁组成。因此,在 Linux 操作系统中也类似,设计者一些常用的操作组合起来,编程库函数。
例如一个简单的变量内存分配操作,就需要动用多个系统调用。如果没有库函数,我们就得每次都去写多次系统调用,但有了库函数我们直接用 malloc() 库函数就可以实现这个功能。
所以说,库函数是集大成者,是系统调用的模块化体现。
效率利器:Shell
我们除了使用库函数去实现常用的操作之外,还可以使用 Shell 去实现。Shell 其实与库函数的功能类似,他们都将一些常用的系统调用组装起来,方便后续调用,可以说是模块化的提现。
但是 Shell 与库函数的定位还是略有不同的。库函数更多时候是作为开发 API 来使用,应用通过调用 API 来实现各种功能。而 Shell 则更多是作为运维的工具,能通过 Shell 脚本实现更多复杂的功能。
Shell 不仅仅是操作系统中的一个层次,它还指某种特定的语言规范,通过这种语言规范,我们可以组装成 Shell 脚本,从而实现复杂的功能。牛逼的运维都会使用 Shell 脚本来自动化处理业务,从而极大地提高工作效率。
友好使者:应用
在操作系统最外层就是应用了。在这一层我们可以调用 Shell、库函数、系统调用这几个层次的东西,从而方便我们的开发。我们常用的各种办公软件、图像处理软件都是这一层次的东西。
如果要选一个最人类最友好的使者,那么非应用这个小兄弟不可了。经历了重重难关,从硬件到内核,再从内核到库函数,最后到应用这一层才能看得比较舒服,我们也才能够更高效地使用。
总结
许多工作了十几年的工程师很多时候都搞不清楚操作系统的层级关系,更甚者连内核是什么都不知道。但树义认为,了解操作系统的层级结构是很重要的知识点,可以为我们深入理解应用层面的知识打下基础。
例如当我们学到 Netty 的时候,我们会学到 Unix 网络 IO 模型,这时候就会涉及到数据是如何从文件或者网络另一端读取到本机的内存中的。此时,就会涉及到内核空间以及用户空间的知识。这时如果你不理解内核是什么,那么自然也就无法理解 Unix 网络 IO 模型这个知识点了。
好了,今天的文章就到这里。如果你喜欢的话,麻烦转发让更多的朋友看到。一个人学习可以走得很快,但一群人可以走得更远。