首页 > 其他分享 >操作系统 | OS

操作系统 | OS

时间:2023-05-08 16:55:12浏览次数:29  
标签:操作系统 队列 线程 内核 进程 OS 内存

1 操作系统简介

1.1 操作系统的定义与功能

操作系统指的是从计算机加电运行后一直在内存运行的程序, 又称 “内核”. 它负责管理计算机硬件和软件资源, 同时为用户和应用程序提供一个友好的交互界面. 操作系统的主要功能包括进程管理, 内存管理, 文件系统管理, 设备管理, 用户界面和系统调用.

1.2 操作系统的分类和发展历程

  • 早期计算机系统 (无操作系统, 使用程序直接与硬件交互)
  • 批处理操作系统 (自动地处理一批待执行的程序, 减少人机交互)
  • 分时操作系统 (多个用户通过终端同时使用计算机, 共享计算资源)
  • 个人计算机操作系统 (面向单用户, 如 MS-DOS, Windows, MacOS)
  • 网络操作系统 (支持计算机之间的通信与资源共享, 如 Novell NetWare)
  • 分布式操作系统 (将多台计算机资源整合在一起, 提供统一的计算环境)

1.3 操作系统的体系结构

1.3.1 单内核

在单内核结构中, 操作系统的所有功能模块都集成在一个庞大的内核中. 这种结构的优点是性能较好, 因为系统调用和上下文切换开销较小. 然而, 缺点是结构复杂, 难以扩展和维护. 此外, 一个模块出现故障可能会影响整个系统. 传统的 Unix 和 Linux 就是单内核结构的代表.

1.3.2 微内核

微内核结构将操作系统的基本功能 (如内存管理, 进程调度和通信) 保留在内核中, 而将其他功能 (如设备驱动, 文件系统等) 移到用户空间, 作为单独的服务程序运行. 这种结构的优点是可扩展性和可维护性较好, 同时具有较高的安全性和可靠性. 然而, 由于系统调用和进程间通信的开销较大, 微内核结构的性能可能较差. Mach, Minix 和 QNX 等操作系统采用了微内核结构.

1.3.3 混合内核

混合内核结构试图在单内核和微内核之间找到一个平衡点. 它将一些关键的功能模块 (如文件系统, 设备驱动等) 移回内核空间, 以提高性能. 与单内核相比, 混合内核仍具有较好的可扩展性和可维护性. 混合内核结构的代表包括 Windows 系列等操作系统.

1.4 执行状态

1.4.1 用户态

用户态 (User mode): 用户态是处理器运行用户程序的模式. 在用户态下, 程序只能访问受限制的资源和功能, 无法直接访问操作系统内核和硬件资源. 用户态下的程序执行在一个受限制的环境中, 不能执行某些特权指令, 如修改内存映射表, 操作 I/O 设备等. 当用户程序需要访问受保护的资源或执行特权操作时, 必须通过系统调用 (System call) 向操作系统发出请求, 让操作系统在内核态中代为执行.

1.4.2 内核态

内核态 (Kernel mode): 内核态是处理器运行操作系统内核代码的模式. 在内核态下, 操作系统可以访问所有硬件资源和执行特权指令. 操作系统内核负责管理系统资源, 如内存, 文件, I/O 设备等, 以及处理系统调用, 中断和异常. 当操作系统在内核态中执行时, 它具有完全控制权, 可以执行任何指令和访问任何资源.

2 进程管理

进程是程序在计算机上的一次执行过程, 是操作系统资源分配和调度的基本单位. 进程就是进行中的程序, 即进程 = 程序 + 执行.

2.1 进程的内存管理

操作系统通过将内存分割为不同的区域, 实现了对进程内存的有效管理. 进程的内存空间通常分为以下几个部分:

  1. 代码区 (Code Segment): 代码区用于存储程序的可执行代码, 通常是只读的, 以防止程序在运行过程中意外修改其代码.

  2. 数据区 (Data Segment): 数据区包括全局变量和静态变量, 通常分为已初始化数据区 (初始化的全局变量和静态变量) 和未初始化数据区 (未初始化的全局变量和静态变量).

  3. 堆 (Heap): 堆用于存储动态分配的内存, 如使用 malloc 或 new 分配的内存. 堆的大小是可变的, 可以根据程序的需求进行扩展和收缩.

  4. 栈 (Stack): 栈用于存储函数调用的局部变量和控制信息 (如返回地址). 栈具有后进先出 (LIFO) 的特性, 可以实现函数调用和返回的顺序控制.

