S1

操作系统是什么

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 台计算机具备 CPU、内存和外设,仍不等于它已经能方便、可靠地运行应用。CPU 能执行指令,内存能保存正在使用的数据,磁盘和网卡能连接持久存储与网络;这些部件提供的是能力。谁可以使用这些能力、多个应用怎样共用它们、发生错误时由谁收拾现场,需要另一套规则。本课讨论的操作系统,就是让硬件能力成为可共享、可约束、可使用服务的基础软件。

学完本课,应能从资源管理、抽象和隔离3 个角度解释操作系统的作用,区分用户态与内核态,描述1 次系统调用的大致路径,并辨认发行版、容器镜像和虚拟机镜像各自包含什么。命令示例以 Linux 为背景,只用于观察这些概念,不要求预先掌握云平台,也不展开进程调度和虚拟内存的内部算法。

没有操作系统时,程序要承担什么

设想1 台只运行温度采集程序的设备。程序启动后读取传感器,把结果写到指定位置,然后重复。若硬件固定、功能单一,开发者可以让程序直接控制设备,这类没有通用操作系统的运行方式通常称为裸机运行。它并非错误方案,只是把许多责任放进了程序本身:初始化硬件、识别设备状态、决定何时读写,以及处理异常。

现在再加入1 个显示程序和1 个网络上传程序。3 个程序都可能需要 CPU,都要在内存中保存数据,2 个程序还可能同时写同1 份记录。如果每个程序自行决定写到哪里、何时访问设备,某个地址算错就可能覆盖别人的数据;2 个写入动作交错,可能让记录只剩一半。即使大家约定先后顺序,开发者也必须在每次增加功能时重新检查这些约定。

问题还不止于共享。更换1 种磁盘后,应用可能需要重写设备控制代码;某个上传程序陷入死循环后,显示功能可能无法继续;程序退出时遗留的设备状态和缓冲区也需要清理。硬件越丰富、程序越多,这些工作越难靠每个应用分别完成。操作系统把大量共同责任集中起来,提供一套稳定接口和受保护的执行环境。

因此,“为什么需要操作系统”不能只回答为“让电脑开机”。开机只是进入可用状态的过程。操作系统更持久的价值,是在整个运行期间持续协调资源、提供服务并约束程序行为。它也有成本,包括自身占用的资源、接口调用的开销和维护复杂度。对于功能简单且限制明确的设备,直接控制硬件仍可能合适;对于通用计算机和服务器,共同管理通常更容易形成可靠的系统。

三大职责:管理、抽象与隔离

第1 项职责是资源管理。资源包括 CPU 执行时间、内存容量、存储空间和设备访问能力。操作系统记录使用情况,决定哪些请求能够继续,哪些需要等待,哪些应当失败,并在程序结束后回收相应资源。这里的“管理”不代表资源无穷,也不保证所有程序都能同时获得最大性能;它意味着使用资源要遵守统1 规则,而不由程序任意占用。

第2 项职责是抽象。应用通常以文件名读写内容,不需要知道磁盘上的控制寄存器;应用通过套接字收发网络数据,不需要逐次指挥网卡传输。文件、进程和套接字都是理解系统的重要抽象。进程可以先理解为1 个正在运行的程序实例,以及系统为它维护的执行状态和资源。1 个程序可以启动多个进程,同1 份程序文件与某次运行实例并不是同1 个概念。

第3 项职责是隔离。1 个普通应用不应随意改写另1 个应用的数据,也不应直接修改影响整台机器的关键状态。操作系统结合硬件提供的保护机制建立边界,再通过权限和资源限制控制跨越边界的行为。隔离降低故障扩散和越权访问的机会,但不能据此推断应用绝对安全。错误的授权、内核漏洞和共享资源上的竞争,仍然可能破坏预期。

操作系统以管理、抽象和隔离组织共享硬件资源
操作系统以管理、抽象和隔离组织共享硬件资源

3 项职责经常同时发生。应用请求打开1 个文件时,系统用文件名这一抽象寻找对象,检查调用者是否有权访问,再为成功打开的对象分配跟踪信息。稍后应用关闭文件,系统释放相应引用。因此,权限错误、配额耗尽和设备故障虽然都会表现为1 次操作失败,根因却可能属于不同职责。诊断时先判断失败发生在哪1 层,比笼统地说“系统坏了”更有帮助。

