Linux 历史简介[size=0.875]Linux 还是 GNU/Linux?
[size=0.875]您可能已经注意到,作为操作系统的 Linux 在某些情况下被称为“Linux”,在其他情况下被称为“GNU/Linux”。这背后的原因是 Linux 是操作系统的内核。使操作系统有用的广泛应用程序是GNU 软件。例如,窗口系统、编译器、各种 shell、开发工具、编辑器、实用程序和其他应用程序存在于内核之外,其中许多是GNU 软件。出于这个原因,许多人认为“GNU/Linux”是操作系统更合适的名称,而“Linux”仅指内核时更合适。
虽然 Linux 可以说是最受欢迎的开源操作系统,但考虑到操作系统的时间线,它的历史实际上很短。在计算的早期,程序员使用硬件语言在裸硬件上进行开发。缺少操作系统意味着一次只有一个应用程序(和一个用户)可以使用这个庞大而昂贵的设备。早期的操作系统是在 1950 年代开发的,目的是提供更简单的开发体验。示例包括为 IBM 701 开发的通用汽车操作系统 (GMOS) 和由北美航空为 IBM 709 开发的 FORTRAN 监控系统 (FMS)。
在 1960 年代,麻省理工学院 (MIT) 和许多公司为 GE-645 开发了一种名为 Multics(或多路复用信息和计算服务)的实验性操作系统。该操作系统的开发者之一 AT&T 退出 Multics 并于 1970 年开发了自己的操作系统,称为 Unics。与此操作系统一起使用的是 C 语言,为此开发了 C 语言,然后对其进行了重写,以使操作系统开发具有可移植性。
20 年后,Andrew Tanenbaum 创建了 UNIX® 的微内核版本,称为 MINIX(用于最小 UNIX),可在小型个人计算机上运行。这个开源操作系统激发了 Linus Torvalds 在 1990 年代初期对 Linux 的初步开发(见图 1)。
图 1. 主要 Linux 内核版本的简短历史
Linux 从一个单人项目迅速演变为一个涉及数千名开发人员的全球开发项目。Linux 最重要的决定之一是采用 GNU 通用公共许可证 (GPL)。在 GPL 下,Linux 内核受到保护,免于商业开发,它还受益于 GNU 项目(Richard Stallman 的用户空间开发,其源代码使 Linux 内核的源代码相形见绌)的用户空间开发。这允许有用的应用程序,例如 GNU Compiler Collection (GCC) 和各种 shell 支持。
Linux内核简介现在开始高空观察 GNU/Linux 操作系统架构。您可以从两个层面考虑操作系统,如图 2 所示。
图 2. GNU/Linux 操作系统的基本架构
[size=0.875]系统调用接口 (SCI) 的方法
[size=0.875]实际上,体系结构并不像图 2 中所示的那样清晰。例如,处理系统调用(从用户空间到内核空间的转换)的机制可能因体系结构而异。与使用传统 int 80h 方法的旧 x86 处理器相比,提供虚拟化指令支持的较新 x86 中央处理器 (CPU) 在此过程中更有效。
顶部是用户或应用程序空间。这是执行用户应用程序的地方。用户空间下面是内核空间。在这里,Linux 内核存在。
还有 GNU C 库 (glibc)。这提供了连接到内核的系统调用接口,并提供了在用户空间应用程序和内核之间转换的机制。这很重要,因为内核和用户应用程序占用不同的受保护地址空间。虽然每个用户空间进程都占用自己的虚拟地址空间,但内核只占用一个地址空间。
Linux内核可以进一步分为三个粗略的层次。最上面是系统调用接口,实现了read和等基本功能write。系统调用接口下面是内核代码,可以更准确的定义为与架构无关的内核代码。此代码对于 Linux 支持的所有处理器架构都是通用的。下面是依赖于体系结构的代码,它形成了更常见的 BSP(板级支持包)。此代码用作给定架构的处理器和平台特定代码。
Linux 内核的属性在讨论大型复杂系统的架构时,您可以从多个角度查看系统。架构分解的一个目标是提供一种更好地理解源代码的方法,这就是我们在这里要做的。
Linux 内核实现了许多重要的架构属性。在较高级别和较低级别,内核被分层为许多不同的子系统。Linux 也可以被认为是单一的,因为它将所有基本服务都集中到内核中。这与微内核架构不同,微内核架构中内核提供通信、I/O、内存和进程管理等基本服务,更具体的服务插入微内核层。每个都有自己的优势,但我会避开这场辩论。
随着时间的推移,Linux 内核在内存和 CPU 使用率方面变得高效,并且非常稳定。但考虑到 Linux 的大小和复杂性,它最有趣的方面是它的可移植性。Linux 可以编译为在具有不同架构约束和需求的大量处理器和平台上运行。一个例子是 Linux 能够在具有内存管理单元 (MMU) 的进程以及不提供 MMU 的进程上运行。Linux 内核的 uClinux 端口提供了非 MMU 支持。
Linux内核的主要子系统现在让我们使用图 3 中所示的细分作为指导,看看 Linux 内核的一些主要组件。
图 3. Linux 内核的一种架构视角
系统调用接口SCI 是一个薄层,它提供了从用户空间到内核执行函数调用的方法。如前所述,即使在同一处理器系列中,该接口也可以依赖于体系结构。SCI 实际上是一个有趣的函数调用复用和解复用服务。您可以在 ./linux/kernel 中找到 SCI 实现,在 ./linux/arch 中可以找到与体系结构相关的部分。
流程管理[size=0.875]什么是内核?
[size=0.875]如图 3所示,内核实际上只不过是一个资源管理器。无论被管理的资源是进程、内存还是硬件设备,内核都会管理和仲裁多个竞争用户(在内核和用户空间中)之间对资源的访问。
流程管理侧重于流程的执行。在内核中,这些被称为线程,代表处理器的单独虚拟化(线程代码、数据、堆栈和 CPU 寄存器)。在用户空间中,通常使用术语进程,尽管 Linux 实现没有将这两个概念(进程和线程)分开。内核通过 SCI 提供应用程序接口(API)来创建新进程(fork、exec 或可移植操作系统接口 [POSIX] 函数)、停止进程(kill、exit)以及它们之间的通信和同步(信号或 POSIX 机制)。
进程管理中还需要在活动线程之间共享 CPU。内核实现了一种新颖的调度算法,该算法在恒定时间内运行,而不管竞争 CPU 的线程数量如何。这称为 O(1) 调度程序,表示调度一个线程所花费的时间与调度多个线程所花费的时间相同。O(1) 调度程序还支持多处理器(称为对称多处理,或 SMP)。您可以在 ./linux/kernel 中找到进程管理源代码,在 ./linux/arch 中找到与体系结构相关的源代码)。
内存管理内核管理的另一个重要资源是内存。为了提高效率,考虑到硬件管理虚拟内存的方式,内存在所谓的页面中进行管理(对于大多数架构来说,大小为 4KB)。Linux 包括管理可用内存的方法,以及用于物理和虚拟映射的硬件机制。
但是内存管理不仅仅是管理 4KB 缓冲区。Linux 提供了超过 4KB 缓冲区的抽象,例如slab 分配器。这种内存管理方案使用 4KB 缓冲区作为其基础,然后从内部分配结构,跟踪哪些页面已满、部分使用和空。这允许该方案根据更大系统的需要动态地增长和收缩。
支持多个内存用户,有时可能会耗尽可用内存。因此,页面可以移出内存并移到磁盘上。这个过程称为交换,因为页面从内存交换到硬盘上。您可以在 ./linux/mm 中找到内存管理源。
虚拟文件系统虚拟文件系统 (VFS) 是 Linux 内核的一个有趣方面,因为它为文件系统提供了通用接口抽象。VFS 在 SCI 和内核支持的文件系统之间提供了一个交换层(见图 4)。
图 4. VFS 提供用户和文件系统之间的交换结构
在 VFS 的顶部是对 open、close、read 和 write 等函数的通用 API 抽象。VFS 的底层是文件系统抽象,定义了上层功能的实现方式。这些是给定文件系统的插件(其中存在 50 多个)。您可以在 ./linux/fs 中找到文件系统源。
文件系统层下面是缓冲区缓存,它为文件系统层提供了一组通用的功能(独立于任何特定的文件系统)。该缓存层通过将数据保留一小段时间(或推测性地提前读取以便数据在需要时可用)来优化对物理设备的访问。缓冲区缓存下方是设备驱动程序,它们实现特定物理设备的接口。
网络栈网络堆栈在设计上遵循以协议本身为模型的分层架构。回想一下,互联网协议 (IP) 是位于传输协议(最常见的是传输控制协议,或 TCP)之下的核心网络层协议。TCP上面是sockets层,通过SCI调用。
套接字层是网络子系统的标准 API,为各种网络协议提供用户界面。从原始帧访问到 IP 协议数据单元 (PDU),再到 TCP 和用户数据报协议 (UDP),套接字层提供了一种标准化的方式来管理连接和在端点之间移动数据。您可以在内核中的 ./linux/net 中找到网络资源。
设备驱动程序Linux 内核中的绝大多数源代码都存在于使特定硬件设备可用的设备驱动程序中。Linux 源代码树提供了一个驱动程序子目录,该目录按支持的各种设备进一步划分,例如蓝牙、I2C、串行等。您可以在 ./linux/drivers 中找到设备驱动程序源。
依赖于架构的代码尽管 Linux 的大部分内容与其运行的架构无关,但为了正常操作和效率,必须考虑架构的一些元素。./linux/arch 子目录定义了内核源代码的体系结构相关部分,这些部分包含在许多特定于体系结构的子目录中(共同构成 BSP)。对于典型的桌面,使用 i386 目录。每个体系结构子目录都包含许多其他子目录,这些子目录专注于内核的特定方面,例如引导、内核、内存管理等。您可以在 ./linux/arch 中找到与体系结构相关的代码。
Linux 内核的有趣特性如果 Linux 内核的可移植性和效率还不够,它还提供了其他一些在前面的分解中无法归类的功能。
Linux 作为一个生产操作系统和开源,是新协议和这些协议进步的一个很好的测试平台。Linux 支持大量网络协议,包括典型的 TCP/IP,以及高速网络的扩展(大于 1 Gigabit Ethernet [GbE] 和 10 GbE)。Linux 还支持诸如流控制传输协议 (SCTP) 之类的协议,它提供了许多高于 TCP(作为替代传输级协议)的高级功能。
Linux 也是一个动态内核,支持动态添加和删除软件组件。这些被称为可动态加载的内核模块,它们可以在需要时(当发现特定设备需要该模块时)或在任何时间由用户在引导时插入。
Linux 的最新进展是将其用作其他操作系统(称为管理程序)的操作系统。最近,对内核进行了修改,称为基于内核的虚拟机 (KVM)。此修改启用了一个新的用户空间接口,允许其他操作系统在支持 KVM 的内核上运行。除了运行 Linux 的另一个实例之外,Microsoft® Windows® 也可以被虚拟化。唯一的限制是底层处理器必须支持新的虚拟化指令。
构造方法StringTokenizer 有三个常用构造方法
- 直接输入要解析的字符串,默认会把 “ \t\n\r\f” 当作分隔符。同时,解析返回结果不包含分隔符
Constructs a string tokenizer for the specified string. The tokenizer uses the default delimiter set, which is “ \t\n\r\f”: the space character, the tab character, the newline character, the carriage-return character, and the form-feed character. Delimiter characters themselves will not be treated as tokens.
Params: str – a string to be parsed.
Throws: NullPointerException – if str is null
public StringTokenizer(String str) { this(str, " \t\n\r\f", false);}- 输入要解析的字符串和分隔符列表。解析返回结果不包含分隔符
Constructs a string tokenizer for the specified string. The characters in the delim argument are the delimiters for separating tokens. Delimiter characters themselves will not be treated as tokens.
Note that if delim is null, this constructor does not throw an exception. However, trying to invoke other methods on the resulting StringTokenizer may result in a NullPointerException.
Params: str – a string to be parsed.
delim – the delimiters.
Throws: NullPointerException – if str is null
public StringTokenizer(String str, String delim) { this(str, delim, false);}- 输入要解析的字符串和分隔符列表。你决定要不要包含分隔符,true 为包含。
用法获取每一段字符串 nextToken()public String nextToken()返回每一段被分隔的字符串 (token),等效于 Scanner.next(), 返回结果是否包含分隔符取决于你的构造参数
如果没有更多 token,会抛出 java.util.NoSuchElementException
看看还有没有更多待分隔的字符串 hasMoreTokens()public boolean hasMoreTokens()
true 表示有更多,可以安全地调用 nextToken()
走得更远本文只是触及了 Linux 内核架构及其特性和功能的皮毛。您可以查看每个 Linux 发行版中提供的文档目录,以获取有关内核内容的详细信息。