以操作系统的管理职能为切入点,从宏观层面上认识什么是进程

认识冯·诺依曼体系结构

为什么要认识冯·诺依曼体系结构?
答: 理解冯·诺依曼体系结构有助于我们在硬件层面上理解为什么在运行一个程序时,需要将该程序加载到内存中。另外,这种体系结构是现代计算机的基础架构,了解它可以顺便补充我们对于计算机组成原理的相关知识。

程序运行起来要被加载到内存中和我们了解进程有什么关系?
答: 计算机操作系统相关书籍中对于进程的定义是:进程是一个程序及其数据在处理机上顺序执行时发生的活动,是程序的一次动态运行过程。粗粒度地理解,运行中的程序就是进程。虽然目前尚不清楚程序到进程的演变过程,但显然理解程序需要被加载到内存中才能运行,有助于我们后续更全面地理解进程的概念。

冯.诺依曼体系结构是现代计算机的基础,截至目前,大多计算机仍遵守冯.诺依曼计算机的组织结构,在冯·诺依曼体系中,计算机的各硬件单元被分为五个主要部分:控制器、运算器、存储器、输入设备和输出设备。每个部分在计算机系统中都有特定的作用:

  • 控制器: 控制器负责从存储器中取出指令,解释这些指令,并根据指令协调计算机的各个部分进行操作。它是计算机的指挥中心,确保各部件按照程序的要求协同工作。

  • 运算器: 运算器是计算机进行算术运算和逻辑运算的核心部件。它执行所有的算术操作(如加减乘除)和逻辑操作(如与、或、非)。

  • 存储器: 存储器是用于存储数据和程序的部件。根据存储时间和功能的不同,存储器可以分为主存储器(如内存)和辅助存储器(如磁盘/固态硬盘)。

  • 输入设备: 输入设备用于向计算机输入数据和指令。它们是计算机与外部世界进行交互的途径。常见的输入设备有话筒、摄像头、键盘、鼠标、磁盘、网卡等。

  • 输出设备: 输出设备用于将计算机处理的结果输出给用户或其他系统。它们是计算机与外部世界交流的另一条途径。常见的输出设备有显示器、声卡、显卡、磁盘、网卡、打印机等。

注:1. 有的设备只做输入或者输出,有的设备既是输入设备又是输出设备;2. 五个主要部分中,控制器和运算器集成在一起被称为中央处理器,也就是我们常说的CPU。

以下是一个简单的冯·诺依曼结构示意图:
在这里插入图片描述

在冯·诺依曼结构体系结构中,各组成部分是独立的,这个独立是指每个组件在功能上是独立的,并且具有明确的职责和接口。这种设计理念带来了系统的模块化和可维护性,使得每个部分可以独立地开发、测试和替换,而不影响其他部分的正常运行。

举个例子,假如你不小心把笔记本的显示器摔坏了,那顺坏的也只是输出设备中的显示器硬件单元,只要更换显示器,、计算机依旧可以正常执行;另外,由于各个组件的功能独立性和模块化设计,不同的开发团队可以并行地开发和维护各个组件。这种并行性提高了开发效率,缩短了系统的开发周期。这也可以验证了为什么说计算机是各个不同的硬件厂商组合而成的一个品牌。

虽然功能上独立,但是物理上计算机的各硬件单元之间其实通过刻画在主板上的硬件电路连接起来的,连接只是一种手段,其目的是让数据在不同的模块之间进行流动。

数据流动的本质实际上是数据在硬件设备之间来回拷贝的一个过程,举个例子,当你在键盘上输入一个数字进行计算时,数据从键盘拷贝到内存,再从内存拷贝到运算器,运算器计算完成之后将结果拷贝回内存,最后结果通过显示器输出,这一切都是通过硬件电路来实现数据在各部件之间的拷贝。

因此,拷贝的整体效率是衡量计算机效率的一个重要指标。

计算机是一个辅助我们解决问题的工具,我们把数据输入给计算机,计算机完成计算工作,然后把结果交还给我们,这个过程中,好像只需要输入设备,CPU,输出设备三个大模块即可完成,显然这样的设计更加简单稳定,数据在设备间的拷贝次数少、数据拷贝整体效率高,但是为什么现在广泛使用的是冯·诺伊曼体系,而不是我们所想的这种体系结构?