在多个进程的内存存储方面, 操作系统通常采用虚拟内存技术实现进程的内存隔离和保护. 虚拟内存技术允许每个进程拥有自己的独立地址空间, 从而使得进程之间的内存空间互不干扰, 确保了进程的独立性.

操作系统也提供了一些机制以支持进程间的通信和共享, 例如共享内存是一种允许多个进程访问同一块内存区域的进程间通信方式.

2.2 进程状态

2.2.1 进程状态

进程有五种基本状态: 新建 (New), 就绪 (Ready), 运行 (Running), 阻塞 (Waiting) 和终止 (Terminated).

新建状态

  1. 进程刚被创建好时, 处于 New 状态;

  2. 等待被系统接纳.

就绪状态

  1. 已经被成功加载进内存并初始化完毕, 等待系统分配 CPU 资源.

运行状态

  1. 已经从就绪状态被调度器选中, 正在利用 CPU 执行.

阻塞状态

  1. 进程执行受到阻碍, 必须暂停的状态;

  2. 阻碍进程继续执行的因素可能有: I/O, 等待某个事件发生.

终止状态

  1. 进程执行完毕后等待被系统清除的状态.

2.2.2 状态迁移

  • 新建 -> 就绪

进程的数据结构创建完毕, 初始化好了后, 操作系统将状态标记为就绪, 将进程控制块插入进程就绪队列;

  • 就绪 -> 运行

就绪进程被派遣程序安排到 CPU 上运行;

  • 运行 -> 终止

进程运行结束, 或因出错被异常终止;

  • 阻塞 -> 就绪

IO 完成事件, 使等待该 IO 进程被唤醒, 转入就绪状态.

2.3 进程调度

2.3.1 调度算法评价指标

吞吐率: 在单位时间内完成的进程数量. 吞吐率用于衡量系统处理能力, 吞吐率越高, 说明系统处理能力越强.

周转时间: 从进程创建到进程终止的总时间. 周转时间包括进程在就绪队列中等待的时间, 实际占用 CPU 运行的时间以及因等待 I/O 操作而阻塞的时间. 降低周转时间意味着进程能更快地执行完毕.

服务时间: 进程实际占用 CPU 运行的时间, 即进程在 CPU 上执行的累计时间. 服务时间是衡量进程实际运行所需时间的指标.

等待时间: 进程在就绪队列中等待 CPU 资源的总时间. 减少等待时间可以降低进程延迟并提高系统的响应能力.

响应时间: 从进程提交到进程开始执行的时间. 在交互式系统中, 响应时间是衡量用户体验的一个重要指标, 因为它反映了用户提交请求后需要等待多久才能得到反馈.

响应比: 响应比是周转时间与服务时间的比值. 在一些调度算法中,响应比越高的进程将被优先调度.

2.3.2 调度算法

FCFS 调度算法

先来先服务 (First-Come-First-Serve) 采用 FIFO 队列维护就绪进程, 每次从 FIFO 队列取队首进程, 将其投入运行, 新进入就绪态的进程放在队尾.

FCFS 算法不稳定, 长进程先于短进程到达会导致平均等待时间拉长 (护航效应).

SJF 算法

短作业优先 (Shortest-Job-First) 每次进行调度时, 优先选择下一个 CPU 周期最短的进程, 以平均等待时间为指标, SJF 是最优的调度. 它是一种分时调度, 也就是非抢占式.

SJF 算法可能导致一些长进程迟迟得不到执行.

SRTF 算法

最短剩余时间优先 (Shortest-Remaining-Time-First) 可被视为 SJF 的抢占式版本. 若新就绪进程的剩余时间短于正在执行的进程, 则切换为新进程.

SRTF 同样会导致饥饿问题.

优先级调度算法

每个进程被赋予一个优先数 (Priority Number), 有的系统优先数越大优先级越高, 有的系统反之. 每次调度时优先级最高的任务被选中执行.

它分为抢占式优先级调度和非抢占式优先级调度. 优先级调度是为了照顾紧迫型任务.

HRRN 算法

最高响应比优先调度 (Hightest Response Ratio Next) 选取最高响应比的任务执行. 它是一种动态优先权调度.