再看1 个网络服务:它通过套接字接收请求,内核负责把到达的数据交给相应的套接字,并维护接收缓冲区。应用暂时忙于计算时,已经到达的数据可以先等待,但缓冲区有容量限制,不能无限积压。以 TCP 连接为例,接收端可通过通告接收窗口约束对端继续发送的数据量;应用若长期不读取,对端的发送也可能受阻。这里,套接字隐藏了网卡收发的具体步骤,缓冲区体现资源管理,而不同进程不能任意读取彼此连接中的数据,则体现访问边界。抽象提供了统一用法,却没有消除等待和容量限制。

隔离也有明确的责任范围。假设2 个获准写入同1 个报告文件的进程,各自先读旧内容,再生成并写回新内容,后写入者可能覆盖前1 个进程的结果。内核即使正确检查了双方权限,也不知道业务上应该保留哪份报告。应用仍需采用文件锁、单独的输出文件或其他协调办法,并约定所有写入者共同遵守。操作系统能阻止未经授权的访问,却不会自动把多个合法操作组合成正确的业务流程。判断故障时,要区分保护边界失效与应用缺少协作规则。

共享资源需要可见的边界

抽象让资源更容易使用,也可能让实际成本不容易被看见。打开1 个文件,看起来只是获得1 个编号,系统却需要维护与该编号有关的状态。Linux 中,这种由进程使用的小整数编号称为文件描述符,常缩写为 FD。它可以指向普通文件,也可以用于管道、套接字等对象。编号只是进程访问对象的入口,不能简单当作磁盘文件本身。

文件描述符通常受每进程限制和系统整体容量等多层约束。某个进程用完自身可用的描述符时,机器仍可能有大量空闲内存,其他进程也可能继续正常工作。相反,所有进程都没有碰到各自上限,也不意味着系统整体一定还有余量。容量评估需要同时观察局部限制和整体预算,不能把不同层级的限制混成1 个数字。

下面的滑块用简化模型展示这种关系:进程数乘以每进程期望打开的描述符数,得到总需求,再与1 个假设的整体预算比较。所有进程需求相同、没有共享关系、没有背景占用,都是为了看清关系而作出的教学假设。现实中系统如何计数还与打开的对象及其共享方式有关,这里显示的数字不对应某台 Linux 主机的真实可用容量。

如果增加进程数后总需求超过预算,不能仅靠提高每进程上限解决问题。上限表示允许申请到什么程度,预算表示模型中最多能容纳多少需求,两者含义不同。实际方案可能需要减少连接与文件的长期占用、限制并发、及时关闭不再使用的描述符,或增加经过验证的系统容量。应用处理失败返回值的能力,也是资源管理的一部分:请求没有成功时,继续使用1 个假定存在的资源会产生后续错误。

内核与用户态:谁有资格执行关键操作

内核是操作系统中负责核心资源管理和保护的部分。对本课采用的常见 Linux 系统而言,内核处理进程相关管理、内存管理、文件系统和设备访问等核心事务。用户日常接触的命令解释器、桌面程序和大多数应用运行在用户态。用户态与内核态描述的是执行权限层次,不是屏幕上2 个可见窗口,也不是2 个物理处理器。

处理器提供权限检查等硬件机制,内核负责配置和使用这些机制。用户态代码可以进行普通计算、操作获准访问的数据,但无法随意执行受限制的关键操作。直接访问受保护区域或执行不允许的指令,通常会触发异常,由内核按照规则处理。硬件与内核共同维持边界,单靠应用承诺“不会乱写”不足以建立这样的保护。

用户程序通过受控入口请求内核管理资源
用户程序通过受控入口请求内核管理资源

当编辑器需要把内容保存到磁盘时,它不需要获得任意控制设备的能力,而是向内核提出具体请求。内核检查请求、执行允许的部分,再把结果交还应用。这个设计把“需要使用某项服务”和“拥有整个系统的控制权”分开。普通用户也能保存自己有权写入的文件,因为内核可以按权限代为完成受保护的工作。