理解这个问题需要了解三点:

  1. 第一点:计算机中的存储金字塔。

存储金字塔是计算机存储系统的一个层次结构,由多个存储单元组成,各层存储器在速度、容量和成本上存在显著差异。具体来说,存储金字塔包括以下层次:

  • CPU寄存器:速度最快,但容量最小,用于临时存储CPU正在处理的数据。
  • CPU高速缓存(Cache):速度快于内存但慢于寄存器,用于存储频繁访问的数据,减少内存访问延迟。
  • 内存(RAM):速度适中,容量较大,存储正在运行的程序和数据。
  • 硬盘(HDD/SSD):速度最慢,容量最大,长期存储数据和程序。

这种层次结构是为了在性能和成本之间找到最佳平衡点。顶层的存储器速度极快,确保CPU能够快速访问数据,而底层的存储器容量大,能够存储大量数据。通过这种设计,计算机能够在满足性能需求的同时控制成本。
在这里插入图片描述

  1. 第二点:CPU与外设的处理速度差距过大。

在计算机中除CPU和内存以外的设备统称为外部设备(外设),CPU与外设之间的处理速度差距过大,以设备响应时间为衡量指标的话,得到如下结果:

  • CPU速度:响应时间在纳秒级,执行速度极快。
  • 内存速度:响应时间在微秒级,比CPU慢,但比外设快。
  • 外设速度:响应时间在毫秒级,相对CPU非常慢。

如果让CPU直接与外设交互,由于外设响应时间过长,会导致CPU大部分时间处于等待状态,极大地降低了计算机的整体效率。这就是所谓的木桶效应,系统的性能将被最慢的部分拖累。通过在CPU和外设之间引入内存,CPU可以快速从内存中读取数据,避免长时间等待外设的响应,显著提升整体效率。

  1. 第三点:预加载缓存技术。

即使引入了内存,外设速度慢的短板仍然存在,而内存的引入也增加了数据拷贝的次数。然而,通过预加载缓存技术,可以显著提高系统的整体效率:

  • 预加载缓存:内存可以提前从外设加载预计用到的数据到内存中(预加载)。这样,CPU在需要数据时可以直接从内存中读取,而不是等待外设的响应。
  • 缓存机制:合理设计的缓存机制可以确保CPU主要与内存进行数据交换,而内存则与外设进行数据交换。这样,CPU和外设之间实现了解耦。

通过预加载缓存技术,CPU的效率得到极大提升,因为它大部分时间都在处理内存中的数据,而不是等待外设的响应。外设的数据则可以在后台慢速加载到内存,确保数据在CPU需要时已经准备好。

这里回到一开始的问题,为什么一个程序运行起来首先要被加载到内存中,以.exe为后缀的可执行程序本质上就是一个存储于磁盘上的文件,而磁盘属于外设,在冯·诺伊曼体系中CPU无法访问外设,所以得先将这个程序加载到内存,这就是答案。

了解计算机系统的层状结构

为什么要了解计算机系统的层状结构?
答:主要是为了对计算机系统的整体架构有一个初步的认知,这里写的所有内容都是为了解进程而做的知识铺垫,进程管理是Linux操作系统的一部分,一款操作系统在计算机系统中处于一个什么样的位置,这是我们需要了解的。

