🌲操作系统章节复习

🌟 第一章基础知识

  1. OS作为用户与计算机硬件系统之间的接口。OS处于用户和计算机硬件系统之间,用户通过OS来使用计算机系统。用户可以通过以下三种方式使用计算机:命令行;系统调用;图形化
  2. OS的定义:操作系统是一组控制和管理计算机硬件和软件资源,合理地对各类作业进行调度,以及方便用户使用的程序的集合
  3. 关于操作系统的比喻:魔术师,提供干净、易于使用的物理资源抽象,应用程序的“机器”是由操作系统提供的进程抽象,每个正在运行的程序都在它自己的进程中运行,进程提供了比原始硬件更好的接口。裁判,管理保护、隔离和资源共享,操作系统相互隔离各个过程,操作系统将自己与其他过程隔离开来,即使它们实际上是运行在相同的硬件上!胶水,常见的服务:存储,窗口系统,网络,共享和授权,外观和感觉
  4. 进程:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。进程是程序的一次执行,进程是一个程序及数据在处理机上顺序执行时所发生的活动。进程=地址空间+线程+与之关联的其他系统状态
  5. 操作系统的发展过程:无操作系统的计算机系统、单道批处理系统、多道批处理系统、分时系统、实时系统。多道批处理系统是操作系统成熟的标志。
  6. 分时系统的特征:多路性、独占性、及时性、交互性
  7. 操作系统的基本特征:并发性、共享性、虚拟性、异步性
  8. 共享:系统中的资源可供内存中多个并发执行的进程共同使用,相应地把这种资源共同使用称为资源共享或称为资源复用。
  9. 临界资源:在一段时间内只允许一个进程访问的资源
  10. 并发和共享是操作系统的两个最基本特征,他们又是互为存在的条件。
  11. 虚拟技术:操作系统中所谓的虚拟,是指通过某种技术把一个物理实体变为若干个逻辑上的对应物。物理实体是实的,即实际存在的,后者是虚的,是用户感觉上的东西。用于实现虚拟的技术称为虚拟技术。
  12. 操作系统的主要功能:(1)处理机管理功能:进程控制、进程同步、进程通信、调度(2)存储器管理功能:对内存进行分配、保护和扩充、地址映射 (3)设备管理功能:缓冲管理、设备分配、设备处理 (4)文件管理功能:文件存储空间的管理、目录管理、文件的读写管理和保护 (5)操作系统与用户之间的接口:用户接口和程序接口

🌟 第二章 进程、线程与文件系统

2.1 四个操作系统基本概念

  1. 线程:单个唯一执行上下文(PC寄存器、数据寄存器、执行标志、堆栈、内存状态)。当线程驻留在处理器寄存器中时,它是在处理器上执行的。当状态未被加载到处理器时,线程被挂起(不执行) ,处理器状态指向其他线程,程序计数器不指向此线程的下一个指令。线程是虚拟内核,时分复用,线程在真正的物理核心,或保存在内存块中(线程控制块TCB),TCB在线程不运行时保留寄存器的内容,TCB现在存储在内核
  2. 地址空间:程序可访问的一组内存地址(用于读取或写入)保护操作系统免受程序攻击:base&bound,重定位,地址空间转换,分页虚拟地址空间
  3. 进程:运行程序的实例(受保护的地址空间+一个或多个线程)权利受限的执行环境,具有一个或者多个线程,拥有内存、文件描述符、套接字,封装一个或多个线程共享过程资源,提供内存保护。线程封装并发,地址空间封装保护,并行性
  4. 双模式操作保护:只有系统才有能力访问某些资源 内核态/用户态 管态/目态。用户内核模式转换:系统调用fopen、中断(硬件触发,如时钟中断、键盘操作)、陷入或异常
  5. code:代码段,程序的可执行指令,编译后的二进制代码 static data:数据段,已初始化的全局变量和静态变量(如 int global_var = 42;)未初始化的全局/静态变量(BSS段,默认值为0,如 static int uninit_var;) heap:堆,通过malloc或者new等动态分配的内存,如运行时创建的数组、对象、数据结构 stack:栈,函数调用的上下文,局部变量、函数参数、返回地址等

2.2 并发进程与线程

虚拟地址空间

  1. 并发:多处理器:多个CPU(核心),多道:多个作业/进程,多线程:多个线程/进程
  2. 并发性是同时处理多件事情,并行是同时做多件事情
  3. 进程管理API:exit终止进程 fork复制当前进程 exec更改当前进程运行的程序 wait等待进程完成 kill向其他进程发送信号(中断式通知) sigaction为信号设置处理程序
  4. 基础代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