用户身份与执行模式是2 个不同维度。以管理员身份运行的普通程序,其大部分指令仍在用户态执行;进入内核态也不意味着调用者自动获得管理员权限。内核办理请求时仍要检查进程的身份、权限和其他约束。把“我是管理员”误当成“我正在内核里”,会让人同时误解权限边界和故障定位。

内核态也不等于“更快的模式”。1 次进入内核的请求需要经过受控入口,可能还要检查参数、等待设备或处理错误。只有确实需要系统服务时才应发起对应请求;在用户态完成计算并不低级。至于内核如何让多个执行中的程序轮流使用 CPU,以及各自地址空间怎样建立,将在后续课程分别讨论。

系统调用:把请求交给内核

系统调用是用户程序请求内核服务的受控接口。例如,程序需要打开文件、读取内容或建立某些系统资源时,通常会通过相应系统调用获得内核帮助。程序员往往先调用语言运行库提供的函数,再由库根据需要发起系统调用。库函数和系统调用不是一一对应关系:有的库函数只做用户态计算,有的组合多个请求,有的先使用已经缓存的数据。

以读取文件为例,应用先准备好接收数据的缓冲区,再通过库函数提出读取请求。最终发起读取系统调用时,需要传递文件描述符、缓冲区位置和希望读取的字节数等参数。处理器通过专门的受控机制进入内核入口,内核依据调用编号找到相应服务,并检查参数和访问条件。应用不能因为提供了1 个地址,就要求内核无条件向那里写数据。

文件读取从用户程序经系统调用进入内核再返回结果
文件读取从用户程序经系统调用进入内核再返回结果

如果条件满足,内核通过文件系统等相应部分获取可提供的数据,随后把实际读取量或错误结果返回给用户程序。数据可能已经在内存缓存中,因此1 次读取系统调用不一定引发1 次磁盘访问。1 次请求也不保证读满期望字节数;读到文件结尾时可能返回零,发生错误时则需要按照所用接口的约定处理。应用的正确行为依赖对返回结果的检查。

进入内核后再返回,并不必然意味着换成另1 个进程执行。这里首先发生的是执行权限层次的变化;如果请求需要等待,系统之后也可能安排其他程序运行,但那属于另1 层机制。把这2 个概念分清,就不会把每个系统调用都想象成“系统启动了1 个帮手进程”,也不会从调用次数直接推导程序切换次数。

系统调用接口还构成应用兼容性的边界之一。相同源代码能在多个系统编译,不代表为某个平台生成的程序文件可以直接在其他平台运行。运行时还要考虑处理器架构、可执行文件格式、所需系统调用和运行库。1 个“找不到文件”的启动错误,也可能与程序指定的解释器或动态加载器缺失有关,不能仅凭主程序文件存在就认定环境完整。

内核、发行版与应用运行环境

日常说“安装 Linux”,通常安装的是某个 Linux 发行版。Linux 内核提供核心机制,发行版则把内核、系统工具、运行库、软件包管理和默认配置等组织成可安装、可维护的系统。发行版的版本号、内核版本号和应用版本号各自描述不同对象。知道其中1 个,无法可靠推出另外2 个,也无法据此判断全部安全更新是否已经安装。

同一内核系列之上可以配备不同的用户态工具和运行库。同一发行版也可能支持多个内核版本,并通过自己的更新流程修复问题。因而,“版本更新”需要说明更新了哪1 层:应用、运行库、系统工具还是内核。涉及内核的修复通常还要确保修复后的内核实际生效;具体是否需要重启,取决于系统支持的更新方式和采用的维护策略。

发行版将内核与用户态工具及维护机制组合起来
发行版将内核与用户态工具及维护机制组合起来

应用运行环境不只包括1 个可执行文件。程序可能依赖动态运行库、语言运行时、证书、时区数据和配置文件。有些依赖只有执行到特定路径时才会被使用,因此“程序能启动”只是兼容性检查的起点。例如服务启动成功,却在第1 次验证远端证书时失败,问题就可能来自信任证书缺失,而不是 CPU 或内核不能运行它。

选择发行版时,需要把支持周期、软件包来源、团队维护能力和应用依赖一起考虑。体积较小的环境能减少携带的组件,但缺少诊断工具、必要证书或所需运行库,也会增加运行与排障成本。环境越精简,越需要明确应用真正依赖什么,以及出错时通过什么途径取得证据。不能把镜像文件更小直接等同于更适合所有服务。

