操作系统
操作系统(Operating System,简称OS)是一种系统软件,是计算机系统中的核心软件之一。它负责管理和协调计算机硬件和软件资源,为用户和应用程序提供服务,以实现计算机系统的有效运行。操作系统扮演着资源管理者、任务调度器、用户接口提供者等多重角色。
常见的操作系统包括:
- Windows: 由微软公司开发的操作系统,广泛应用于个人计算机。
- Linux: 一种开源的Unix-like操作系统内核,可以被用于各种设备和用途。
- macOS: 苹果公司的操作系统,运行于Macintosh计算机上。
- Unix: 一类多用户、多任务操作系统的家族,包括许多变种,如AIX、Solaris等。
- Android: 面向移动设备的操作系统,基于Linux内核。
基础概念
- 进程: 一个执行中的程序实例, 拥有独立的内存空间和资源
- 线程: 一个进程内的执行单元, 共享进程的内存空间和资源
- 进程和线程对比
- 创建、切换和终止的开销: 进程 > 线程
- 内存和资源: 线程共享进程的内存和资源, 进程之间独立内存和资源
- 通信: 进程间通信 IPC, 线程共用地址空间通过变量或数据结构通信
- 独立: 线程之间会影响, 比如某一线程崩溃可能会引起另一线程也崩溃, 进程不会
- 数据资源保护: 进程资源隔离, 无需同步机制保护. 线程通过信号量、锁等确保对共享数据的正确访问
- 吞吐量
- 定义: 操作系统在单位时间内能够创建、管理和终止的进程或线程的数量
- 栈
- 生长方向: 从内存高地址到低地址.
- 设计原因
- 栈和堆生长方向相反会导致他们相遇, 确保了最大的内存利用率, 固定栈的大小以及堆的动态分配添加新内存块
- 栈内存占用通常较小, 放在高地址向下生长提升内存利用率
- 设计原因
- 内存管理: 自动管理, 函数返回自动释放内存
- 内存分配: 预先分配, 有内存限制. 由单个连续内存块组成(临时内存)
- 用途: 局部变量, 函数参数、调用、返回相关信息
- 访问速度: 快, 数据存储是连续的,并且存取操作是预测性的
- 生长方向: 从内存高地址到低地址.
- 堆
- 生长方向: 从内存低地址到高地址
- 内存管理: 手动管理如垃圾回收、内存碎片处理, 释放内存
- 内存分配: 程序可用的物理内存和虚拟内存大小. 由多个不连续的内存块组成
- 用途: 动态分配内存
- 访问速度: 慢, 需要寻找一个合适的内存块
系统启动
- 引导: 计算机启动时, 将操作系统从磁盘传输到内存上
- 引导加载器(BootLoader):存储在启动设备上的这个程序负责加载操作系统内核
- 启动过程: 固件 -> 引导程序 -> 内核
- 电源启动. 初始化关键硬件, CPU、RAM、显卡等.
- cpu 启动. cpu 从预设内存地址开始执行, 这通常是一个指向主板上非易失性存储器(如 Flash ROM)中的固件(如 BIOS)的地址
- 引导程序. 固件初始化硬件, 搜索启动设备(硬盘, usb), 固件从所选的启动设备的启动扇区读取并加载引导程序到 RAM, 将控制权转让给引导程序
- 加载内核. 引导程序从存储设备找到操作系统内核并将其加载到 RAM 中
- 启动内核. 内核加载到 RAM 后, 控制权转让给内核
系统架构(自顶向下)
操作系统的架构可以从上到下(自顶向下)分为多个层次,每个层次负责不同的功能。以下是典型的操作系统架构,自顶向下的层次结构:
- 用户界面层(User Interface Layer):
- 用户接口: 提供用户与操作系统交互的界面,可以是图形用户界面(GUI)或命令行界面(CLI)。
- 应用程序层(Application Layer):
- 应用程序: 包括用户编写的各种应用程序,如文本编辑器、浏览器、游戏等。
- 应用程序编程接口(API)层(API Layer):
- API: 提供给应用程序的接口,允许应用程序与操作系统进行交互。常见的API包括系统调用、库函数等。
- 系统调用接口层(System Call Interface Layer):
- 系统调用: 提供给应用程序的接口,允许应用程序请求操作系统提供的服务,如文件操作、进程管理等。
- 用户支持层(User Support Layer):
- 用户支持服务: 包括各种支持服务,如用户身份验证、文件系统访问控制等。
- 进程管理层(Process Management Layer):
- 进程控制: 管理系统中的进程,包括进程的创建、调度、同步和通信。
- 存储管理层(Memory Management Layer):
- 内存管理: 负责系统内存的分配和释放,以及虚拟内存的管理。
- 文件系统层(File System Layer):
- 文件系统: 提供对文件和目录的管理,包括文件的创建、读取、写入和删除等操作。
- 设备驱动程序层(Device Driver Layer):
- 设备驱动程序: 提供与硬件设备的通信接口,使得操作系统可以控制硬件设备。
- 内核层(Kernel Layer):
- 内核: 包含操作系统的核心部分,管理系统资源、执行系统调用,并处理中断和异常。
- 硬件抽象层(Hardware Abstraction Layer):
- 硬件抽象: 提供对底层硬件的抽象,使得操作系统能够在不同硬件平台上运行。
整个操作系统的架构是一个分层的结构,每一层都建立在下一层的基础上,并为上一层提供服务。这种层次结构有助于模块化设计、可维护性和可移植性。每个层次的模块都有明确定义的接口,使得不同层次的开发人员能够独立地工作。
核心概念
以下是操作系统的一些核心概念:
- 进程(Process): 进程是程序的一次执行,是操作系统中的基本执行单元。它包括代码、数据和执行环境。操作系统通过进程管理来调度和控制程序的执行。
- 线程(Thread): 线程是进程中的一个独立执行路径,共享进程的资源。多线程使得程序可以并发执行,提高系统的性能和响应能力。
- 调度(Scheduling): 调度是操作系统中的一个重要功能,负责决定哪个进程或线程在何时执行。调度算法的选择影响系统的性能和响应时间。
- 内存管理(Memory Management): 内存管理涉及分配和释放内存空间,以及虚拟内存的管理。它确保每个程序都能获得足够的内存空间,并防止程序之间相互干扰。
- 文件系统(File System): 文件系统负责管理计算机存储设备上的文件和目录。它提供了对文件的读写、创建、删除等操作,以及对文件和目录的组织结构。
- 设备驱动程序(Device Drivers): 设备驱动程序是连接操作系统和硬件设备的软件模块。它们负责将操作系统的抽象接口转换成硬件能理解的命令。
- 输入/输出(I/O)管理: I/O管理涉及处理计算机与外部设备之间的数据传输。它包括缓冲、中断处理、设备控制等方面的功能。
- 系统调用(System Call): 系统调用是应用程序与操作系统之间的接口,允许应用程序请求操作系统提供的服务。常见的系统调用包括文件操作、进程控制等。
- 中断(Interrupt): 中断是硬件或软件引起的事件,它打断当前正在执行的程序。操作系统通过中断处理程序响应和处理中断,保证系统的正常运行。
- 虚拟化(Virtualization): 虚拟化技术允许在一个物理计算机上运行多个虚拟机,每个虚拟机具有自己的操作系统和应用程序,从而提高硬件资源的利用率。
- 安全性与权限控制: 操作系统负责确保系统的安全性,包括用户身份验证、访问控制、数据加密等。
- 分布式系统: 分布式系统是由多台计算机组成的系统,它们通过网络连接并协同工作。操作系统在分布式系统中负责资源管理和通信。
时间分片
时间分片是操作系统中一种用于实现多任务调度的机制。在多任务环境中,多个任务(进程或线程)共享CPU的执行时间。时间分片机制通过将CPU时间划分为小的时间片段,每个任务在一个时间片段内执行,然后切换到下一个任务,从而实现多任务的轮转执行。
以下是时间分片的详细概念:
- 基本原理: 时间分片的基本原理是将总体可用的CPU时间划分为固定长度的时间片,每个时间片分配给一个任务。当一个任务的时间片用完时,操作系统会暂停该任务的执行,保存其状态,并切换到下一个任务。这个过程不断重复,形成了任务的轮转执行。
- 调度器: 操作系统中的调度器负责决定当前应该运行哪个任务。它按照时间分片的方式进行调度,确保每个任务都有机会执行。调度器的工作是在任务之间进行切换,维护任务的运行状态信息。
- 时间片长度: 时间片的长度是一个重要的调优参数。较短的时间片可以使任务更频繁地切换,增加系统响应速度,但可能引入一些上下文切换的开销。较长的时间片减少了上下文切换的频率,但可能导致任务响应较慢。
- 公平性: 时间分片的机制有助于保证多个任务的公平性。每个任务都有机会占用CPU执行,而不会长时间地独占CPU资源。这有助于防止某个任务长时间运行而导致其他任务无法得到执行的情况。
- 实时系统: 在实时系统中,时间分片的长度可能是固定的,以确保系统对于特定的实时任务能够满足响应时间的要求。实时系统通常需要更精确的任务调度。
- 优先级调度: 时间分片可以与优先级调度结合使用。不同任务可能具有不同的优先级,高优先级的任务可以在时间片结束前抢占低优先级任务的执行。
- 抢占式和非抢占式: 在抢占式的时间分片调度中,操作系统可以在一个任务的执行中抢占CPU,切换到其他任务。而在非抢占式的时间分片调度中,任务只有在自愿放弃CPU时才会被切换。
调度器
CPU调度器是操作系统中的一个重要组件,负责管理和调度系统中的进程或线程,以便有效地利用CPU资源。其主要目标是提高系统的性能、响应时间和公平性。以下是有关CPU调度器的详细讨论:
- 基本概念:
- 进程和线程: CPU调度器管理的主体是进程或线程。进程是程序的一次执行,而线程是进程中的一个独立执行路径。
- 上下文切换: 当调度器切换到一个新的进程或线程时,需要保存当前执行任务的上下文信息,并加载下一个任务的上下文,这个过程称为上下文切换。
- 调度算法:
- 先来先服务(FCFS): 按照任务到达的顺序进行调度。简单但可能导致短任务等待长任务。
- 最短作业优先(SJF): 选择执行时间最短的任务,以最小化平均等待时间。
- 轮转调度(Round Robin): 每个任务被分配一个时间片,按照轮流的方式执行。适用于时间分片调度。
- 多级反馈队列调度: 将任务分配到多个队列,根据任务的优先级和历史行为进行调度。
- 优先级和抢占:
- 优先级调度: 给任务分配优先级,高优先级的任务优先执行。
- 抢占式调度: 允许调度器在一个任务执行时抢占CPU,切换到更高优先级的任务。
- 多核和多线程:
- 多核调度: 在多核系统中,调度器需要考虑如何分配任务到不同核心,以充分利用硬件资源。
- 多线程调度: 对于支持多线程的系统,调度器需要考虑线程之间的调度和协作。
- 实时调度:
- 实时调度算法: 适用于实时系统,确保任务在规定的时间内完成,以满足实时性要求。
- 负载平衡:
- 负载平衡: 在多核系统中,调度器需要考虑如何平衡各个核心的负载,以充分利用系统资源。
- 调度器的实现:
- 内核空间调度器: 在内核空间实现的调度器,负责操作系统层面的任务调度。
- 用户空间调度器: 有些系统允许应用程序或用户态库自行实现调度,通过系统调用与内核进行交互。
- 性能指标:
- 吞吐量: 每单位时间内完成的任务数量。
- 响应时间: 从任务提交到任务开始执行的时间。
- 公平性: 不同任务之间获得CPU时间的公平程度。
- 调度器的挑战:
- 上下文切换开销: 频繁的上下文切换可能导致性能下降。
- 负载不平衡: 不同核心的负载不均衡可能导致资源浪费。
- 动态调整:
- 动态优先级调整: 根据任务的运行情况和系统状态,动态调整任务的优先级。
进程管理
进程管理是操作系统中的一个核心功能,负责创建、调度、终止和协调进程的执行。
以下是进程管理相关概念:
- 进程的创建:
- 进程控制块(PCB)详解: PCB 包含了进程的各种信息,如进程状态、程序计数器、寄存器状态、内存指针、文件描述符等。
- 进程地址空间: 进程在内存中的地址空间包括代码段、数据段、堆和栈等,每个进程有独立的地址空间,互不干扰。
- fork() 系统调用: 在 Unix/Linux 系统中,fork() 用于创建一个新的进程,新进程是原进程的副本。新进程称为子进程,原进程称为父进程。
- exec() 系统调用: exec() 用于加载一个新的程序文件,替代当前进程的内容。这样可以在一个进程中执行不同的程序。
- 进程调度:
- 调度算法详解: 先来先服务(FCFS)、最短作业优先(SJF)、轮转调度(Round Robin)、多级反馈队列调度等不同算法的优缺点及适用场景。
- 多核系统调度: 在多核系统中,需要考虑如何合理分配任务到不同的核心,以充分利用硬件资源。
- 优先级调度: 进程可以被分配不同的优先级,高优先级的进程在调度时获得更多的 CPU 时间。
- 实时调度: 对于实时系统,需要确保任务在规定的时间内完成。
- 进程同步和通信:
- 互斥量详解: 用于保护共享资源,确保在同一时刻只有一个进程可以访问。
- 信号量详解: 用于控制对共享资源的访问,可以实现进程间的同步。
- 死锁和避免: 死锁是进程因为争夺资源而无法继续执行的状态,操作系统需要实现机制来检测和解决死锁。
- 进程挂起和唤醒:
- 挂起状态详解: 进程被挂起时,其状态和内存内容被保存,但不占用 CPU 时间。挂起可以是主动(如进程等待某事件)或被动(如被调度器挂起)的。
- 唤醒操作: 当挂起的进程被重新唤醒时,其状态和内存内容被还原,可以继续执行。
- 进程终止:
- 正常终止和异常终止: 进程可以正常执行完成,也可以因为错误或异常情况被提前终止。
- 进程退出码: 进程在终止时返回一个退出码,用于通知父进程它的执行结果。
- 父子进程关系: 父进程通常需要等待子进程的终止,并获取其退出码。
- 进程管理的资源分配:
- CPU时间分配: 调度器根据进程的优先级、时间片等分配 CPU 时间。
- 内存分配: 进程需要足够的内存空间,操作系统需要负责内存的分配和释放。
- 文件和设备分配: 进程可能需要访问文件和设备资源,操作系统需要进行相应的管理。
- 守护进程:
- 守护进程详解: 守护进程是在后台运行的进程,通常独立于用户会话,并执行系统任务如日志记录、定期维护等。
如何创建进程
- 分配唯一的进程标识符(PID):
- 操作系统为新进程分配一个唯一的进程标识符,即 PID。PID 是用于唯一标识系统中每个进程的整数。
- 为进程分配资源:
- 操作系统为新进程分配必需的资源,包括内存空间、文件描述符、寄存器等。这些资源通常在进程控制块(PCB)中进行记录。
- 初始化进程控制块(PCB):
- 操作系统创建一个数据结构,通常称为进程控制块(PCB),用于存储和管理进程的信息,如程序计数器、寄存器状态、进程状态等。
- 加载可执行文件:
- 操作系统将进程的可执行文件或程序加载到分配给进程的内存中。这通常涉及将程序的代码段、数据段等部分加载到适当的内存地址。
- 设置程序计数器(PC):
- 操作系统将程序计数器(PC)设置为进程的起始地址,使其指向程序的入口点,从而使进程能够开始执行。
- 启动执行:
- 操作系统通过将 CPU 控制权转移给新创建的进程,开始执行该进程。这可能涉及到上下文切换,其中操作系统保存当前进程的上下文,并加载新进程的上下文。
- 用户态和内核态的切换:
- 操作系统将新进程从用户态切换到内核态,以便执行系统级操作,如访问硬件资源、进行系统调用等。
- 返回用户空间:
- 一旦进程被加载并开始执行,操作系统将控制权返回给用户空间,使得进程能够执行其指定的任务。
进程上下文
进程上下文是指操作系统维护的关于进程执行状态的信息,以便在进行进程切换时能够恢复和保存进程的状态。进程上下文包括了当前进程在执行过程中的所有关键信息,使得进程可以在被中断或被调度切换时能够无缝地恢复到之前的状态。
- 相关概念:
- 程序计数器(Program Counter,PC): 记录了下一条要执行的指令的地址。
- 寄存器状态: 包括通用寄存器、程序状态寄存器(PSR/FLAGS)等,记录了进程执行过程中的各种数据。
- 栈指针(Stack Pointer,SP): 指向进程运行时的栈顶,用于存储局部变量和函数调用信息。
- 堆指针: 指向进程运行时的堆顶,用于动态内存分配。
- 内存映射信息: 记录了进程的内存映射关系,包括代码段、数据段、堆、栈等。
- 文件描述符表: 记录了进程打开的文件和网络连接等信息。
- 上下文切换过程:
- 保存当前上下文: 在进程切换之前,操作系统会保存当前进程的上下文信息。这包括将寄存器状态、程序计数器等信息保存到该进程的进程控制块(PCB)中。
- 加载新上下文: 在切换到新的进程时,操作系统会从该进程的 PCB 中加载保存的上下文信息,恢复寄存器状态、程序计数器等。
- 上下文切换的触发条件:
- 中断: 当硬件或软件发生中断时,当前执行的进程的上下文会被保存,然后切换到中断服务程序的上下文。
- 系统调用: 当进程请求操作系统提供的服务(例如文件读写、网络通信等),会触发系统调用,导致上下文切换。
- 时钟中断: 定时器中断周期性地发生,触发操作系统的调度器进行进程切换。
- 用户态和内核态切换:
- 用户态切换到内核态: 当进程需要进行系统调用或发生异常时,会从用户态切换到内核态。这时会保存用户态的上下文信息。
- 内核态切换到用户态: 当系统调用或异常处理完成后,会从内核态切换回用户态,加载之前保存的用户态上下文。
- 进程的生命周期中的上下文变化:
- 创建上下文: 当进程被创建时,需要初始化其上下文,包括设置初始的寄存器状态、堆栈等信息。
- 执行上下文: 进程在执行过程中,不断更新上下文信息,如寄存器状态、程序计数器等。
- 终止上下文: 当进程执行完成或被终止时,其上下文信息会被销毁。
- 上下文切换的开销:
- 开销: 上下文切换是一个相对昂贵的操作,因为需要保存和加载大量寄存器状态,可能会导致缓存失效等性能问题。
- 优化: 操作系统和硬件通过一些技术(如快速上下文切换、多核并发等)来减小上下文切换的开销。
- 进程上下文的安全性:
- 安全性: 进程上下文中包含敏感信息,如密码、加密密钥等。操作系统需要采取措施来确保上下文的安全性,防止被非法获取。
进程控制块 PCB
PCB(Process Control Block,进程控制块) 是操作系统中用于管理和维护进程信息的数据结构。每个进程都有一个对应的 PCB,它包含了关于进程状态、寄存器内容、内存分配、打开文件等重要信息。PCB 是操作系统对进程进行调度和管理的基础。
- 进程标识信息:
- 进程ID(PID): 用于唯一标识系统中的每个进程。
- 父进程ID: 记录创建该进程的父进程的ID。
- 进程状态信息:
- 进程状态: 记录进程的当前状态,如就绪、运行、阻塞等。
- 程序计数器(PC): 存储下一条要执行的指令的地址。
- 寄存器状态: 记录通用寄存器、程序状态寄存器(PSR/FLAGS)等的内容。
- 进程调度信息:
- 优先级: 进程被调度时的优先级,用于决定其获得CPU时间的权重。
- 调度队列指针: 进程在就绪队列中的位置,用于调度算法。
- 进程控制信息:
- 进程所属用户: 标识进程属于哪个用户。
- 进程创建时间: 记录进程被创建的时间戳。
- 进程运行时间: 统计进程运行的总时间。
- 内存管理信息:
- 基址和界限寄存器: 用于内存保护,限制进程访问的内存范围。
- 页表指针: 存储进程的页表信息,用于虚拟内存管理。
- 文件管理信息:
- 打开文件表: 记录进程打开的文件和相应的文件控制块(FCB)指针。
- 当前工作目录: 进程当前所在的工作目录。
- 进程间通信信息:
- 消息队列、信号量、共享内存等: 用于进程之间的通信和同步。
- 进程挂起和唤醒信息:
- 挂起标志: 表示进程是否被挂起。
- 挂起原因: 记录进程被挂起的原因,如等待I/O。
- 异常和中断处理信息:
- 中断状态寄存器: 记录中断处理的状态信息。
- 异常处理程序指针: 存储异常处理程序的入口地址。
线程控制块 TCB
TCB(Thread Control Block,线程控制块) 是用于管理和维护线程信息的数据结构。与进程控制块(PCB)类似,TCB 专门用于描述线程的状态、上下文和相关信息。每个线程都有一个对应的 TCB,用于操作系统对线程进行调度和管理。
- 线程标识信息:
- 线程ID(TID): 用于唯一标识系统中的每个线程。
- 所属进程ID: 标识线程所属的进程的ID。
- 线程状态信息:
- 线程状态: 记录线程的当前状态,如就绪、运行、阻塞等。
- 程序计数器(PC): 存储下一条要执行的指令的地址。
- 寄存器状态: 记录通用寄存器、程序状态寄存器(PSR/FLAGS)等的内容。
- 线程调度信息:
- 优先级: 线程被调度时的优先级,用于决定其获得CPU时间的权重。
- 调度队列指针: 线程在就绪队列中的位置,用于调度算法。
- 线程控制信息:
- 线程创建时间: 记录线程被创建的时间戳。
- 线程运行时间: 统计线程运行的总时间。
- 内存管理信息:
- 基址和界限寄存器: 用于内存保护,限制线程访问的内存范围。
- 页表指针: 存储线程的页表信息,用于虚拟内存管理。
- 线程挂起和唤醒信息:
- 挂起标志: 表示线程是否被挂起。
- 挂起原因: 记录线程被挂起的原因,如等待I/O。
- 异常和中断处理信息:
- 中断状态寄存器: 记录中断处理的状态信息。
- 异常处理程序指针: 存储异常处理程序的入口地址。
- 安全性信息:
- 权限和特权级别: 限制线程对系统资源的访问权限。
- 安全上下文: 包括安全相关的信息,如加密密钥等。
- 线程间通信信息:
- 线程私有变量: 存储线程私有的数据。
- 共享资源信息: 记录线程对共享资源的访问情况。
- 其他信息:
- 线程环境信息: 包括线程的环境变量、命令行参数等。
- 线程状态变迁信息: 记录线程从创建到终止的状态变迁。
浏览器如何创建js解释器线程*
当浏览器执行 JavaScript 代码时,通常会涉及到创建解释器线程来解释和执行 JavaScript 代码。
以下是简要的流程:
- 用户输入 JavaScript 代码:
- 用户在浏览器中输入或触发与 JavaScript 相关的操作,例如在地址栏输入网址、点击按钮等。
- HTML 解析和 DOM 构建:
- 浏览器首先解析 HTML 文档,构建 DOM(文档对象模型)树,同时发现并加载 JavaScript 脚本。
- 创建解释器线程:
- 当浏览器发现需要执行 JavaScript 代码时,它会创建一个 JavaScript 解释器线程。不同的浏览器使用不同的 JavaScript 引擎,例如 V8 引擎(Chrome)、SpiderMonkey(Firefox)、JavaScriptCore(Safari)等。
- 解释和执行 JavaScript 代码:
- 解释器线程负责将 JavaScript 代码翻译成机器代码或字节码,并执行这些代码。执行期间,可能会涉及词法分析、语法分析、抽象语法树(AST)的构建,然后执行生成的中间表示。
- 与主线程的交互:
- JavaScript 解释器线程和浏览器的主线程之间可能需要进行交互。例如,解释器线程可能会更新 DOM、触发事件,或者通过 Web APIs 发起异步请求。
- 事件循环(Event Loop):
- JavaScript 是单线程的,因此浏览器通过事件循环机制实现异步操作。当有异步操作(如定时器、网络请求)完成时,将触发回调函数,该回调函数将被添加到事件队列中等待执行。
- 处理异步任务:
- 解释器线程在执行完当前的 JavaScript 任务后,会检查事件队列,如果有待执行的异步任务,就会调用相应的回调函数。
- 渲染更新(可选):
- 如果 JavaScript 代码修改了页面的结构或样式,浏览器可能需要触发重新布局(layout)和重绘(paint)过程,最终更新屏幕上的渲染结果。
进程状态
在操作系统中,进程状态是指进程在其生命周期中所处的不同状态。进程可以经历多个状态,而其状态的改变通常是由不同的事件触发的。
常见的进程状态包括:
- 创建状态(New):
- 进程被创建但尚未开始执行。
- 操作系统正在为进程分配资源,初始化 PCB(进程控制块)等数据结构。
- 就绪状态(Ready):
- 进程已准备好执行,等待被调度。
- 进程已经获取了所有必需的资源,但由于处理器有限,还未被分配 CPU 时间。
- 运行状态(Running):
- 进程正在执行指令,占用 CPU 时间。
- 在单处理器系统中,同一时刻只有一个进程处于运行状态。
- 阻塞状态(Blocked):
- 进程因等待某个事件(如 I/O 操作、信号)而暂时停止执行。
- 进程在等待事件完成之前不能被调度执行。
- 终止状态(Terminated):
- 进程执行完成,或被提前终止。
- 操作系统会释放进程占用的资源,回收 PCB 等数据结构。
- 挂起状态(Suspended):
- 进程被暂时从内存中移除,但保留其状态。
- 可分为挂起就绪(Suspended-Ready)和挂起阻塞(Suspended-Blocked)两种状态。
- 唤醒状态:
- 挂起状态的进程被重新放回内存,可以继续执行。
- 通常是由于某个事件的发生,使得进程不再被阻塞。
- 就绪挂起状态和阻塞挂起状态:
- 就绪挂起状态(Ready-Suspended): 进程处于就绪状态,但被挂起,即不能被立即调度执行。
- 阻塞挂起状态(Blocked-Suspended): 进程处于阻塞状态,但被挂起,即不能被立即唤醒和执行。
- 新状态(New):
- 在某些系统中,进程创建后可能会经过一个新状态,表示刚刚被创建的进程。
- 就绪挂起、阻塞挂起状态的唤醒:
- 这两个状态的进程在被唤醒后,会回到对应的就绪或阻塞状态,具体状态取决于其被挂起前的状态。
进程切换
进程切换是操作系统中的一个关键概念,它指的是从一个正在运行的进程切换到另一个进程的过程。进程切换包括保存当前运行进程的上下文并加载下一个要执行的进程的上下文。这个过程通常由操作系统的调度器负责。
- 保存当前进程的上下文(Context Switch):
- 将当前运行进程的寄存器状态、程序计数器(PC)、堆栈指针等上下文信息保存到该进程的 PCB(进程控制块)中。
- 如果是多线程环境,可能还需要保存线程的 TCB(线程控制块)。
- 选择下一个要执行的进程:
- 通过调度算法从就绪队列中选择下一个要执行的进程。
- 调度算法可以是先来先服务(FCFS)、最短作业优先(SJF)、轮转调度(Round Robin)等。
- 加载下一个进程的上下文:
- 从下一个要执行的进程的 PCB 中加载寄存器状态、程序计数器、堆栈指针等上下文信息。
- 如果是多线程环境,加载相应线程的 TCB。
- 执行下一个进程:
- 将控制权交给新选择的进程,使其开始执行。
- 操作系统调度器可能会发出切换指令,将 CPU 控制权切换到新选择的进程。
- 切换完毕:
- 当新进程开始执行后,进程切换过程就完成了。
- CPU 现在执行的是新选择的进程。
- 成本和开销:
- 进程切换是一个相对昂贵的操作,因为需要保存和加载大量的上下文信息,可能导致缓存失效等性能问题。
- 操作系统和硬件采用一些技术来降低进程切换的成本,如快速上下文切换、多核并发等。
- 特殊情况的切换:
- 中断处理切换: 当发生中断时,操作系统可能需要切换到中断处理程序的上下文。
- 系统调用切换: 当进程发起系统调用时,可能需要从用户态切换到内核态。
- 抢占和非抢占调度:
- 抢占调度: 操作系统可以根据调度算法随时剥夺当前运行进程的 CPU 时间,切换到更高优先级的进程。
- 非抢占调度: 只有在当前进程主动释放 CPU 或发生中断时才进行切换。
- 进程切换的频率:
- 进程切换的频率与调度算法、系统负载等因素有关。频繁的切换可能会导致性能下降。
进程通信
进程通信是指在多进程环境中,不同进程之间进行信息传递和数据交换的机制。进程通信允许在并发执行的进程之间共享信息,以便协调和合作完成任务。
在操作系统中,有多种进程通信的方式,其中常见的包括:
- 管道(Pipes):
- 单向通信: 管道是一个单向通信的机制,一个进程可以写入数据,另一个进程可以读取数据。
- 用途: 通常用于具有父子关系的进程之间的通信。
- 命名管道(Named Pipes):
- 命名: 与管道类似,但是可以通过命名访问,即可在不相关的进程之间进行通信。
- 用途: 可用于不同用户或不同应用程序之间的通信。
- 消息队列(Message Queues):
- 消息缓冲区: 进程可以通过消息队列向其他进程发送消息,消息包含数据和一个消息类型。
- 用途: 支持进程之间的异步通信,适用于复杂的通信场景。
- 信号(Signals):
- 异步通知: 进程可以通过信号向其他进程发送异步通知,用于处理事件或异常情况。
- 用途: 常用于处理外部事件、错误处理等。
- 共享内存(Shared Memory):
- 内存区域: 多个进程可以共享同一块内存区域,使得它们可以直接读写这块内存。
- 用途: 适用于高性能的数据共享,但需要注意同步和互斥问题。
- 套接字(Sockets):
- 网络通信: 进程可以通过套接字在网络上进行通信,不仅限于同一台机器。
- 用途: 适用于分布式系统中的进程通信。
- 文件映射(Memory-mapped Files):
- 虚拟内存映射: 将一个文件或内存对象映射到进程的地址空间,实现共享数据的通信。
- 用途: 用于高效的文件读写和进程通信。
- 信号量(Semaphores):
- 计数器: 提供了一个计数器,用于多个进程之间的同步和互斥。
- 用途: 控制对共享资源的访问,防止竞争条件。
- 流(Streams):
- 字节流: 通过字节流进行读写操作,允许进程之间传递数据。
- 用途: 适用于父子进程通信等场景。
- RPC(Remote Procedure Call):
- 远程调用: 允许一个程序调用另一个地址空间的过程,实现远程进程之间的通信。
- 用途: 在分布式系统中实现进程之间的远程调用。
- 消息传递(Message Passing):
- 直接通信: 进程之间通过显式发送和接收消息进行通信。
- 用途: 实现松耦合的进程通信,适用于并发和分布式环境。
进程同步
- 定义:
- 进程同步是指多个进程在执行过程中需要按照一定的顺序协调运行,以确保共享资源的正确访问和数据的一致性。
- 目的:
- 防止进程间的竞争条件(Race Condition),即多个进程同时访问共享资源可能导致的问题。
- 确保数据的一致性,避免因并发访问导致的数据错误。
- 常见的同步机制:
- 互斥锁(Mutex): 通过对共享资源进行加锁和解锁,确保在任意时刻只有一个进程能够访问共享资源。
- 信号量(Semaphore): 用于控制对有限资源的访问,允许多个进程同时访问,但数量有限。
- 条件变量(Condition Variable): 用于在进程之间传递信号,通知其他进程某个特定条件的发生。
- 实现方式:
- 利用硬件指令(如测试-设置指令)或操作系统提供的原语,通过代码控制进程对共享资源的访问。
进程并发
- 定义:
- 进程并发是指多个进程在同一时刻执行,共享系统资源。并发不仅包括多任务的同时执行,还包括交替执行。
- 目的:
- 提高系统资源的利用率,允许多个任务在相同的时间间隔内执行,从而提高整体吞吐量。
- 通过分时操作系统,使用户感觉多个任务在同时进行。
- 特点:
- 并发是指多个任务在宏观上交替执行,而在微观上可能同时执行。
- 进程并发可以在多核处理器上通过真正的并行执行,或在单核处理器上通过时间片轮转的方式进行模拟。
- 实现方式:
- 利用操作系统的调度机制,通过分时复用 CPU 时间,使多个进程在宏观上同时执行。
线程同步
线程同步是指多个线程在执行过程中按照一定的顺序协调运行,以确保共享资源的正确访问和数据的一致性。在多线程环境中,线程同步非常重要,以避免竞争条件(Race Condition)和数据不一致性问题。
目的:
- 防止竞争条件:
- 竞争条件指的是多个线程同时访问共享资源,由于执行顺序不确定而导致的问题。同步机制确保在任意时刻只有一个线程能够访问共享资源。
- 确保数据一致性:
- 当多个线程同时访问和修改共享数据时,需要确保对数据的操作是原子的,以防止数据不一致性。
常见的线程同步机制:
- 互斥锁(Mutex):
- 通过对共享资源加锁和解锁,确保在任意时刻只有一个线程能够访问共享资源。其他线程必须等待锁的释放。
- 信号量(Semaphore):
- 用于控制对有限资源的访问,允许多个线程同时访问,但数量有限。信号量可用于解决生产者-消费者问题等。
- 条件变量(Condition Variable):
- 用于在线程之间传递信号,通知其他线程某个特定条件的发生。常与互斥锁配合使用。
- 读写锁(Read-Write Lock):
- 区分读取和写入操作,允许多个线程同时读取共享资源,但只有一个线程能够写入。适用于读操作频繁、写操作较少的场景。
- 屏障(Barrier):
- 用于协调多个线程在某个点上汇合,等待所有线程都到达后再继续执行。适用于需要所有线程同步的场景。
内存管理
内存管理是操作系统负责分配和回收计算机内存的一项核心任务。有效的内存管理是确保系统稳定性和性能的关键因素之一。
以下是关于内存管理的一些基本概念:
- 内存分配和释放:
- 分配内存:
- 静态分配: 在程序编译时分配固定大小的内存。例如,全局变量和静态变量的内存分配。
- 动态分配: 在程序运行时根据需要分配内存。常见的动态分配方式包括使用 malloc(C语言)、new(C++)、malloc、calloc、realloc(C语言和C++)、alloc(Python)等。
- 释放内存:
- 对于静态分配的内存,由系统自动回收。
- 对于动态分配的内存,需要显式释放,否则可能导致内存泄漏。使用 free(C语言)、delete(C++)、dealloc(Objective-C)、del(Python)等。
- 内存碎片:
- 内部碎片:
- 动态分配的内存块可能比实际需要的要大,导致内存浪费。
- 外部碎片:
- 已经分配给进程的内存中,由于释放和分配的不规律,留下了无法使用的小块零散内存。
- 虚拟内存:
- 定义:
- 将物理内存和磁盘空间组合使用的技术。虚拟内存允许程序使用比物理内存更多的内存。
- 分页和分段:
- 分页: 将物理内存和虚拟内存划分为固定大小的页,实现数据的分页存储。
- 分段: 将程序划分为若干段,每段具有不同的访问权限,实现数据的分段存储。
- 页面置换:
- 当物理内存不足时,操作系统通过页面置换算法将部分页面从物理内存移到磁盘,以释放空间。
- 内存保护:
- 访问权限:
- 操作系统通过设置页表或段表,为每个内存区域指定访问权限,如读、写、执行等。
- 地址空间布局:
- 操作系统将进程的地址空间划分为代码段、数据段、堆、栈等区域,并规定它们的地址范围。
- 内存映射:
- 文件映射:
- 将磁盘上的文件映射到进程的地址空间,使得文件可以被当做内存访问。
- 共享内存:
- 允许多个进程访问相同的物理内存,实现进程间通信。
- 垃圾回收:
- 自动内存管理:
- 通过垃圾回收机制自动识别和回收不再使用的内存,减少内存泄漏的风险。
- 垃圾回收算法:
- 常见的垃圾回收算法包括标记清除、引用计数、分代垃圾回收等。
- 内存优化:
- 内存对齐:
- 结构体和类成员的内存地址按照某个基本类型的大小对齐,提高内存读写效率。
- 缓存优化:
- 通过合理利用缓存,减少内存访问延迟,提高程序性能。
- 内存池:
- 预先分配一定数量的内存块,避免频繁的动态内存分配和释放。
磁盘驱动器
磁盘驱动器是计算机系统中用于存储和检索数据的关键部件,通常用于长期存储数据。它包含磁盘、磁头、电机、控制电路等组件,通过旋转的磁盘表面和移动的磁头来实现数据的读取和写入。
以下是关于磁盘驱动器的一些基本概念:
- 磁盘组成部分:
- 磁盘:
- 磁盘驱动器通常包含一个或多个硬盘,每个硬盘都是由多个磁性的盘片组成。
- 磁头:
- 磁头是贴近磁盘表面的传感器,用于读取和写入数据。每个盘片上都有一个磁头。
- 扇区、磁道和柱面:
- 扇区: 磁盘上被划分的最小存储单元,存储固定数量的字节。
- 磁道: 磁盘上同心圆的一个环,包含多个扇区。
- 柱面: 相邻磁盘上的同一磁道形成的三维结构,包含多个磁道。
- 工作原理:
- 旋转和寻道:
- 磁盘在高速电机的驱动下旋转,磁头则移动到指定的磁道上。这两个动作结合起来称为寻道。
- 读写操作:
- 磁头通过磁性感应读取或写入磁盘表面上的磁性颗粒,进行数据的读取和写入。
- 磁盘调度
- 最短寻道时间优先
- 电梯算法
- 最短定位时间优先
- 廉价冗余磁盘阵列 RAID Redundant Array of Inexpensive Disks
- 优点
- 性能: 并行使用多个磁盘, 加快 I/O 时间
- 容量: 多个磁盘大容量
- 可靠性: 磁盘故障
- 组成: 处理器、内存、磁盘
- RAID 0(条带化):
- 结构: 将数据分散存储在两个或更多的硬盘上,但不提供冗余。
- 优点: 提高读写性能,因为数据可以并行处理。
- 缺点: 如果任何一个硬盘故障,整个阵列的数据都将丢失。
- 适用场景: 高性能需求,数据可靠性不是首要考虑因素的场景。
- RAID 1(镜像):
- 结构: 数据在两个硬盘上完全复制,形成“镜像”。
- 优点: 提供很高的数据可靠性,如果一个硬盘故障,另一个可以立即接管。
- 缺点: 成本较高(需要双倍存储容量)。
- 适用场景: 数据安全性极为重要的应用。
- RAID 5(带奇偶校验的条带化):
- 结构: 数据和奇偶校验信息分散在三个或更多的硬盘上。
- 优点: 提供数据冗余和改进的读取性能。
- 缺点: 写入性能受到一定影响,恢复速度慢。
- 适用场景: 需要平衡性能和可靠性的业务应用。
- RAID 6(双奇偶校验):
- 结构: 类似 RAID 5,但有两套奇偶校验系统,可以容忍两个硬盘同时故障。
- 优点: 比 RAID 5 更高的数据安全性。
- 缺点: 需要更多的硬盘,成本较高。
- 适用场景: 对数据安全性有极高要求的场合。
- RAID 10(或称 RAID 1+0):
- 结构: 结合 RAID 0 和 RAID 1 的特点,先镜像后条带化。
- 优点: 提供很高的数据可靠性和良好的读写性能。
- 缺点: 成本高,至少需要四个硬盘。
- 适用场景: 需要高性能和高可靠性的关键应用。
文件系统
从操作系统的角度来看,文件系统是操作系统用于管理和组织存储在计算机上的数据的一种机制。文件系统提供了一个抽象层,使得用户和应用程序可以通过简单的文件和目录结构来访问和管理存储设备上的数据。
- 基本概念
- inode index node
- 定义: Inode(索引节点)是 UNIX 和类 UNIX 系统中的一种数据结构,用于存储文件的元数据,但不包含文件名或实际数据。
- 内容: Inode 包含文件的大小、权限、所有者、创建和修改时间等信息,以及指向存储文件实际内容的数据块的指针。
- 位图
- 用途: 位图是一种简单高效的方式,用于管理磁盘空间的分配,如跟踪磁盘上的空闲和已用数据块。
- 工作原理: 每个位代表磁盘上的一个块或 Inode 的状态(已用为 1、空闲为 0)。
- 数据块
- 定义: 数据块是文件存储的基本单位。文件系统将磁盘分割成一系列块,用于存储文件数据。
- 大小: 块的大小通常固定(如 1KB、4KB),对文件系统的性能有重要影响
- 硬链接: 指向文件的 Inode,而不是文件名。删除原始文件不会影响到硬链接。
- 软链接(符号链接): 类似于快捷方式,包含指向另一个文件路径的文本。如果原始文件被删除或移动,软链接将失效。
- 目录树
- 概念: 文件系统通过目录树的结构组织文件和文件夹,每个节点代表一个文件或目录。
- 特点: 根目录是树的起点,每个目录项都包含文件/目录的名称和指向其 Inode 的指针
- 文件系统数据结构和访问方法
- 数据结构: 文件系统使用如 B-树、哈希表等数据结构来存储和索引文件。
- 访问方法: 包括顺序访问、直接访问、索引访问等,影响文件系统的性能和效率。
- 校验和(Checksum)
- 定义: 校验和是一种用于检测数据完整性的机制。
- 用途: 在文件传输或存储过程中,用于检测数据是否在传输或存储过程中发生变化。
- 崩溃一致性问题
- 解决方案
- 文件检查程序
- 日志
- 软更新
- 写时复制
- 基于反向指针的一致性
- 常见的文件系统类型
- 基于磁盘的文件系统: 如 FAT(File Allocation Table)、NTFS(New Technology File System)、EXT(Extended File System)等。
- 网络文件系统: 如 NFS(Network File System),允许通过网络访问远程文件。
- 分布式文件系统: 如 Google 的 GFS(Google File System),为大规模数据存储设计。
- 虚拟文件系统: 如 Linux 中的 VFS(Virtual File System),为不同的实际文件系统提供统一接口。
- 文件分配方法
- 连续分配: 文件在磁盘上连续存储,易于实现,但容易产生碎片。
- 链式分配: 文件分散存储,每个部分指向下一个部分的位置。
- 索引分配: 使用索引块来跟踪文件的各个部分。
- 文件系统的性能优化
- 缓存: 使用内存中的缓存来减少磁盘访问。
- 碎片整理: 重新组织文件存储以减少碎片。
- 日志或快照: 用于快速恢复和保护数据完整性。