计算机系统按功能和作用划分为多个层次,从用户交互的顶层到底层的硬件部分。每个层次都有其特定的功能和职责。下面对各层次的划分和详细介绍如下:

  1. 用户部分
    用户部分是计算机系统的最上层,直接与用户交互。它包括三个主要操作:

    • 指令操作:用户通过命令行界面(CLI)或图形用户界面(GUI)与计算机进行交互。例如,通过输入命令或点击图标来执行文件操作和启动应用程序。
    • 开发操作:开发人员使用各种开发工具(如编辑器、编译器和调试器)编写和测试程序。开发者通过调用库(libraries)和编程接口(APIs)来简化开发过程。
    • 管理操作:系统管理员执行系统配置和管理任务,如安装软件、配置网络和管理用户权限。这些操作确保系统资源的优化配置和安全运行。
  2. 用户操作接口
    用户操作接口是用户部分和操作系统部分之间的桥梁,提供用户与操作系统之间的交互方式:

    • shell外壳:shell是用户与操作系统之间的桥梁,通常以命令行形式出现,如Bash和Zsh。用户通过输入命令与操作系统进行交互,shell解析并执行这些命令。
    • lib(库):库文件是预先编写好的代码集合,开发者可以调用这些库来实现常见功能,而无需从头编写代码。例如,标准C库(libc)提供了文件操作和字符串处理等基本功能。
    • 部分指令:用户执行的特定系统指令,通过这些指令直接与操作系统进行交互,如ls列出文件和cd切换目录。
  3. 系统调用接口
    系统调用接口是用户操作接口和操作系统之间的桥梁。它提供了一组函数,使用户程序可以请求操作系统服务:

    • 系统调用接口:提供了访问操作系统核心功能的方法。通过系统调用,用户程序能够执行文件操作(如open、read、write)、进程管理(如fork、exec)和内存管理(如malloc、free)等任务。
  4. 操作系统
    操作系统是计算机系统的核心,负责管理硬件和软件资源。它主要包括以下子系统:

    • 内存管理:负责内存的分配和回收,确保各个进程之间的内存隔离和有效利用。内存管理包括内存分页、虚拟内存等。
    • 进程管理:负责创建、调度和终止进程,管理进程的执行状态,包括进程的上下文切换、多任务调度和进程间通信(IPC)。
    • 文件管理:负责管理文件系统,提供文件的创建、删除、读取和写入等操作,同时维护文件目录结构和管理文件权限。
    • 驱动管理:负责加载和管理设备驱动程序,使操作系统能够与硬件设备通信,如硬盘驱动程序和网卡驱动程序。
  5. 驱动程序
    驱动程序是操作系统与硬件之间的桥梁,负责将硬件设备的操作抽象为操作系统可以理解的形式:

    • 硬盘驱动程序:提供硬盘与操作系统之间的通信接口,处理数据的读写操作,如SATA和NVMe驱动。
    • 其他驱动程序:负责其他硬件设备的管理,如网卡驱动程序、显卡驱动程序和打印机驱动程序,确保操作系统能够识别并使用这些设备。
  6. 底层硬件
    底层硬件是计算机系统的物理部分,它们一般以某种体系结构组织起来,比如冯·诺依曼体系结构:

    • 网卡:负责网络通信,处理数据的发送和接收,如以太网卡和无线网卡。
      硬盘:用于数据存储,保存操作系统、应用程序和用户数据,包括机械硬盘(HDD)和固态硬盘(SSD)。
    • 其他硬件设备:包括键盘、鼠标、显示器和打印机等外围设备,提供用户输入和输出的接口。

在这里插入图片描述

认识操作系统

操作系统是一款进行软硬件资源管理的软件,这部分内容更多地是在于了解操作系统如何进行管理,理由在于:进程属于操作系统的一部分,操作系统管理进程的方式决定了进程在操作系统的表现形式。

概念

任何计算机系统都包含一个基本的程序集合,这些程序统称为操作系统(OS)。广义上,操作系统包括以下两个主要部分:

  1. 内核(Kernel):

    • 进程管理:创建、调度和终止进程,确保CPU的高效利用。
    • 内存管理:分配和回收内存,确保进程间内存隔离,利用虚拟内存机制扩展内存。
    • 文件管理:管理文件和目录的创建、删除、读取和写入,维护文件系统的完整性和安全性。
    • 驱动管理:通过设备驱动程序控制硬件设备,提供统一的设备接口。
  2. 其他程序:

    • 函数库:提供常用功能和系统调用的封装,供应用程序调用。
    • Shell程序:提供用户与操作系统交互的接口,通常以命令行形式出现。

另外,狭义上的操作系统指的就是内核(Kernel)本身。

设计OS的目的