/* get current processes PID */
pid_t pid = getpid();
printf("My pid: %d\n", pid);

exit(0);
}
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
pid_t cpid, mypid;
pid_t pid = getpid(); /* get current processes PID */
printf("Parent pid: %d\n", pid);
cpid = fork();
if (cpid > 0) { /* Parent Process */
mypid = getpid();
printf("[%d] parent of [%d]\n", mypid, cpid);
} else if (cpid == 0) { /* Child Process */
mypid = getpid();
printf("[%d] child\n", mypid);
} else {
perror("Fork failed");
}
}
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

void signal_callback_handler(int signum) {
printf(“Caught signal!\n”);
exit(1);
}
int main() {
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = signal_callback_handler;
sigaction(SIGINT, &sa, NULL);
while (1) {}
}
  1. 线程掩盖了I/O延迟。

  2. 线程的三种状态:运行、就绪、阻塞

  3. 多线程程序:运行可执行程序将创建执行该程序的进程,最初新进程在自己的地址空间中只有一个线程。生成多线程程序的方法:调用pthread_create()创建新线程,指定线程函数和参数,使用pthread_join()等待线程结束,编译时需链接-lpthread库。

  4. pthread 是一套标准化的C语言线程操作函数库(如 pthread_create, pthread_join),允许程序在单进程内并发执行多个任务,共享进程资源但拥有独立执行流。

  5. pthread常用函数包括:

    1. pthread_create() - 创建新线程,需指定线程函数和参数;
    2. pthread_join() - 阻塞等待线程结束并回收资源;
    3. pthread_exit() - 线程主动退出,可返回结果;
    4. pthread_self() - 获取当前线程ID;
    5. 同步控制函数:
      • pthread_mutex_init()/lock()/unlock() - 互斥锁保护共享数据;
      • pthread_cond_wait()/signal() - 条件变量实现线程间通信;
    6. pthread_detach() - 设置线程为分离状态,终止后自动回收资源。

    :使用时需链接-lpthread库,并注意同步避免竞争条件。

  6. 竞争与锁:线程之间的协调,通常涉及共享数据。临界区:访问临界资源的代码,一个线程的临界区必须一次执行完,不能中断。

  7. 线程竞争(Race Condition)是指多个线程同时访问和修改同一共享资源时,由于执行顺序的不确定性导致程序结果出现错误或不可预测的行为。例如,两个线程同时对一个变量进行自增操作时,可能因线程切换导致部分修改丢失,最终结果不符合预期。锁(如互斥锁pthread_mutex_t是解决竞争的核心机制:它在共享资源被访问前加锁(确保同一时间仅一个线程能操作),操作完成后解锁(允许其他线程访问)。锁强制将并发操作转为串行,保证数据一致性,但过度使用可能降低性能。简单来说,竞争是“乱抢资源引发的混乱”,锁则是“排队使用资源的规则”。

int pthread_mutex_init(pthread_mutex_t *mutex,			     const pthread_mutexattr_t *attr)

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

2.3 文件与I/O

  1. 对于文件的抽象:在UNIX系统中,“万物皆文件” 是其核心设计哲学,此处的“文件”是一个高度抽象的概念,它远不止于磁盘上存储数据的普通文件。本质上,一切可以被读写操作(read/write)的资源都被抽象成了文件,并通过统一的文件描述符(File Descriptor) 接口进行访问。这包括:存储数据的普通文件(-)、提供进程间通信的管道(pipe, |)套接字(socket)、代表硬件设备的设备文件(如/dev/sda,分字符c和块设备b)、以及映射进内存的虚拟文件(如/proc目录下的文件,它们实际上是内核数据结构接口)。因此,无论是操作硬盘数据、键盘输入、屏幕输出,还是进行网络通信,在UNIX看来,都只是在用openreadwriteclose等相同的系统调用对不同的“文件”进行读写,这种极致抽象极大地简化了系统的设计和应用开发。
  2. POSIX I/O:POSIX I/O 是POSIX标准定义的一组文件输入/输出(Input/Output)系统调用,提供跨UNIX/Linux系统的统一文件操作接口。使用前open,面向字节,显式close
  3. 文件的缓冲机制:操作系统的文件缓冲机制 是一种提升I/O效率的核心技术,其核心思想是在高速的内存(RAM)中开辟一块区域作为缓冲区,作为慢速的外设(如硬盘)与高速的CPU之间的数据中转站。当进程写入数据时,并不直接写入磁盘,而是先复制到内存缓冲区,由操作系统在后台选择合适的时机(如缓冲区满或定期刷写)再统一写入磁盘,这能将多次零散的小写操作合并为一次大的顺序写操作,极大减少了耗时的磁盘访问次数;读取数据时,操作系统也会预读更多数据到缓冲区,使得后续的读取请求可以直接从内存获取,避免了真实的磁盘寻道。这种机制以少量的内存空间为代价,通过批处理和预读策略,有效地掩盖了I/O速度的不匹配问题,显著提升了系统整体性能,但代价是可能带来数据延迟写入的风险(如突然断电可能导致数据丢失)。
  4. 共享文件描述符是指多个进程(如父子进程通过fork()继承)或线程持有指向同一个内核文件表项的描述符编号,这意味着它们不仅共享对底层文件(如普通文件、套接字或管道)的访问权限,更重要的是共享文件的内部状态,尤其是当前读写偏移量。例如,若进程A通过lseek移动了文件指针,进程B接下来的读操作会从新的位置开始;同理,若两个进程同时写入一个共享描述符指向的文件,它们的输出数据会按写入顺序正确拼接,而不会相互覆盖。这种机制是实现进程间顺序协作通信(如管道)的基础,但也要求使用者注意并发操作的同步问题。

🌟 第三章 进程管理与进程调度

  1. 进程控制块(PCB) 是操作系统内核中为每个进程创建的一个核心数据结构,它如同进程的“身份证”和“档案袋”,完整地记录了进程的所有关键信息。当进程被创建时,内核就为其分配一个PCB;当进程被调度执行时,CPU的上下文(如寄存器值)被存入其PCB;当进程被切换下CPU时,其运行状态又从PCB中恢复。PCB中主要包含进程标识信息(如PID)处理器状态(如寄存器、程序计数器)进程控制信息(如状态、优先级) 以及资源清单(如打开的文件、内存分配)。因此,PCB是操作系统感知、管理和控制进程的唯一实体,是进程调度的直接依据。
  2. 三状态模型(运行、就绪、阻塞)—(+new +terminated)—-> 五状态模型 —-(+suspend)—>七状态模型。

五状态模型

七状态模型

  1. 操作系统的上下文切换-调度循环是内核在多进程间实现并发执行的核心机制。其本质是一个“保存-切换-恢复”的循环:当定时中断发生或运行中进程主动放弃CPU时,调度器会首先将当前进程的执行现场(包括程序计数器、寄存器等上下文)保存到其进程控制块(PCB,在内存中)中,并将其状态置为就绪或阻塞;随后,调度算法从就绪队列中选择一个最应运行的进程,并将其PCB中保存的上下文重新加载到CPU寄存器中;最后,CPU开始执行新进程。这个过程循环往复,通过快速轮转,在用户看来便形成了多个进程“同时”运行的假象。整个循环完全由内核在背后驱动,对进程透明,是实现多任务并发的基石。

  2. 开销:(1)只有一个CPU核心,非并行 切换开销: 同进程: 低 不同进程: 高 数据保护 同进程: 低 不同进程: 高 共享数据开销 同进程: 低 不同进程: 高 。(2)多个CPU核心,并行 切换开销: 同进程: 低 不同进程: 高 数据保护 同进程: 低 不同进程: 高 共享数据开销 同进程: 低 不同进程同核心:中 不同进程不同核心: 高 (cache)

  3. 内部事件源于当前正在CPU上执行的进程自身的动作,而外部事件则源于与该进程无关的系统活动。内部事件:系统调用如fopen、发生异常、主动让出CPU。外部事件:时钟中断、I/O中断。

  4. 进程调度主要是队列操作,调度算法从就绪队列中选取线程。

  5. CPU进程调度:通过多道程序设计得到CPU的最高利用率。CPU调度决策可能发生在:进程从运行切换到等待,进程从运行切换到就绪,进程从等待切换到就绪,进程终止。14非抢占,23抢占。调度延时:调度程序终止一个进程的运行并并启动另一个进程运行所花的时间

  6. 调度算法优化准则:最大的CPU利用率,最大的吞吐量,最短的周转时间,最短的等待时间,最短的响应时间。

  7. 调度算法:先来先服务FCFS、短作业优先SJF、最短剩余时间优先(指数平均数)、轮转法、优先级调度、多级队列、多级反馈队列、最早截止时间优先、比例分享调度、可移植操作系统接口(POSIX)实时调度

最短剩余时间优先

  1. 操作系统调度算法详解
调度算法 核心思想 调度依据 优点 缺点 适用场景
先来先服务 (FCFS) 像排队一样,先到的进程先接受服务。 进程到达就绪队列的时间。 简单、公平、实现简单。 ** convoy效应 **:短进程可能因等待长进程而耗时很久;平均等待时间较长。 早期批处理系统;现已很少作为主要算法。
短作业优先 (SJF) 优先运行预计执行时间最短的进程,以使平均等待时间最小。 进程的(预估)下一个CPU执行区间长度。 理论上的最优平均等待时间(针对非抢占式)。 可能导致长进程饥饿;需要预知未来(执行时间难以准确预估)。 理论模型;适用于能较准确估计运行时间的批作业。
最短剩余时间优先 (SRTN) SJF的抢占式版本。当新进程就绪时,会与当前进程的剩余时间比较,运行剩余时间最短的。 进程的剩余执行时间。 比SJF有更优的平均周转时间。 实现复杂;需要预知总执行时间;可能造成长进程饥饿。 理论价值高,实际因“预知时间”问题而较少使用。
轮转法 (RR) 为每个进程分配一个固定的时间片,时间片用完则强行切换,进程回到就绪队列末尾。 时间片是否用完。 公平性强,响应时间有保障,适用于分时系统。 时间片设置关键:太长退化为FCFS;太短切换开销过大。平均等待时间较长。 交互式系统的经典算法(如Unix/Linux的默认调度)。
优先级调度 每个进程拥有一个优先级,调度器总是选择优先级最高的进程运行。 进程的优先级(静态或动态)。 能区分任务紧急程度,灵活。 可能导致低优先级进程饥饿;静态优先级不够灵活。 广泛用于各种系统,常与其他算法(如RR)结合使用。
多级队列 (MQ) 将就绪队列划分为多个独立队列,每个队列可拥有自己的调度算法(如系统进程用RR,交互进程用高优先级)。 进程的类型(系统、交互、批处理等)。 根据进程类型区别对待,策略灵活。 队列间调度不灵活(可能存在队列饥饿);需要预先分类。 用于对任务类型有清晰划分的系统。
多级反馈队列 (MLFQ) MQ的改进版,是实际系统中最常用的算法之一。允许进程在队列间移动(如未在时间片内完成则降低优先级)。 进程已执行的时间和历史行为。 结合了RR和优先级调度的优点:短作业优先完成,交互式任务响应快,长作业也不会完全饥饿 参数(队列数量、时间片大小、优先级调整规则)设置复杂,影响性能。 通用操作系统的核心调度算法(如Windows NT、Unix System V)。
最早截止时间优先 (EDF) 优先运行截止时间最早的任务。是动态优先级调度。 任务的绝对截止时间。 理论最优的动态优先级实时调度算法(CPU利用率可达100%)。 进程超载时系统行为难以预测;实现复杂。 软实时系统(如多媒体播放)。
比例分享调度 目标是让每个进程获得其预先定义的CPU时间份额(如A占50%,B占30%)。 进程已获得的CPU时间与其应得份额的差距。 能提供精确的QoS(服务质量)保证。 需要额外的机制(如彩票、步幅)来跟踪和决策。 需要保证特定应用性能的场景(如虚拟化、云平台)。
POSIX实时调度 这是一套API标准,定义了如何控制调度策略,而非具体算法。主要包括: 1. SCHED_FIFO: 类似FCFS,但基于静态优先级,可抢占低优先级任务,无限时间运行。 2. SCHED_RR: 类似SCHED_FIFO,但增加了时间片,同优先级任务按轮转法执行。 3. SCHED_OTHER: 指系统默认的非实时调度策略(如Linux的CFS)。 由程序员通过API设置的静态优先级和策略。 为实时应用提供可预测性和确定性 需要程序员负责正确设置优先级,否则高优先级任务可能霸占CPU。 硬实时和软实时应用(如工业控制、机器人、航空电子)。
  1. 算法评估确定性评价:对于每个算法计算最小平均等待时间。随机过程的排队模型,下一个人将在x时间内到达。概率模型,不是精确值,而是均值