轮转调度算法

轮转调度 (Round Robin) 通过时间片轮流给每个任务分配执行时间, 具有高度的公平性. 进程结束后重新放到就绪队列队尾排队.

如果时间片结束同时有新进程到达, 默认新进程比刚结束的进程优先进入就绪队列. 时间片内进程提前完成则时间片提前结束.

MLQ 算法

多级队列调度 (MLQ) 是将任务队列划分为多个级别, 每个级别有一个独立的队列, 不同级别的队列具有不同的优先级和不同的调度策略.

一般来说, 高优先级的队列优先被调度, 低优先级的队列在高优先级队列没有任务时才会被调度. 这种调度算法适用于有明确优先级区分的任务, 如实时任务和普通任务. 它是一种静态优先权调度.

MLFQ 算法

多级反馈调度 (MLFQ) 在 MLQ 的基础上引入了反馈机制, 通过观察任务的行为来调整任务的优先级. 具体地说, 当一个任务等待了一定时间还没有得到执行时, 它的优先级会降低; 相反, 当一个任务被连续执行了一段时间时, 它的优先级会提高. 这种调度算法适用于具有不确定执行时间和可能产生饥饿的任务, 如批处理任务. 它是一种动态优先权调度.

当新进程进入内存后, 首先将它放入第一队列的末尾, 按 FCFS 原则等待调度. 当轮到该进程执行时, 如它能在该时间片 (q = 1) 内完成, 便可撤离系统. 否则, 调度程序将其转入第二队列的末尾等待调度; 如果它在第二队列中运行一个时间片 (q = 2) 后仍未完成, 再依次将它放入第三队列 (q = 4), 依此类推.

调度程序首先调度最高优先级队列中的诸进程运行, 仅当第一队列空闲时才调度第二队列中的进程运行; 换言之, 仅当第 1 ~ (i - 1) 所有队列均空时, 才会调度第 i 队列中的进程运行. 如果处理机正在第 i 队列中为某进程服务时又有新进程进入任一优先级较高的队列, 此时须立即把正在运行的进程放回到第 i 队列的末尾, 而把处理机分配给新到的高优先级进程.

2.4 进程同步

2.4.1 信号量

信号量 (Semaphore) 本质上是一个整数值 S, 用于控制对共享资源的访问. 信号量有两种基本操作: P (Proberen, 尝试) 操作和 V (Verhogen, 提高) 操作.

  1. P 操作: 当进程执行 P 操作时, 信号量 S 的值减一. 如果信号量值小于 0, 进程被阻塞, 直到信号量值变为非负数.
  2. V 操作: 当进程执行 V 操作时, 信号量 S 的值加一. 如果信号量值小于等于 0, 进程唤醒等待队列中的一个阻塞进程.

信号量数据结构:

typedef struct{
    int value;
    struct process *L;
}semaphore;

其中 L 为等待队列.

// P 操作
void wait(semaphore S){
    S.value--;
    if (S.value < 0){
        add this process to S.L;
        block();
    }
}

// V 操作
void signal(semaphore S){
    S.value++;
    if (S.value < 0){
        remove a process P from S.L;
        wakeup(P);
    }
}

2.4.2 进程互斥

进程互斥是指在操作系统中, 多个进程在访问共享资源时需要互斥进行, 即在同一时刻只有一个进程可以访问共享资源.

临界区

  • 多个进程并发访问共享变量 (代表共享资源)
  • 访问共享变量的代码区域, 称为临界区 (Critical Section)

通过保证临界区被互斥的方式访问, 从而保证并发进程计算结果的一致性.

互斥要求

  • 互斥访问: 同一时刻仅有一个进程能进入临界区
  • 空闲让进: 一个进程处于空闲状态时让其他进程占用处理器资源
  • 有限等待: 不能让某个进程无休止等待

互斥算法

  • Peterson 两进程解法

它使用两个布尔型变量 (flag 数组) 和一个整数型变量 (turn) 来表示进程的请求和轮次.

bool flag[2] = {false, false};
int turn = 0;

// 进程 0
while (true) {
    flag[0] = true;
    turn = 1;
    while (flag[1] && turn == 1);
    // 进入临界区
    flag[0] = false;
    // 离开临界区
}