操作系统的设计目的主要有两个:

  1. 与硬件交互,管理所有的软硬件资源:操作系统作为硬件和软件之间的桥梁,负责对硬件资源的抽象和管理,使硬件资源能够被高效和安全地使用。
  2. 为用户程序(应用程序)提供一个良好的执行环境:操作系统为应用程序提供一个一致和稳定的运行环境,简化应用程序开发,确保程序能够高效运行并与其他程序协同工作。

OS的定位

在整个计算机软硬件架构中,操作系统的定位是一款纯正的“搞管理”的软件。它负责对计算机的所有资源进行管理和调度,确保资源的高效利用和系统的稳定运行。

如何理解OS的 "管理"

想理解OS如何 "管理",就得明白什么是管理。

管理的本质就是在做决策,这个谓词后面涉及到两个对象,分别是管理者和被管理者。

管理者做决策,被管理者做执行。

以大学校园背景为例看待管理的话我们发现:校领导就是管理者,辅导员是辅助管理决策落地的中间层,学生就是被管理者。

  1. 校领导不能凭空做决策,做决策的依据是学生数据,只要拿到了学生的数据,校领导甚至都不需要和学生见面就可以把学生安排得明明白白,从这个角度来看,管理的本质不在于对人的管理,而是对人的信息,即数据的管理。

  2. 既然管理者和被管理者不需要见面,那么数据的收集和决策的下达与落实就交给中间层来处理,也就是三个角色中的辅导员。

  3. 一所大学中,学生的数量是远超于校领导的数量的,学生越多,产生的数据也就越多,但不是所有的数据都是有效的,比如说一个学生一天的吃喝拉撒对校领导来说毫无作用,领导们要的都是诸如姓名、学号、绩点、挂科记录、宿舍号等关键信息,然后采集每个学生这样的关键信息以结构化的形式组织起来,说人话就是建excl表格。

从这个例子中我们可以将管理者做管理的方法归结为六个字——先描述,再组织。

  • 描述数据是管理的第一步,在大学校园管理这个例子中,学生的姓名、学号、绩点、挂科记录、宿舍号等都是对学生个人情况的具体描述,一份数据可以反映学生的学术和生活状态。

  • 组织数据是管理第二步,学生越多数据量越大,但是数据的类别都是相同的,建立excl表格就是对数据的组织。

最后再回到我们的主题,操作系统是一款搞管理的软件,潜台词就是操作系统是管理者,假设现在操作系统要管理计算机的底层硬件,它该怎么实现?

操作系统是软件,软件是用编程语言写的,一般写操作系统用的都是C,这里就以C为例:

  1. 用一个通用的struct结构体描述硬件设备,并通过设备类型和具体属性进行区分:
Show Code
#include <stdio.h>
#include <stdlib.h>

// 通用硬件设备类型枚举
enum DeviceType {
    DEVICE_TYPE_HARD_DRIVE,
    DEVICE_TYPE_MEMORY,
    DEVICE_TYPE_NETWORK_CARD,
    // 其他硬件设备类型...
};

// 通用硬件设备描述结构体
struct HardwareDevice {
    int id;                     // 设备ID
    enum DeviceType type;       // 设备类型
    void *baseAddress;          // 基地址(通用)
    size_t size;                // 大小(通用)
    int isOnline;               // 是否在线
    void *specificInfo;         // 特定设备信息的指针
    struct HardwareDevice *next; // 指向下一个设备
};
  1. 有了描述硬件的结构体之后,我们可以根据硬件的具体信息填充结构体字段,为不同的硬件创建结构体对象,将这些结构体以链表或者其他更加高效的数据结构管理起来。

  2. 正如校领导对学生的管理转变成了对excl表格的增删查改,先描述后组织使得操作系统对于硬件的管理变成了对数据结构的增删查改,操作系统只需要修改结构体字段即可,具体的执行可以由硬件厂商提供的驱动程序来完成,所以驱动程序的作用就在于行下屏蔽硬件的操作方法的差异,以统一的接口提高给操作系统完成硬件操作方法的调用。