容器与虚拟机中,操作系统在哪里

虚拟机通常由虚拟化层提供一套虚拟硬件,在这套虚拟硬件上运行自己的客户机内核和用户态环境。客户机中的应用向客户机内核发起系统调用,再由后续机制完成对底层资源的使用。这里说“自己的内核”,是指客户机拥有独立运行的内核实例;它仍然与其他客户机共享宿主机的物理 CPU、内存和设备容量。

常见 Linux 容器的应用则作为宿主机上的进程运行,借助内核提供的环境隔离和资源控制机制组织出相对独立的运行视图。容器镜像通常包含应用及所需用户态文件,不携带一套供该容器单独启动使用的 Linux 内核。容器里的程序发起系统调用时,请求到达的是承载它的那1 个 Linux 内核。某些桌面环境会先运行 Linux 虚拟机,再在其中启动容器,此时共享的是该虚拟机的内核。

虚拟机具有客户机内核而常见 Linux 容器共享承载内核
虚拟机具有客户机内核而常见 Linux 容器共享承载内核

因此,容器里看到某个发行版的标识,通常是在描述镜像中的用户态环境,不能据此认定内核也是由该镜像提供。1 个来自不同发行版的镜像能够运行,也不表示任意系统环境都可混搭。镜像仍须符合处理器架构,并满足应用对内核接口、运行库及设备能力的要求;只有用户态文件完整,不能补足不存在或被禁止的内核能力。

容器的边界也需要结合配置理解。给容器增加过多权限、挂入敏感宿主机目录或暴露设备,会扩大其可影响的范围。共享内核还意味着相关内核问题可能影响同一承载环境中的多个容器。虚拟机多了1 层客户机内核和虚拟硬件边界,也仍需要补丁、权限和资源规划。2 种方式适合解决不同的交付与隔离需求,不能仅凭名称决定安全等级。

用 uname 与 /proc 观察系统

以下命令用于 Linux 环境中的只读观察。它们不修改系统,也不要求更改权限。示例输出只说明形状,实际字段会随系统、工具版本和环境配置而变化。在其他操作系统上,命令可能不同,/proc 也可能不存在;不要为了复现本课而把这些观察方式当成所有系统必须具有的接口。

uname -s
uname -r
uname -m
ls /proc
cat /proc/version

uname -s 显示内核名称,常见 Linux 环境会输出 Linux;uname -r 显示内核发布版本字符串;uname -m 显示机器架构信息。三者回答的是不同问题。尤其在容器中,uname -r 通常反映承载容器的 Linux 内核,而不是镜像所选发行版的版本。完整记录这些字段有助于描述环境,但其中任何1 项都不能单独证明应用依赖已经满足。

/proc 通常是内核提供的伪文件系统入口。这里的许多内容是在访问时由内核生成的状态视图,不是磁盘上长期保存的1 批普通文档。执行 ls /proc,可能看到数字目录以及 cpuinfo、meminfo、version 等名字。数字目录通常对应当前可见的进程编号;进程会创建和退出,所以2 次查看结果不同是正常现象。

cat /proc/version 可以读取内核构建相关信息,但它不能替代发行版和软件包清单。看到某个进程目录,也不意味着一定有权读取其中每个条目。权限设置、挂载选项和容器的进程可见范围都会影响结果。容器中看不到宿主机全部进程,说明观察视图受到约束,并不意味着它拥有1 个独立内核。

内核信息与进程视图需结合命令及观察范围解释
内核信息与进程视图需结合命令及观察范围解释

还可以观察当前命令进程的描述符入口:ls /proc/self/fd。其中 self 指访问这个路径的进程,不能把它理解为固定指向某个终端窗口。常见程序会使用编号为零、一、二的标准输入、标准输出和标准错误,但运行环境可以重定向或关闭它们。查看命令自身也可能临时打开描述符,所以目录里的编号不是整台系统已经打开的文件总数。

这些命令适合作为证据链的起点。先记录所处环境,再判断输出描述内核、用户态文件还是进程视图,最后联系应用故障分析。读取系统状态时也要区分“当前快照”和“长期保证”:某个时刻资源充足,不等于高峰期间也充足;某条路径当前可见,不等于所有身份都能访问。观察范围本身就是解释结果的一部分。