// 进程 1
while (true) {
    flag[1] = true;
    turn = 0;
    while (flag[0] && turn == 0);
    // 进入临界区
    flag[1] = false;
    // 离开临界区
}
  • Lamport 多进程解法

它是一种适用于多个进程的互斥解决方案, 使用请求标记 (requesting 数组) 和时间戳 (timestamp 数组) 来决定访问顺序.

bool requesting[N] = {false};
int timestamp[N] = {0};

// 进程 i
while (true) {
    requesting[i] = true;
    timestamp[i] = max(timestamp) + 1;
    for (int j = 0; j < N; j++) {
        if (j == i) continue;
        while (requesting[j]) {
            while (timestamp[j] < timestamp[i] || (timestamp[j] == timestamp[i] && j < i));
                break;
            }
        }
    }
    // 进入临界区
    requesting[i] = false;
    // 离开临界区
}
  • 硬件解法

硬件解法利用特殊的硬件指令来实现进程互斥. 这些指令具有原子性, 确保在多个进程之间不会出现竞争条件.

int lock = 0;

// Test-and-Set 函数
int test_and_set(int *lock) {
    int old = *lock;
    *lock = 1;
    return old;
}

// 进程
while (true) {
    while (test_and_set(&lock));
    // 进入临界区
    lock = 0;
    // 离开临界区
}

Test-and-Set 函数读取 lock 的旧值, 然后将 lock 的值设置为 1, 返回 lock 的旧值. 当 test_and_set 函数返回 0 时, 表示当前进程已经获取到锁, 可以进入临界区; 当 test_and_set 函数返回 1 时, 表示锁已经被其他进程占用, 当前进程需要继续等待.

2.4.3 直接协作

直接协作指的是进程之间直接进行通信和同步, 例如通过共享内存, 消息传递, 信号量等方式, 在不同的进程之间进行数据传输, 同步或协调操作. 直接协作需要遵循特定顺序.

生产者 - 消费者问题

定义: 给定一个有限大小的缓冲区, 生产者将生产出的产品放入缓冲区, 消费者从缓冲区取出产品.

步骤:

  1. 定义信号量的初值 empty = n, full = 0.

  2. 使用 P/V 操作施加并发控制.

// Producer
while (true) {
    生产一个产品;
    P(empty);
    送产品到缓冲区;
    V(full);
}

// Consumer
while (true) {
    P(full);
    从缓冲区取产品;
    V(empty);
    消费产品;
}
  1. 在有多个生产者或消费者时, 需要考虑添加互斥锁 mutex = 1 来保证进程同步.
// Producer
while (true) {
    生产产品;
    P(empty);
    P(mutex);
    往 Buffer[i] 放产品;
    i = (i + 1) % n;
    V(mutex);
    V(full);
}

// Consumer
while (true) {
    P(full);
    P(mutex);
    从 Buffer[i] 取产品;
    j = (j + 1) % n;
    V(mutex);
    V(empty);
    消费产品;
}

2.4.4 间接协作

间接协作是指进程之间不直接进行通信和同步, 而是通过一个中间的代理来实现协作, 例如通过任务队列, 事件循环等机制, 将进程需要完成的任务提交给一个任务调度器, 由调度器来负责调度和分配任务. 间接协作不需要遵循特定顺序.

读者 - 写者问题

关系:

  1. 有读者在读时, 不可以写; 有写者在写时, 不可以读.

  2. 不允许两个写者同时写共享数据.

  3. 允许两个读者同时读共享数据.

表示:

// Semaphore
rw_mutex = 1 // 读写锁
r_mutex = 1 // 读锁
mutex = 1 // 写锁

// Reader
while (true) {
    P(rw_mutex);
    P(r_mutex);
    r_cnt++;
    if (r_cnt == 1)
        P(mutex);
    V(r_mutex);
    V(rw_mutex);
    read();
    P(r_mutex);
    r_cnt--;
    if (r_cnt == 0)
        V(mutex);
    V(r_mutex);
}

// Writer
while (true) {
    P(rw_mutex);
    P(mutex);
    write();
    V(mutex);
    V(rw_mutex);
}

当一个读者正在读取共享资源时, 另一个读者也可以同时进入访问共享资源, 因为它们都只是获取了读锁, 而没有获取写锁. 但是,当一个写者正在访问共享资源时, 其他读者和写者都需要等待, 直到该写者释放写锁.