最后基于上面的理解,虽然博客里关于什么是进程一个字也没有说,但是我们不难想象,操作系统里有很多进程,操作系统要管理进程,而管理的方式就是先描述再组织,操作系统源码里必然有描述进程的结构体以及修改进程结构体对象的函数方法。总结一下就是,这部分内容的核心让我们能够从管理的角度切入来理解什么是进程。

系统调用和库函数的理解

系统调用

理解完操作系统通过“先描述,再组织”的方法实现其管理职能之后,我们对于计算机层状结构模型中的下三层(操作系统、驱动、硬件,个人认为这里的“操作系统”更多指的是内核,不过图是这么画的,这里就这么写算了)有了一个大概的了解,但是除此之外的三层就一眼懵,尤其是系统调用接口和用户操作接口(用户操作接口又称库函数),这部分内容的核心在于基于管理的基础上来认识系统调用和库函数的概念及其作用,了解的理由是Linux的进程管理当中用到了大量的系统调用接口。

计算机是为人类服务的工具,操作系统的管理功能也是为了服务用户。我们为什么需要操作系统的管理呢?因为人为管理计算机的效率太低,已经被淘汰了(第一台通用电子计算机“埃尼阿克”(ENIAC)就是通过开关控制的计算机)。试想一下,如果我们打个游戏还得不停地控制开关来操作CPU、显卡、网卡、声卡、键盘、显示器等硬件设备,那该有多麻烦。

我们不仅需要操作系统来管理计算机软硬件资源,而且需要好的管理。如果一台计算机三天一蓝屏、五天一重启,没有人会愿意使用。所以,操作系统的管理功能是一种手段,其真正目的是为用户提供稳定、安全、高效的使用环境。

操作系统如何提供安全的管理,实现的方式就是我们这部分内容的核心——系统调用

操作系统管理的是软硬件资源,但更本质地来讲,操作系统管理的是软硬件先描述再组织之后得到的数据,安全的管理指的是要保证操作系统内组织的数据不会被恶意篡改、删除,这么说不好理解,用C++来举个例子:

假设我们有一个类OperatingSystem,它代表操作系统。然后类内部有一些关键数据结构,如内存表、进程表、文件系统等。假设这些数据结构是公开的,任何人都可以直接访问和修改它们:

数据公开的OperatingSystem类代码
class OperatingSystem {
public:
    // 公开的内存表
    std::map<int, std::string> memoryTable;
    // 公开的进程表
    std::map<int, std::string> processTable;

    // 公开的文件系统
    std::map<std::string, std::string> fileSystem;
};

int main()
{
  OperatingSystem os;
  os.memoryTable[0] = "New Process";
  // 假设某个恶意程序修改了进程表
  os.processTable[0] = "Malicious Process";
  // 假设某个恶意程序删除了文件系统的关键数据
  os.fileSystem["/"] = "";
  return 0;
}

上述代码可能会导致以下问题:

  • 内存泄漏或非法内存访问:如果某个进程随意修改内存表,可能导致其他进程的数据被覆盖或内存访问冲突。
  • 进程管理混乱:恶意程序修改进程表,可能导致系统无法正确调度和管理进程,甚至导致系统崩溃。
  • 数据丢失或文件系统损坏:随意删除或修改文件系统的关键数据,可能导致文件丢失或文件系统不可用。

在数据公开的情况下,是很难做到有效的保护措施,比如说一个人到银行取钱,银行工作人员告诉ta账户数据已更新,让ta自己到现金库取钱,谁能保证最后取出来的钱是不是多到装不下了,银行拦不住也不能拦,毕竟现金库公开的是银行自己的规定,所以OperatingSystem类的内部数据就是公开的现金库,谁来了都可以踩一脚,虽然关闭现金库是最安全的解决方法,但是银行又必须向大众提供存取服务,所以最好的最好的办法是ta将要求告知银行,银行工作人员确认要求合法后由银行工作人员完成业务要求,体现到OperatingSystem类中就是,编写一些公开的成员函数,来为用户完成查看、修改类内数据的请求:

