早期的计算机只有在军事或者高科技用途、学术院校的学术研究才能碰到。并且,那时候的计算机架构很难用,指令周期慢、操作接口很麻烦。那时候输入设备只有卡片阅读机,输出设备只有打印机,用户也无法与操作系统进行互动(多道批处理操作系统)。而卡片阅读机,也导致编写程序十分麻烦,因为需要将程序的相关信息在读卡纸上打洞
Unix 的历史
起源和 Unix 主线发展
回顾Unix历史,我们就要说一下一个叫 MULTICS(Multiplexed Information and Computing Service)的项目。上世纪六十年代时,大部份计算机都是采用批处理(Batch Processing)的方式(也就是说,当作业积累一定数量的时候,计算机才会进行处理)。那时,我们熟知的美国电话及电报公司(American Telephone and Telegraph Inc.;AT&T)、通用电器公司(General Electrics;G.E.)及麻省理工学院(Massachusetts Institute of Technology;MIT)计划合作开发一个多用途(General-Purpose)、分时(Time-Sharing)及多用户(Multi-User)的操作系统,也就是这个MULTICS,其被设计运行在GE-645大型主机上。不过,这个项目由于太过复杂,整个目标过于庞大,糅合了太多的特性,进展太慢,几年下来都没有任何成果,而且性能都很低。于是到了1969年2月,贝尔实验室(Bell Labs)决定退出这个项目(贝尔实验室就是 AT&T 下的一个实验室)。
Multics 项目被终止后,贝尔实验室的人们发现自己处于一个没有交互式操作系统可用的境地(真实情况是 Ken Thompson 想玩一款游戏,但是 MULTICS 系统上运行很慢耗费也贵,所以 Ken Thompson 想让 Dennis Ritchie 为这块游戏开发一个简单的操作系统)。在这种情况下,1969 年的夏天,贝尔实验室设计了一个文件系统原型,而这个原型最终发展成现在的 Unix。当时 Thompson 是在一台无人问津的 PDP-7 型机上实现了这个全新的操作系统。(1969年8月,Linux之父Linus Torvalds在芬兰出生了。)
1971 年,Thompson写了充分长篇的申请报告,申请到了一台PDP-11/24的机器。于是 Unix 被移植到这台机器上。此时算是 Unix 第一版。
1973 年的时候,Ken Thompson 与 Dennis Ritchie 感到用汇编语言做移植太过于头痛,他们想用高级语言来完成第三版,对于当时完全以汇编语言来开发程序的年代,他们的想法算是相当的疯狂。一开始他们想尝试用Fortran,可是失败了。后来他们用一个叫BCPL(Basic Combined Programming Language)的语言开发,他们整合了 BCPL 形成 B 语言,后来 Dennis Ritchie 觉得 B 语言还是不能满足要求,就是就改良了 B 语言,这就是今天的大名鼎鼎的 C 语言。于是,Ken Thompson 与 Dennis Ritchie 成功地用 C 语言重写了 Unix 的第三版内核。结果这给 Unix 系统的广泛移植铺平了道路。
1974年,汤普逊和里奇合作在ACM通信上发表了一篇关于UNIX的文章,这是UNIX第一次出现在贝尔实验室以外。
1975年,UNIX发布了 4、5、6 三个版本,并提供源代码。6 这个版本是在贝尔实验室以外被广泛使用的第一个 Unix 版本。
许多其他的公司也把 Unix 移植到新的机型上。伴随着这些一直,开发者们按照自己的方式不断增加系统的功能,并由其产生了若干变体。
1979 年发布了 Unix 第七版,这个版本被称为是“最后一个真正的Unix”,因为 20 世纪 80 年代相继发布的 8、9、10 版本只授权给了少数大学。
1982年,AT&T 基于版本7开发了 UNIX System Ⅲ 的第一个版本,这是一个商业版本仅供出售。
为了解决混乱的 UNIX 版本情况,AT&T综合了其他大学和公司开发的各种 UNIX,1983 年 AT&T 推出了 UNIX System V Release 1。这个新的UNIX商业发布版本不再包含源代码。
变体 --- BSD
由于 Unix 设计简洁并且发布时提供源代码,所以许多组织和团体都可以对它进行进一步的开发,按照自己的方式不断增加系统的功能,并由其产生了若干变体。加州大学伯克利分校便是其中影响最大的一个。他们推出的变体叫做 BSD(Berkeley Software Distributions)。
1977 年伯克利推出了第一个 Unix 演化版,称为 1BSD 系统,它的实现基于贝尔实验室的 Unix 版本,不但在其上加入了许多修正补丁,而且还集成了不少额外的软件。
1978 年伯克利继续推出了 2BSD 系统,其中包含了我们如今仍在使用的 csh、vi 等应用软件。
1979 年伯克利推出了 3BSD 系统,这是伯克利真正独立开发的 Unix 系统。该系统引入了一系列令人振奋的新特性,支持虚拟内存便是其中一大亮点。
在 3BSD 以后,伯克利又相继推出了 4BSD 系列,包括 4.0BSD、4.1BSD、4.2BSD、4.3BSD 等众多分支。这些 Unix 演化版实现了任务管理、换页机制、TCP/IP 等新的特性。这些发布版中的 TCP/IP 代码几乎是现在所有系统中TCP/IP 实现的前辈,包括 AT&T System V 和 Microsoft Windows 中的 TCP/IP 都参照了 BSD 的源码。
1994 年伯克利重写虚拟内存子系统,并推出了伯克利 Unix 系统的最终官方版,即 4.4BSD。
由于 4.4BSD 的开放性许可,BSD 继续以 FreeBSD、NetBSD 和 OpenBSD 等继续。
变体 --- 其他
20 世纪 80 年代和 90 年代,许多服务器厂商推出了他们自己的 Unix,这些 Unix 大部分是在 AT&T 或伯克利发行版的基础上加上一些满足他们特定体系结构需要的特性。这其中包括 Sun 的 Solaris 和 SunOS(是基于 BSD 的)。
Unix 总结
Unix 强大的根本原因有这么几点:
- Unix 很简洁。它不像其他动辄提供数千个系统调用并且设计目的不明确的操作系统那样,Unix 仅仅提供了几百个系统调用并且有一个明确的设计目的。
- Unix 中,几乎所有的东西都被当做文件对待。这种抽象使对数据和对设备的操作都是通过一套相同的系统调用接口来进行的:open()、read()、write()、lseek() 和 close()。
- Unix 内核和其他相关的系统工具都是用 C 语言编写而成,所以可以让 Unix 在各种硬件体系结构面前都具有令人惊异的一致能力,并且广大的开发人员也很容易就能接受。
- Unix 的进程创建非常迅速,并且有一个非常独特的 fork() 系统调用。
- Unix 提供了一套非常简单当又很稳定的进程间通信原语。
快速简洁的进程创建过程使得 Unix 的程序可以被设计得很简单,这个程序只要保质保量地执行完一个任务上即可而不用多个任务。而进程间通信机制又可以保证这些单一目的的简单程序可以方便地组合在一起,来解决生活中越来越复杂的任务。正式这种策略和机制分离的设计理念,确保了 Unix 系统具备清晰的层次化结构。
今天的 Unix 已经发展成一个支持抢占式、多线程、虚拟内存、换页、动态链接和 TCP/IP 网络的现代化操作系统。 Unix 的不同变体被应用到大到数百个 CPU 的集群,小到嵌入式设备的各种系统上。Unix 的成功归功于其简洁和一流的设计,早期设计人员的最初决策以及不妥协于成见而有活力的设计抉择。
Linux 的历史
一句话概括的话是:1991 年,Linus 为当时新推出的、使用 Intel 80386 微处理器的计算机开发了一款全新的操作系统,Linux 由此诞生。
稍微具体点的是这么个过程:Linus 那时正作为大学的一名学生,正因为不能随心所欲地使用 Unix 而苦恼。当时流行的 Microsoft 的 DOS 系统对他而言,除了玩个游戏之外别无它用。相比之下,更热衷于 Minix,一种用于教学的 Unix。当时,由于 Minix 的许可证限制,它不能轻易修改和发布该系统的原代码,也不能对 Minix 的开发者所作的设计轻举妄动。
为了走出这种困境,决定开发自己的操作系统。一开始 Linus 写了个简单的终端仿真程序,用于连接到本校的大型 Unix 服务器。那时他专门写了一个用于他当时正在用的硬件、与操作系统无关的程序,因为它想使用 80386 处理器的功能。开发是在 Minix,用 GCC 来完成的。之后,经过一学年的改进和完善,Linus 手上就有了虽然不成熟当时五脏俱全的 Unix。1991 年年底,他在 Internet 上发布了早起版本。从此,Linux 便起飞了。
Linux 吸收了很多开发者、黑客对其代码进行修改和完善,所以虽然 Linus 被认为是 Linux 之父,但是开发工作其实是由网上各路大牛协力完成的,是一个互联网上的协作开发项目。另外 Linux 是一个非商业化的产品,是一款自由公开的软件,任何人都可以开发内核。但是, Linux 使用 GNU 的 GPL(General Public License)第 2 版作为限制条款,意味着你可以获取内核并随意修改它。然而,如果你希望发布你修改过的内核,你也得保证得到你的内核的人同时享有你曾经享有过的所有权利,包括全部的源代码。
另外,一般情况下,我们称 Linux 都是将其指代为 Linux 内核。
Linux 内核与传统 Unix 内核的比较
首先需要明确的是 Linux 是类 Unix 系统,但不是 Unix。尽管 Linux 借鉴了 Unix 的许多设计并且实现了 Unix 的 API(由 Posix 标准和其他 Single Unix Specification 定义的),但是 Linux 没有像其他 Unix 变种那样直接使用 Unix 的源代码。必要的时候,Linux 的实现可能和其他各种 Unix 的实现大相径庭,但是考虑到不能忽视 Unix 的底蕴,尤其是 Unix 的API,所以虽然实现的方式不同但是保留了 Unix 的设计目标并且保证了应用程序编程接口的一致。
由于 Linux 并没有基于某种特定的 Unix,Linux 的开发者们可以选择已知最理想的解决方案或者创造出新的解决方案。Linux 内核与传统的 Unix 系统之间存在一些显著的差异:
- 虽然 Linux 和 Unix 都是单内核(宏内核),但是 Linux 支持动态加载内核模块,可以在需要的时候动态地卸除和加载部分内核代码。
- Linux 支持对称多处理(SMP)机制。虽然许多 Unix 的变体也支持 SMP,当传统的 Unix 并不支持这种机制。
- Linux 内核可以抢占,允许在内核运行的任务优先执行的能力。然而传统的 Unix 不支持,其他各种 Unix 产品中,也只有少数(Solaris 和 IRIX)支持抢占,大多数 Unix 内核都是不支持抢占的。
- Linux 对线程支持的实现比较有意思:内核并不区分线程和其他的一半进程,即线程和进程的实现是一样的,只是其中的一些共享资源而已,比如线程共享地址空间。
- Linux 提供设备类的面向对象的设备模型、热拔插事件,以及用户空间的设备文件系统(sysfs)。
- Linux 忽略了一些被认为设计得很拙劣的 Unix 特性以及忽略了难以实现的过时标准。
- Linux 更多体现了自由这个词的精髓。Linux 的诸多特定是 Linux 公开开发、自由发展的结果。Linux 中的任何改变都必须要求能通过简洁的设计以及正确可靠的实现来解决现实生活中确实存在的问题。因此,Unix 变种中有些没有普遍意义的特性都会被 Linux 所抛弃(取其精华,去其槽粕)。
- Unix 内核通常需要硬件提供页机制(MMU)来管理内存,这种页机制加强了对内存空间的保护,并保证每个进程都可以运行在不同的虚拟地址上。初期的 Linux 虽然也需要 MMU 支持,但是一些特殊版本并不需要 MMU 比如在没有 MMU 的小型设备上。不过,现在小型的嵌入式设备等也都开始具有 MMU 这种功能了,因此这个差异你可以当做没有(这是在支持 MMU 的 Liunx 系统的情况之下),也可以当做是有。
但是不管 Linux 和 Unix 有多大的不同,Linux 都要打上 Unix 的烙印。之外,所有 Unix 内核都同宗同源,并且提供相同的 API,现在的 Unix 内核设计也存在许多相似之处。(PS:所以说为啥有些书书名写的是 Unix 的开发但是同样也适用于 Linux 或者其他 Unix 变种,这主要是因为它们提供的 API 都会相同的)。
宏内核和微内核
操作系统内核可以分为两大阵营:单内核(宏内核)和微内核。
-
单内核是一种比较简单的设计。1980 年之前,所有的内核都被设计为单内核。单内核就是将内核从整体上作为一个单独的大过程来实现,同时内核也运行在一个单独的地址空间上。这样的内核通常以单个静态二进制文件的形式存放在磁盘中,所有的内核服务都运行在内核态,并处于同一个地址空间:内核可以直接通过调用函数的方式来调用服务,因此内核之间的通信是微不足道的。比如,文件系统经常被作为一个服务,那么单内核的情况想要读写文件,调用读写函数即可,调用的读写函数最终通过一层一层的调用完成读写操作。
这种内核具有简单和性能高的特性,Unix 和 Linux 都是这样的设计。但是 Linux 汲取了微内核的精华,使用了模块设计、抢占式内核、支持内核线程以及动态装载内核模块的能力。同时,Linux 弃掉了微内核设计上的性能缺陷保留了单内核的优势,即让所有事情直接运行在内核态,调用函数即可。
-
微内核并不作为一个单独的大过程来实现。相反,微内核的功能被划分为多个独立的过程,每个过程被叫做服务,比如将文件系统单独拎出来作为一个服务。大多情况下,只有强烈请求特权的服务才会运行在特权模式下,其他服务都只运行在用户空间。不管运行在哪,所有的服务都保持独立并且运行在各自的地址空间上。因此,当调用服务时,不可能像单模块内核那样直接调用函数,而是通过消息传递或者 RPC 等通信方式来告诉服务需要完成的操作,各个服务之间通过 IPC 机制互通消息。比如,在某个用户进程需要读写的时候,可能就需要使用消息传递的方式告诉文件系统服务需要完成读写操作,文件系统服务完成之后再通过消息传递的方式将读取的内容或者写入的结果返回去。
微内核的好处在于,一个服务的失效避免导致另一个服务的失效。但是,微内核的缺陷在于,IPC 机制的开销多于函数调用,而且有些服务可能运行在用户空间,那么又会涉及到内核空间和用户空间的上下文切换,开销更大。这些开销在单内核中是不存在的。
为了避免频繁的上下文开销,实际应用基于微内核的系统都让大部分或全部服务位于内核。Windows NT 系列(Windows XP、Windows Vista 和 Windows 7 等)和 Mach(Mac OS X 的组成部分)都是微内核的典型实例。但是不管哪种,不让任何微内核服务运行在用户空间的方式都违背了微内核设计的初衷。
假如你不太好理解,那么你可以单内核和微内核理解为现在流行的微服务和单体应用。Linux 和 Unix 采用的就是类似单体应用的方式,所有的功能模块都集成在一块,想用调用某个功能,那么直接调用函数即可。然而,Linux 优秀的一点,它采用了单体应用中模块化设计的思想,也就是将内核功能分成一个个单独的模块,可动态加载和卸载。但是都是在一个单体应用。而微内核的方式则类似于现在的微服务架构,微内核将内核的功能拆分成一个个独立的模块,然后每个模块之间通过通信的方式来实现某个操作或者功能。