
Linux并非操作系统而是一个内核我记不清在网上看过这句话多少次但那些总复述它的人却鲜少愿意详加解释这篇文章将沿着进程树回溯至首个进程精确理清这条分界线。问题的答案竟藏在用户进程中这点或许会让很多人感到意外首要任务是理解进程的创建机制。Windows系统中的进程可通过系统调用请求创建新进程系统会直接执行。而Linux类UNIX系统要求进程先申请自我克隆 克隆产生的进程再请求替换自身程序两种系统管理进程创建的方式存在哲学层面的差异但核心理念始终一致。用户进程皆由其它用户进程创建。 请思考一下如果所有用户进程都由其它用户进程创建那就意味着每个进程都有创建它的父进程但由于这些父进程本身也是用户进程它们必然也被其它用户进程创建以此类推。这意味着如果我们向上 追溯最终会发现一个作为所有用户进程始祖的单一进程。在LINUX系统中你实际上可以通过运行pstree命令来可视化这颗树状结构正如下图所示树状结构根部确实存在一个其它所有进程的始祖进程 但这个根进程引出了一个疑问它是由谁创建的? 它不可能是由其它进程创建的否则就不会是树状结构的根。答案就在这里因为在某种意义上该进程之前发生的所有事情都被视为内核的一部分 既然如此我们就来定义何为内核。 计算机系统的一端是硬件设备另一端则是以运行多个应用软件为目的的用户。处理硬件实际上非常繁琐且存在风险为防止应用程序因漏洞或劣质代码导致故障在用户应用与硬件之间存在非常复杂的中间层 这一层最底部的组件也就是直接与硬件交互的部分我们称之为内核这种设计的核心思想是每当用户程序需要操作硬件时内核都代表它来执行硬件交互。用户进程仍试图自行操作因此需要额外的保护机制 为此CPU被设计为两种运行模式受限模式与特权模式当CPU处于特权模式时可执行指令集中的所有指令包括配置硬件通过页表管理内存控制中断以及与GPU网卡存储设备等输入输出外设通信在受限模式下CPU仅能执行安全指令子集无法直接访问硬件及关键系统资源该模式下处理器可执行计算比较循环等常规程序逻辑。但无法运行特权指令由于特权模式完全掌控处理器和系统资源 运行于此模式的软件决定着受限模式代码的权限范围。所有用户程序都在受限模式下运行而内核则处于特权模式。因此我们对应使用两个术语内核空间和用户空间。当用户程序需要访问硬件例如屏幕显示文字或通过网络发送消息时只能通过系统调用请求内核代其执行现代系统正是通过这种方式确保普通用户程序无法直接访问硬件既保障正常运行又维护系统安全 不过该隔离层并非单一组件内核整体运行在特权模式下但其内部存在一系列子系统各自管理特定资源或提供特定服务没有内核用户应用程序就无法存在因为它们需要内核来完成受限模式下无法 执行的操作系统调用接口是内核最接近用户程序的部分因此当我们启动计算机时在从用户角度执行任何有效操作前必须通过引导启动过程将内核可执行代码加载到内存中当所有关键组件加载就绪后 内核会创建唯一一个用户进程这就是INIT进程在UNIX,LINUX系统中PID为1的 INIT进程是唯一不由其它用户进程创建的用户进程在内核代码深处存在一段硬编码例程他会按优先级顺序在文件系统中 寻找名为INIT的可执行文件查看LINUX内核源码中的main.c文件找到名为kernel_init的函数会初始化大量资源最终在启动过程中会尝试从指定路径加载INIT 程序该函数会调用run_init_process函数而后者 内部会调用execlp注意这并非用户程序使用的传统excep系统调用由于这段代码本身就在内核模式下执行无需系统调用故使用名为kernel_excep的变体直接执行新进程加载例程。 还需注意如果所有路径都找不到INIT文件内核将在启动过程中触发PANIC此刻你可能疑惑这些内容对解决本篇开头提出的问题有何帮助因为内核开发者对用户空间软件的关注仅止于此。开发者直接将启动 首个用户空间的例程硬编码在内核中但对该进程后续的具体行为毫不关心实际上LINUX内核源码中根本找不到这个INIT程序它不属于内核组成部分.shell,显示服务器会话管理器网络管理器软件包管理器用于远程访问的SSH服务管理器如果系统支持图形界面那么桌面环境及其附带组件终端模拟器文件浏览器任务管理器设置管理器也在此列。这些都我们通常认为是操作系统组成部分的东西。然而它们都不属于内核。所以操作系统并非全部运行于内核空间我们通常所称的操作系统其实大部分是用户空间进程的集合。理解这点最好的例子就是桌面环境KDEGNOME那些停靠栏任务栏窗口平铺系统以及我们联想到桌面体验的抽象概念它们都运行在用户模式 桌面环境并非内核的一部分因此不直接操纵硬件它通过另一个用户空间进程显示服务器与内核交互。后者利用图新个相关的系统调用将渲染好的缓冲区内容呈现到屏幕。内核并不理解窗口的概念也不知道按钮控件和深色模式是什么屏幕上显示的内容是用户空间软件将像素渲染至缓冲区后请求内核通知GPU进行显示的结果。对内核而言这只是内存访问和硬件操作。因此更准确的定义是操作系统是由软件构成的集合。其中被称为内核的组件运行在特权模式其余部分则运行在受限模式这正是两者的分界线。这里有个微妙但关键的要点所有用户进程并非凭空出现它们必须由另一个进程创建。这时就轮到INIT进程登场了。在引导启动阶段当内核完成关键子系统初始化并准备切换到用户空间时会执行前文提到的硬编码例程来定位并启动INIT进程。这么设计的原因是仅靠这个单一用户空间INIT进程便足以繁衍出前面看到的用户空间进程树。从此刻开始操作系统的其余部分并非直接原自内核而是由构建应用程序所依赖全套服务基础设施的用户空间代码生成。比如在UBUNTU上运行进程树命令时你看到的进程树共同塑造了UBUNTU不同于其他发行版例如fedora)的独特体验。根进程INIT通过启动各类应用程序最终塑造出你喜爱的LINUX发行版的独特行为与个性。这也解释了为何不存在所谓标准版LINUXLINUX开发者仅提供内核。讽刺的是如果没有用户空间程序运行其上这个内核本身完全无法发挥作用。内核需要这些用户空间程序才能真正提供功能。如果你曾好奇LINUX发行版的诞生过程这就是答案它们共享相同内核但基于该内核构建的服务基础设施会因发行版创建者的设计理念而异。事实上进程树中显示的根进程也会有不同名称因为INIT有多种实现方案。从传统的sysvinit,busybox到更复杂的 systemd,应有尽有。如前所述内核并不关心这个进程的具体运作方式正因为如此不同项目对INIT进程的行为规范和职责划分形成了不同理念。内核只负责启动首个用户空间进程。该进程的本质及其后续行为完全不属于内核的管辖范围。不过内核确实赋予了这个用户进程若干特殊属性。其一是固定获得进程ID 1普通程序的进程ID很难保证但是INIT进程必然获得ID 1。这也使得到它在任务管理器等工具中易于识别。第二条与其说是特权不如说是责任INIT进程永远不应终止。原因其实非常简单内核不喜欢孤儿进程。进程不能用于已经不存在的父进程。INIT必须常驻运行因为内核需要它来收养孤儿进程。所以尽管内核并不关心INIT进程的具体行为但确实要求它保持长生不老。即使发送KILL -9信号也无法终止它。内核坚持该进程永不终止的原则直接拒绝传递终止信号。正因为永不终止的特性许多INIT实现还被设计为故障恢复机制如果关键服务崩溃比如图形界面INIT进程可能会重启它。如果INIT进程本身因漏洞崩溃甚至是无错退出内核都会发生PANIC。尽管内核自身并无故障。因此INIT进程确实拥有某些特权但是它并不运行在内核空间。真正的起点是进程0——swapper/idle进程。这是内核在启动时“凭空”创建的没有父进程也从来不在用户空间运行。进程1init是进程0通过fork()创建的第一个用户态进程。进程1init是进程0通过fork()创建的第一个用户态进程。进程0由内核构造永远运行在内核空间。进程1由内核“助产”但之后便运行在用户空间负责启动所有其他用户进程。区分内核与操作系统其它部分的分界线相对容易定义所有以特权模式运行的组件都属于内核但其它部分呢如果操作系统所有这些组件都是普通用户空间进程那我们该如何划分操作系统与用户应用程序的边界。操作系统通常预装软件 比如几乎所有现代操作系统都会内置网页浏览器LINUX世界通常是firefox,这是否意味着浏览器属于操作系统?多数人会反对因为浏览器并系统运行必须的但同样的论点也适用于图形界面GUI文件资源管理器任务管理器文本编辑器系统监视器 乃至桌面环境本身毕竟这些程序的功能理论上都可以通过终端和命令行界面实现。这是否意味着它们只是用户应用程序而非操作系统的一部分多数人并不这么认为桌面环境及其应用才是让操作系统独具特色的关键当然这个观点见仁见智 但是想想MAC OSWINDOWSANDROID等图形界面操作系统它们的设计理念就是用户完全不需要借助命令来操作电脑这类系统既没有SHELL,也没有终端所有电脑操作都必须通过GUI完成在这种环境下文件资源管理器任务管理器和设置面板等工具 虽然只是用户空间进程但却是与系统交互的唯一途径因此变得必不可少所以操作系统与用户应用程序的边界更难划定在大部分程序员看来这条界限有些模糊因为我们要如何定义操作系统的用户空间部分是所有预装在操作系统中的程序么 还是没有底层替代方案的程序 可能最接近的定义是这个所有INIT进程自动启动的程序因为理论上它们都属于核心服务但是这个定义本身也存在问题它可能不是必要的某些基础工具并非由INIT进程启动因为它们是用户主动调用时才需要的 比如终端或者BUSYBOX集这些几乎预装载所有LINUX发行版中的工具集合提供了你在SHELL中使用的各种小而关键的指令。这些程序大多不是由INIT进程启动的尽管我们大多数都会承认它们是用户进程但却很难不把它们看成是操作系统的一部分。试想一下 终端里没有任何指令的情形那样的LINUX就失去了灵魂。所以没有一条单一的、普适的线能完美区分“操作系统”和“应用程序”。在讨论时必须先明确语境,是在讨论内核态与用户态的技术划分还是在讨论一个发行版/平台的完整交付物。总结内核只负责“启动第一个用户进程”这个动作至于这个进程是 systemd、SysV init、还是你自己的一个简单 shell内核完全不在意。init 进程本身不是内核的一部分但它的存在是内核“刻意制造的边界”。Unix的设计者尤其是Ken Thompson和Dennis Ritchie秉持一个信念系统调用应该是最小、最原子的操作复杂的场景由用户空间的组合来实现。哲学边界内核是所有进程的“最后授权者”和“初始创造者”。第一个用户进程诞生之前的所有代码都属于内核第一个用户进程诞生之后内核退居幕后成为“服务的提供者”而非“执行的参与者”内核是唯一被允许在裸硬件上“信任执行”的软件其它所有程序都必须通过它来间接接触硬件。内核空间是被“信任”的代码用户空间是被“隔离”的代码。Linux是内核不是操作系统。结束