这里的 rw_mutex 即为中介结构, 用于协调读者和写者对共享资源的访问, 因此读者写者问题被视为间接协作.

2.4.5 管程

定义

管程把分散在各进程的临界区集中起来进行管理.

  • 提供对共享资源的统一管理

  • 提供一组对资源操作的接口

  • 对并发进程对管程中资源访问的控制, 由管程统一实现

语义

  • Mesa 语义

  • Hoare 语义

  • Brinch Hanson 语义

2.5 死锁

2.5.1 死锁定义

死锁 (Deadlock) 是指在多进程或多线程环境中, 一组进程或线程互相等待彼此持有的资源, 导致所有进程或线程都无法继续执行的现象.

2.5.2 死锁原因

死锁的根本原因: 系统资源不足, 进程推进顺序不当.

导致死锁的四个必要条件:

  • 互斥 (Mutual Exclusion): 资源至少有一个是不能被多个进程或线程共享的. 也就是说, 在某一时刻, 某个资源只能被一个进程或线程占用.

  • 占有并等待 (Hold and Wait): 进程或线程已经占有了至少一个资源, 但又需要获取其他进程或线程持有的资源才能继续执行. 此时, 进程或线程会保持对已占有资源的占用, 并等待其他资源.

  • 非抢占 (No Preemption): 资源不能被抢占. 也就是说, 一旦进程或线程占有了某个资源, 其他进程或线程不能强行将该资源从占有者手中夺走, 除非占有者自愿释放资源.

  • 循环等待 (Circular Wait): 存在一个进程或线程集合, 其中每个进程或线程都在等待下一个进程或线程所持有的资源. 这导致了一个循环等待资源的链, 造成死锁.

为了避免或解决死锁, 可以采取死锁预防, 死锁避免, 死锁检测等方法.

2.5.3 死锁预防

  1. 进程必须获取工作所需所有资源, 才能开展工作. (破坏了 "占有并等待", 但是并行性差)
  2. 对资源进行编号, 约定进程按照编号大小顺序对资源进行分配. (破坏了 "循环等待")

2.5.4 死锁避免

对每次进程资源的申请, 都做严格审查:

  • 确定安全, 则允许分配
  • 不能确定安全, 则不允许分配, 进程等待

安全状态下不会死锁 (存在安全序列), 不安全的状态存在死锁风险.

银行家算法

数据结构:

  1. 可用资源向量 (Available): 表示系统当前可用的各类资源数量.
  2. 最大需求矩阵 (Max): 表示每个进程对各类资源的最大需求量.
  3. 分配矩阵 (Allocation): 表示系统已分配给每个进程的各类资源数量.
  4. 需求矩阵 (Need): 表示每个进程仍需要的各类资源数量, 计算公式为 Need = Max - Allocation.

安全状态和不安全状态:

  1. 安全状态: 存在一个安全序列, 按照这个序列, 系统可以按顺序满足所有进程的资源需求, 使它们顺利完成.
  2. 不安全状态: 不存在一个安全序列, 可能导致死锁.

银行家算法步骤:

  1. 当一个进程请求资源时, 首先检查请求的资源数量是否超过其最大需求量. 如果超过, 则拒绝分配.
  2. 检查请求的资源数量是否小于或等于系统当前可用资源数量. 如果大于, 则让进程等待.
  3. 假设系统分配了请求的资源, 更新 Available, Allocation 和 Need 矩阵.
  4. 检查系统是否处于安全状态. 如果是, 则分配资源; 如果不是, 则撤销分配, 让进程继续等待.

这种算法是保守的, 即使进程未来会释放资源, 不安全的状态也不能够被允许.

2.5.5 死锁检测

  1. 检测死锁, 并解除 (开销大)

  2. 不处理, 忽略 (多用于通用桌面操作系统, "鸵鸟策略")

定期启动对进程等待图的环路检测, 如果发现等待图中有环, 那么报告死锁发生.

检测时机

  1. 当进程阻塞时, 系统实施检测
  2. 定时检测
  3. 系统资源利用率下降时检测死锁

死锁解除手段

  1. 重新启动
  2. 撤销进程
  3. 剥夺进程资源
  4. 进程回退

2.6 进程间通信

进程间通信(IPC)分为低级通信和高级通信.

2.6.1 低级通信

低级通信包括信号量, 互斥锁等, 用于进程控制信息的传递, 传输信息量较小.