常见误解与1 次故障定位

第1 类误解是把操作系统等同于桌面。图形界面能够让人操作计算机,但服务器完全可以通过命令行接受管理,内核仍然持续管理资源。第2 类误解是把文件抽象理解成普通磁盘文件:文件描述符可能代表网络连接,/proc 下的内容也常是状态接口。遇到“打开太多文件”的报错时,排查范围应包括连接等描述符使用者。

第3 类误解是把权限问题统一交给管理员身份。权限不足只是1 种可能原因。只读挂载、资源上限、路径不存在和接口不受支持,都可能导致请求失败,增加权限并不能逐一解决这些原因。即使增加权限暂时绕开某个限制,也需要说明被扩大的是哪项能力,以及程序原先为什么会请求它。

设想1 个服务在测试环境能够启动,放进容器后却无法保存报告。首先根据应用日志和接口返回信息确认失败发生在打开、写入还是其他步骤。如果目标路径不存在,应核对镜像与挂载布局;如果路径存在但不可写,应结合进程身份、目录权限和挂载是否只读来判断。此时直接替换内核或扩大容器权限,都缺少足够依据。

另1 个服务在低负载时正常,连接增加后出现描述符相关错误。此时应检查描述符是否及时释放、每进程限制和系统整体使用情况,并联系并发规模。增加内存并不必然改变描述符上限,提高上限也不修复资源泄漏。这里的泄漏是指资源已经不再需要,却因为程序没有正确释放而持续被占用;操作系统只能按照它掌握的状态管理资源。

这2 类故障说明,抽象让应用用统一接口表达需求,隔离和限制又让某些请求有条件地失败。能够解释失败条件,才算理解了操作系统的作用。定位时应把“应用请求了什么、内核依据什么规则处理、环境实际提供什么”连起来,再决定调整代码、权限、配置还是容量,避免把所有问题都归为镜像或机器性能。

放进方案里

接着按服务行为决定权限。普通请求处理与报告生成通常应使用专用的非管理员身份,只授予所需目录和资源的访问权。若某项功能提出额外权限需求,应先说明对应的设备、接口与理由,再决定是否授予,并记录影响范围。容器特权会显著扩大能力,不能作为通用兼容开关;涉及敏感设备或较强隔离要求时,还应评估虚拟机等部署边界。

随后规划写入位置。程序和固定配置可以放在只读根文件系统中,报告输出放到明确挂载的可写目录,临时文件与日志也各有去向。只读根能够限制对这一文件系统的修改,却不会自动保护全部挂载或阻止网络访问。验收时应覆盖临时文件创建、报告落盘及失败清理,确认应用不会隐含依赖某个未声明的可写路径。

最后把资源假设写入方案:预计并发、每个请求占用的连接与描述符、相关上限、余量及超限后的行为。上线前用接近目标负载的方式观察资源是否稳定回落,上线后持续记录错误与使用趋势。承载内核的维护、镜像依赖的更新和应用资源释放分别需要明确负责人。这样,镜像选型、最小权限、只读根和容量限制才能共同支撑服务运行,而不是成为互不相干的配置项。

运行方案同时明确镜像内容与权限写入和承载边界
运行方案同时明确镜像内容与权限写入和承载边界

小结:带着边界进入 S2

操作系统通过管理资源、提供抽象和建立隔离,把硬件能力组织成可使用的服务。内核承担核心管理与保护职责,用户程序通过系统调用请求相应服务;用户身份、执行模式和资源额度分别约束不同事情。文件描述符这样的抽象虽然简单,背后仍有有限容量和生命周期,需要应用正确使用并处理失败。

发行版把内核与用户态组件组织成可维护的系统;常见 Linux 容器携带应用及用户态依赖,共享承载环境的内核;虚拟机则运行自己的客户机内核。命令看到的名称和版本只有放回这些边界中才有意义。下一课 S2 将进1 步讨论进程与执行的组织方式,解释多个程序如何在有限处理器资源上取得进展。

下一课转向 S2 进程与线程:把本章验收过的结论带进后续主题,写成可检查的方案约束。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务