数据私有的OperatingSystem类代码
class OperatingSystem {
private:
    std::map<int, std::string> memoryTable;
    std::map<int, std::string> processTable;
    std::map<std::string, std::string> fileSystem;

public:
    // 提供给用户的系统调用接口

    // 只读接口:获取内存信息
    std::string getMemoryInfo(int pid) {
        if (memoryTable.find(pid) != memoryTable.end()) {
            return memoryTable[pid];
        }
        return "No such process";
    }

    // 只读接口:获取进程信息
    std::string getProcessInfo(int pid) {
        if (processTable.find(pid) != processTable.end()) {
            return processTable[pid];
        }
        return "No such process";
    }

    // 只读接口:获取文件内容
    std::string readFile(const std::string &path) {
        if (fileSystem.find(path) != fileSystem.end()) {
            return fileSystem[path];
        }
        return "File not found";
    }

    // 可修改接口:分配内存
    void allocateMemory(int pid, const std::string &data) {
        // 检查权限和参数
        if (/* 检查合法性 */) {
            memoryTable[pid] = data;
        }
    }

    // 可修改接口:创建进程
    void createProcess(int pid, const std::string &name) {
        // 检查权限和参数
        if (/* 检查合法性 */) {
            processTable[pid] = name;
        }
    }

    // 可修改接口:写文件
    void writeFile(const std::string &path, const std::string &content) {
        // 检查权限和参数
        if (/* 检查合法性 */) {
            fileSystem[path] = content;
        }
    }
};

所以,系统调用的本质其实很简单,就是“函数”。只不过相比我们写的普通函数,系统调用是已经在操作系统源码中定义好的。而一旦我们用户想访问那些被操作系统管理的资源,我们能且只能通过操作系统提供的系统调用来访问。

库函数与可移植性

在理解了系统调用后,我们顺便探讨库函数的作用和意义,库函数的主要有以下两个:

  1. 封装系统调用,简化使用

系统调用提供了访问操作系统核心功能的接口,但直接使用系统调用通常需要用户对操作系统的内部工作机制有较深入的了解。例如,一个简单的文件读写操作,通过系统调用来实现可能需要处理文件描述符、缓冲区管理、错误处理等复杂细节。这对普通用户来说,显得过于繁琐和困难。

库函数通过封装系统调用,简化了这些操作,使用户能够更方便地使用系统资源。例如,在C标准库中,fopenfreadfwrite等函数都是对文件操作相关系统调用的封装。用户只需要调用这些库函数,就能实现文件的打开、读取和写入,而不需要关心底层的系统调用细节。

另外,在C标准库中,printf的作用是将特定格式的字符串输入到显示器上,显示器是硬件,硬件是操作系统管理的资源之一,库函数作为用户层想要访问操作系统资源就得通过操作系统提供的系统调用,所以不是printf能够向显示器输出信息,而是因为printf封装了相关系统调用才能做到向显示器输出,这其中少不了操作系统的帮助。

  1. 提高代码的可移植性

库函数不仅简化了系统调用的使用,还提高了代码的可移植性。不同的操作系统具有不同的系统调用接口,同一功能的系统调用接口在不同的操作系统下名字也不一定会相同,如果直接使用系统调用,代码在不同操作系统之间移植将变得非常困难。库函数通过提供一致的接口,屏蔽了底层操作系统的差异,使得同一份代码能够在不同的操作系统上运行。

例如,C标准库(libc)和C++标准库(libstdc++)定义了一组跨平台的接口,用户可以编写一次代码,然后在Linux、Windows、macOS等不同操作系统上运行。这样,用户只需编写与库函数相关的代码,而不必关心操作系统的具体实现,从而大大提高了代码的可移植性。

宏观层面认识进程

对于操作系统管理职能的认知为我们提供了认识进程的切入点,这里就从“管理”入手认识什么是进程,至于标题中的“宏观”是指这部分内容中与进程相关的理论不针对某个具体的操作系统,而是适用于所有操作系统。

操作系统教材里提到“正在执行的程序是进程”,这里我们以这句话为起点,剖析一下从程序到进程的演变过程,以便更好地理解进程的概念。