2.6.2 高级通信

高级通信包括管道 (Pipe), 共享内存 (Shared Memory), 消息队列 (Message Queue) 等, 用于进程间信息的交换与共享, 传输信息量较大.

管道

管道是一种基本的进程间通信方式, 主要用于具有父子关系的进程之间的通信. 管道允许一个进程的输出成为另一个进程的输入, 实现了进程之间的数据传输.

管道的优点是简单易用, 但缺点是数据传输速度较慢, 且仅适用于具有亲缘关系的进程之间.

共享内存

共享内存是一种将一段内存区域映射到多个进程的地址空间的进程间通信机制. 这种方法允许多个进程访问同一块内存区域, 从而实现快速的数据共享. 共享内存是最快的进程间通信方式, 因为数据无需在进程之间进行复制.

但共享内存的问题在于需要解决同步和互斥问题, 以避免竞争条件. 为了解决这个问题, 通常需要使用信号量, 互斥锁等同步原语.

消息队列

消息队列是一种基于消息的进程间通信机制. 进程可以将消息发送到指定的消息队列中, 然后接收进程可以从队列中读取这些消息. 消息队列通常以先进先出 (FIFO) 的顺序处理消息. 消息队列支持不同优先级的消息, 具有一定的灵活性.

消息队列的实现相对复杂, 需要处理诸如队列管理, 消息排序, 消息持久化等问题.

3 线程管理

线程 (Thread) 是操作系统中能够独立调度和执行的最小单位.

3.1 线程与进程的关系

线程属于进程, 一个进程可以包含一个或多个线程. 与进程相比, 线程共享相同的地址空间, 代码段, 数据段和文件描述符等资源, 这使得线程之间的通信和数据共享更为高效.

线程同样具有新建, 就绪, 运行, 阻塞, 终止五种状态以及迁移过程, 也同样需要考虑同步与互斥问题.

单核 CPU 同样可以使用多线程, 因为每个子任务不同周期可以是交错的.

3.2 线程分类

线程分为用户级线程和内核级线程.

3.2.1 用户级线程

  • 在用户态以线程库的形式实现;

  • 对用户级线程的操作通过调用用户态线程库 API 执行.

3.2.2 内核级线程

  • 在内核态实现, 操作系统内核直接管理;

  • 线程的创建由系统调用完成.

3.3 线程模型

M:1 模型

多个用户级线程绑定到一个内核级线程; 如果一个用户级线程引起阻塞, 会导致整个线程中所有的线程阻塞.

1:1 模型

1 个用户级线程绑定到 1 个内核级线程; 需要内核级线程的数量多, 消耗更多的内核资源.

M:N 模型

多个用户级线程绑定到多个内核级线程; 管理复杂, 影响效率.

4 主存管理

主存 = 内存 = 运存 = RAM. 程序从存储空间加载到主存中才能运行.

4.1 连续内存分配策略

4.1.1 Fix-Sized

使用一个数组来记录各个连续内存块的分配情况. 内存块的状态分为已分配和空闲.

这常常会导致分区内的浪费.

4.1.2 Variable-Sized

使用一个链表或动态数组记录空闲块的状态后分配.

  • 首次适配分配算法 (First-Fit) 从第一个大于请求空间的空闲区中截取所需空间, 修改调整可用表.

链表, 空闲块按起始地址升序.

  • 最佳适配分配算法 (Best-Fit) 对空闲区从小到大排序, 从中搜索第一个大于请求空间的空闲区中截取所需空间, 修改调整可用表.

链表, 空闲块按大小升序.

  • 最差适配分配算法 (Worst-Fit) 对空闲区从大到小排序, 选择第一个空闲区截取所需空间, 修改调整可用表.

链表, 空闲块按大小降序.

这些都会产生内存碎片.

4.2 分页机制

4.2.1 分页

  • 将进程的逻辑地址空间按页划分

  • 将物理内存的地址按页划分

  • 每个页代表一组连续地址

  • 进程被分配的页在物理内存中不一定连续

4.2.2 地址划分

虚拟地址被按位拆分成页号 (高位) 和页内偏移 (低位) 两部分.

页号用于确定该地址位于哪个虚拟页面, 页内偏移表示在该页面中的具体位置.

4.2.3 建立地址映射

为了将虚拟地址映射到物理地址, 操作系统需要维护一个叫做页表 (Page Table) 的数据结构. 页表通常存储在内存中.

每一个页面对应一个页表项记录其虚拟地址到物理地址的映射关系. 32 位 CPU 的页表项大小为 4 字节, 64 位 CPU 的页表项大小为 8 字节. 页表大小等于页表项数与页表项大小的乘积.

4.2.4 地址翻译

当进程访问内存时, CPU 需要将虚拟地址转换为物理地址. 地址翻译的过程如下:

  • 首先, 从虚拟地址中提取页号和页内偏移;
  • 使用页号在页表中查找相应的物理页面. 如果找到了对应的映射关系, 那么从页表项中获取物理页号.
  • 将物理页号与页内偏移组合, 得到物理地址.
  • 访问物理地址对应的内存单元.

为了加速地址翻译过程, 通常使用硬件缓存 TLB (Translation Lookaside Buffer, 快表) 来缓存最近使用的页表项. 当虚拟地址到物理地址的转换在 TLB 中找到对应的映射关系时, 可以避免访问内存中的页表, 从而提高系统性能.

4.3 分段机制

与分页机制类似, 分段地址也分为段号和段内偏移两部分, 也同样具有段表.

4.4 虚拟内存管理

虚拟内存管理是一种内存管理技术, 通过将物理内存与磁盘存储结合, 为进程提供一个连续的虚拟地址空间. 虚拟内存允许操作系统在物理内存不足时, 将不常用的内存页面 (Page) 换出到磁盘上的交换空间 (Swap Space). 当进程需要访问被换出的页面时, 操作系统将其从磁盘加载回物理内存. 这种机制可以实现内存的按需分配, 提高内存利用率.

TLB 通常位于 CPU 内部, 存储了最近使用的部分页表项, 以便在地址转换时快速查找. 当 CPU 访问虚拟地址时, 首先在 TLB 中查找相应的物理地址. 如果 TLB 中存在对应的页表项 (TLB 命中), 则直接获取物理地址, 避免了访问主内存中的页表. 如果 TLB 未命中, 系统需要从内存中的页表查找相应的映射关系, 并将该页表项加载到 TLB 中, 以便后续访问时能够加速地址转换.

4.5 页面置换算法

当进程访问一个尚未加载到物理内存的页面时, 会触发缺页中断. 页面置换算法是操作系统在发生缺页中断时, 选择要换出的内存页面的方法.

  • 最佳页面置换算法: 选择未来最长时间不会被访问的页面进行置换.

  • 最近最少使用: 选择最近一段时间内最少被访问的页面进行置换.

  • 先进先出: 选择最早被加载到内存的页面进行置换.

  • 时钟页面置换算法: 类似于 FIFO, 但使用一个循环队列和页面访问位来模拟时钟指针.

4.6 内存保护技术

内存保护技术可以防止一个进程意外或恶意地访问其他进程的内存空间, 保护操作系统内核和关键数据结构. 常见的内存保护技术包括:

  1. 地址空间隔离

操作系统为每个进程分配独立的地址空间, 以确保进程之间的内存空间不会相互干扰. 现代操作系统通常使用虚拟内存技术实现地址空间隔离.

  1. 访问权限控制

操作系统为内存段或页设置访问权限, 如只读, 可读写等. 当一个进程试图违反访问权限时, 操作系统将产生一个异常, 防止非法访问.

  1. 内核空间和用户空间隔离

操作系统将内存空间划分为内核空间和用户空间. 内核空间用于存储操作系统内核和关键数据结构, 而用户空间用于存储用户程序和数据. 内核空间和用户空间之间的隔离可以防止用户程序意外或恶意地访问内核数据和资源.

  1. 内存保护单元 (MPU) 和内存管理单元 (MMU)

这些硬件组件可以协助操作系统实现内存保护, 例如地址转换, 访问权限检查等功能.

5 文件系统管理

5.1 文件系统的概念与组织

5.2 文件存储空间的管理

5.3 文件目录结构

5.4 文件操作与安全

6 设备管理

6.1 设备管理的概念与功能

6.2 设备驱动程序

6.3 设备调度策略

6.4 输入输出系统

7 用户界面

7.1 命令行界面

7.2 图形用户界面

8 系统调用

8.1 系统调用的概念与功能

8.2 常见的系统调用