首先,这里的“程序”准确地说是指源代码文件经过编译链接之后形成的二进制可执行文件,又称可执行程序,该文件包含CPU可以直接执行的二进制指令以及执行指令所需的数据。而我们常说的“运行一个程序”,就是让CPU执行该可执行程序中的二进制指令。但是,可执行程序是一个文件,文件存储在硬盘上,而硬盘又属于外设,根据冯·诺伊曼体系结构,CPU只与内存进行直接交互,无法直接执行位于硬盘上的二进制指令。因此,运行一个可执行程序之前,操作系统需要将这个可执行程序拷贝到内存中,这个过程被称为“加载”。

其次,我们的计算机可以一次运行多个程序,比如Steam、Edge、微信、记事本、画图、QQ音乐等,这说明计算机内存中存在大量从硬盘拷贝过来的可执行程序。作为计算机软硬件资源的管理者,操作系统也要管理这些被加载的可执行程序。为了保证这些可执行程序之后被CPU正常执行,这些可执行程序分别被拷贝到内存的哪个位置上、一个可执行程序占用了多少内存空间、一个可执行程序有多少条指令、当前执行到哪一条指令、下一条要执行的指令是什么、当前可执行程序执行了多久、什么时候切换到下一个可执行程序……这些问题都是操作系统为了管理可执行程序该考虑的。

然后,操作系统该如何管理被加载到内存中的可执行程序?答案就是我们先前提到的先描述,再组织。

  • 先描述:操作系统中必然存在一个用于描述被加载可执行程序的基本情况和活动过程的类或结构体,宏观层面上,我们把类或结构体被叫做“进程控制块”(Process Control Block,简称PCB),当一个可执行程序被加载到内存的同时,操作系统会申请内存空间、实例化PCB对象,然后用这个被加载可执行程序的信息填充PCB对象字段。

  • 再组织:操作系统就得通过链表或者某种更高效的数据结构将这些PCB对象管理起来,假定这里采用的是链表。

最终,操作系统对于被加载到可执行程序中的管理就变成了对PCB对象的管理。

但是,在上面这个分析过程中,个人从未用“进程”这个这个字眼来程序可执行程序或者PCB对象,那么什么才是进程?

单独的可执行程序不是进程,单独的PCB对象也不是进程。被加载到内存的可执行程序以及运行该可执行程序的相关数据结构所构成的集合才是真正的进程。因此,对于进程的理解,我们可以构建出这样的一条等式:进程 = 内核数据结构 + 可执行程序

另外这里在对“内核数据结构”这一表述的一些补充:

  1. 上面的操作系统指的是狭义上的操作系统,即内核(Kernel)。
  2. 在学习数据结构时,我们往往认为只有链表、数组等才算是“真正的”数据结构。这是因为在传统的数据结构课程中,这些经典的数据结构被反复讲解和应用,导致我们先入为主地忽略了其他形式的数据结构。然而,数据结构的定义其实非常广泛,任何用于组织和管理数据的形式都可以被称为数据结构。因此,类和结构体也是数据结构的一种形式。
  3. 之所以不使用“PCB对象”而是“内核数据结构”,是因为我们将来接触到的用于进程管理的数据结构对象不止PCB对象一个,还有进程地址空间、页表、文件描述符表等。

总结

  1. 首先冯·诺伊曼结构体系从计算机整体效率方面的考量回答了为什么程序运行首先要加载到内存的问题。
  2. 操作系统在计算机层次结构中起到呈上启下的作用,向下管理软硬件资源,向上提供良好的运行环境。
  3. 进程管理属于操作系统内容的一部分,将来操作系统对于进程的管理也将遵循“先描述,在组织”的管理理念。
  4. 操作系统为了保证自身管理的安全稳定,私有内部数据公开一批称之为“系统调用接口”的函数来向用户提供访问软硬件资源的服务。
  5. 前四点都是为了认识计算机、了解操作系统而进行的知识铺垫,最终对于进程,我们能够建立起“进程 = 内核数据结构 + 可执行程序”这样的认知。
posted @ 2024-06-25 21:36  smonkey1257  阅读(33)  评论(0)    收藏  举报