标签:操作系统,队列,线程,内核,进程,OS,内存
From: https://www.cnblogs.com/Arcticus/p/17380709.html

相关文章

  • Dockerfile、常用和不常用命令、dockerfile构建一个djagno项目、docker私有仓库、镜像
    目录1Dockerfile1.1常用和不常用命令1.2dockerfile构建一个djagno项目2docker私有仓库2.1镜像传到官方仓库2.2镜像分层2.3私有仓库搭建3dockercompose介绍4dockercompose部署flask+redis项目4.1新建flask项目app.py4.2编写Dockerfile--》用于构建flask项目的镜像4.3......
  • 更新macOS系统后,使用gcc/g++命令,提示错误xcrun: error: invalid active developer pat
      更新macOS系统后,使用gcc/g++命令编译程序,提示错误xcrun:error:invalidactivedeveloperpath(/Library/Developer/CommandLineTools),missingxcrunat:/Library/Developer/CommandLineTools/usr/bin/xcrun解决方法:重新安装CommandLineTools,一般安装完成后问题就能......
  • docker 安装centos8
    1、安装基础镜像#dockersearchcentos  查询镜像列表#dockerpullcentos:centos8   拉取要安装的镜像# dockerimages  查询已下在的镜像 #  dockerrun-d-p5022:22--namecentos8--privileged=true5d0da3dc9764/usr/sbin/init  运行镜像......
  • 三重化buck/boost。 此拓补很适用于高压大功率场合,仿真功率设置为50kW,
    三重化buck/boost。此拓补很适用于高压大功率场合,仿真功率设置为50kW,高压侧电压为700V,低压侧电池电压为450V。采用电压电流双闭环控制,稳定输出电压。采用载波移相120°,平均电流采样,大大减小了电感电流的纹波和电感体积。在buck与boost两种模式动态切换过程中,没有发生过压与过流,且......
  • Kerberos协议原理
    本文主要介绍Kerberos认证协议的原理以及解决了什么问题Kerberos是什么Kerberos是计算机网络世界中的一种身份认证协议。身份认证是我们日常生活中经常进行的活动,比如我们要去银行取自己账户的钱,就必须先向银行证明你声明想要取钱的账户确实是你自己的。银行采取的认证方法是......
  • 操作系统实验-线程同步
    OS实验一:线程同步使用Windows提供的API线程接口实现。参考:C++创建线程示例,C++多线程,微软多线程编程文档,线程创建与撤销参数说明LPVOID是无类型指针,做形参可接收任意类型的指针VoidExitThread(DWORDdwExitCode)在线程函数内执行该线程的撤销,等价于内部的return。BoolTe......
  • 两级式光伏并网逆变器,DCDC环节采用boost电路,通过增量电导法实现光伏最大功率跟踪MPPT
    两级式光伏并网逆变器,DCDC环节采用boost电路,通过增量电导法实现光伏最大功率跟踪MPPT。逆变器采用二电平逆变器,通过双闭环控制,实现并网单位功率因数,并网电流与电网电压同相位,并网电流THD仅有1.3%,符合并网规范,并稳定直流侧母线电压。为了得到电网电网相位,采用基于双二阶广义积分器......
  • 将windows操作系统(win10)装入移动硬盘
    1.准备windows系统镜像比如我的iso镜像:zh-cn_windows_10_business_editions_version_22h2_updated_april_2023_x64_dvd_c03ed5aa.iso镜像挂载后可以看到关键文件 2.对移动硬盘进行分区 listdiskselectdiskncleanconvertgptselectpartition1deletepartiti......
  • 通过simulink搭建的三通道交错并联双向buck-boost变换器。
    通过simulink搭建的三通道交错并联双向buck-boost变换器。采用电压外环,三电流内环,载波移相120°的控制方式。在buck模式与boost模式互相切换之间,不会产生过压与过流。且交错并联的拓补结构,可以减少电感电流的纹波,减小每相电感的体积,提高电路的响应速度。该拓补可以用于储能系统中......
  • The connection to the server localhost:8080 was refused - did you specify the ri
    遇到如下问题:[root@k8s-node1~]#kubectlgetpodTheconnectiontotheserverlocalhost:8080wasrefused-didyouspecifytherighthostorport?解决方式:cd/etc/kubernetes/查看到有个文件:kubelet.conf(你们的有可能是admin.conf)执行命令echo"exportKUBECONFIG......