本文最后更新于377 天前,其中的信息可能已经过时,如有错误请联系作者
操作系统知识点记录
进程由什么组成?
进程控制块 PCB,这是进程存在的唯一标识,是提供给操作系统用来管理进程的数据结构,所有管理进程所需的信息都存在pcb中,创建和撤销进程也是对PCB进行操作。
PCB包含如下几个部分
- 进程描述信息:包括进程标识符(PID),它是进程的唯一身份证号,用于区分不同的进程。
- 进程状态:指示进程当前的状态,如新建(new)、就绪(ready)、运行(running)、等待(waiting)、阻塞(blocked)等。
- 进程优先级:用于进程调度时的优先级,决定进程获得CPU的时间。
- 资源分配清单:记录进程所分配的资源,如内存大小、正在使用的I/O设备、打开的文件列表等。
- CPU相关寄存器信息:当进程不在运行状态时,其寄存器的内容需要被保存,以便进程下次被调度时能够恢复现场,继续执行。
- 进程控制信息:包括进程的家族关系(如父进程ID)、进程组ID、用户ID等。
- 地址空间:进程的虚拟地址空间,包括代码段、数据段、堆、栈和其他内存区域。
进程的上下文
- 用户级上下文:这包括了进程的地址空间信息,如代码段、数据段、堆和栈等,以及全局变量和进程相关的用户空间内存。这些信息对于进程的运行是必需的,但它们不涉及到操作系统的核心功能。
- 系统级上下文:也称为内核级上下文,它包含了进程在内核中的状态信息,如进程控制块(PCB)、内核栈、进程标识信息、进程现场信息(如寄存器值)和进程控制信息。简而言之就是PCB和内核栈的信息构成的。
- CPU上下文:这是系统级上下文的一部分,特指进程现场信息中与CPU硬件相关的部分,包括所有CPU寄存器的内容。这些寄存器包括程序计数器(PC)、指令寄存器、通用寄存器、状态寄存器等,它们决定了CPU在执行指令时的状态。
进程的上下文切换
- 主动切换:
- 系统调用:当进程需要操作系统提供的服务(如文件操作、网络通信等)时,它会执行一个系统调用,导致CPU从用户模式切换到内核模式,此时操作系统接管CPU,执行内核代码来提供所需服务。
- 软中断:软件中断是程序主动触发的,如进行系统调用、发送信号等。
- 被动切换:
- 时间中断:当进程的时间片用尽或者有更高优先级的进程变为就绪状态时,操作系统会通过时间中断来抢占当前进程的CPU时间,并切换到其他进程。
在进行上下文切换时,当前进程的执行环境(包括CPU寄存器的值、程序计数器等)需要被保存,以便于将来能够恢复该进程的执行。这些信息通常保存在一个叫做进程控制块(PCB)的数据结构中。具体来说,上下文信息实际上是存储在PCB中的栈指针所指向的内核栈里。每个进程都有自己的内核栈,用于存储其运行时的内核态上下文信息。
线程的上下文
线程是进程中的一个执行流,它们共享进程的虚拟地址空间和资源,但拥有自己的栈和寄存器状态。
线程的上下文切换
- 线程间的上下文切换:如果两个线程属于同一个进程,它们的切换通常比进程间的切换开销小,因为它们共享相同的地址空间和资源。在这种情况下,切换时不需要改变虚拟内存等共享资源,只需要切换线程的私有数据(如线程栈、寄存器状态等)。
- 线程与进程间的上下文切换:如果线程属于不同的进程,那么线程的上下文切换就类似于进程的上下文切换,因为它涉及到不同地址空间和资源的变化。
进程与线程
进程与线程的区别
- 资源分配与调度:
- 进程是操作系统进行资源分配和调度的一个独立单位。每个进程都有自己的虚拟地址空间、打开的文件列表、安全属性等资源。
- 线程是CPU调度的基本单位。线程自己不拥有资源,它共享所属进程的资源,如虚拟地址空间、打开的文件等。
- 独立性:
- 进程具有独立性,一个进程的崩溃不会直接影响其他进程。
- 线程是进程的一部分,一个线程的崩溃可能会导致整个进程的崩溃。
- 开销:
- 进程的创建、终止和切换开销较大,因为涉及到资源的分配和释放,以及地址空间和上下文的切换。
- 线程的创建、终止和切换开销较小,因为线程共享了大部分资源,只需维护少量的私有数据。
- 数据共享与通信:
- 进程间数据共享复杂,通常需要通过进程间通信(IPC)机制来实现。
- 线程间数据共享简单,因为它们共享同一进程的内存空间,可以直接读写共同的数据。
线程相比进程的优势:
- 创建和终止速度:线程的创建和终止时间比进程快,因为它们不需要像进程那样进行资源的分配和释放。
- 切换效率:同一进程内的线程切换比进程切换快,因为不需要切换页表等内存管理信息。
- 数据交互:线程间数据传递不需要经过内核,效率更高。
进程与线程的切换过程:
- 保存当前执行上下文:当操作系统决定切换进程或线程时,首先需要保存当前执行进程或线程的硬件上下文,这包括CPU寄存器的值,如程序计数器(PC)、指令寄存器、通用寄存器、状态寄存器等。
- 更新当前进程控制块(PCB):将当前进程或线程的状态更新为就绪或阻塞,并将其加入相应的队列中。如果是进程切换,当前进程的PCB会被更新;如果是线程切换,当前线程的控制块会被更新。
- 调度新的进程或线程:操作系统根据调度算法选择一个新的进程或线程来执行。这个选择可能是基于优先级、时间片或其他调度策略。
- 更新被调度进程或线程的PCB:将新选中的进程或线程的状态更新为运行状态,并将其从就绪队列中移除。
- 更新存储管理数据:如果是进程切换,需要更新虚拟内存的映射,这涉及到页表、缓存(如TLB)和其他与存储相关的硬件状态。线程切换通常不需要这种更新,因为它们共享同一进程的地址空间。
- 恢复被调度进程或线程的硬件上下文:将之前保存的硬件上下文恢复到CPU的寄存器中,这样CPU就可以从上次中断的地方继续执行新的进程或线程。
进程切换为什么比线程切换要慢呢?
- 虚拟地址与物理地址:
- 物理地址是实际内存中的地址,直接寻址方式容易导致进程间相互干扰,因此操作系统引入了虚拟地址空间。
- 虚拟地址空间为每个进程提供了一个独立的地址空间,包括栈、堆、代码段等,这些地址是虚拟的,不直接对应物理内存。
- 地址转换:
- CPU通过内存管理单元(MMU)和内存中的页表来实现虚拟地址到物理地址的转换。
- 页表存储了虚拟地址到物理地址的映射,但频繁访问页表会导致性能瓶颈。
- 转换检测缓冲区(TLB):
- 为了提高地址转换的效率,引入了TLB,它是一个小型的缓存,存储了最近使用的地址映射。
- TLB位于CPU的MMU中,访问速度很快,可以减少对内存中页表的访问。
- 进程切换与TLB失效:
- 当进行进程切换时,由于每个进程有自己的虚拟地址空间和页表,切换进程会导致TLB中的内容失效。
- TLB失效后,新进程的地址转换需要重新访问内存中的页表,这增加了地址转换的成本,导致进程切换后的运行速度变慢。
- 线程切换:
- 线程是进程内的一个执行流,多个线程共享同一进程的虚拟地址空间。
- 因此,线程切换通常不涉及虚拟地址空间的切换,TLB中的内容仍然有效,地址转换的效率较高。
进程通信方式
- 管道通信:
- 管道是一种半双工的通信方式,数据在管道中按照先进先出的原则传输。
- 无名管道:仅能在有亲缘关系的进程间通信,如父子进程。
- 有名管道:可以在任意两个进程间通信,通过文件系统中的命名管道实现。
- 消息队列:
- 消息队列是存储在内核中的消息链表,允许一个或多个进程写入或读取消息。
- 消息队列可以实现消息的随机查询,不必按照先进先出的顺序读取。
- 消息队列中的消息会一直存在,直到操作系统关闭或队列被释放。
- 适合频繁的数据传输,但消息体和队列长度都有最大长度限制。
- 共享内存:
- 共享内存允许多个进程共享一段内存空间,是最快的IPC方式,因为数据不需要在用户态和内核态之间拷贝。
- 进程间可以直接读写内存中的数据,适用于大量数据的传输。
- 信号量:
- 信号量是一种同步机制,用于在多个进程间同步对共享资源的访问。
- 通过P操作(减信号量)和V操作(加信号量)来实现进程间的互斥和同步。
- 信号:
- 这是进程通信中唯一的异步通信机制,可在任何时候发信号给某个进程,不需要知道该进程的状态。
- 比如我们可以发送一个信号让进程终止,或者让其停止但并不结束。同时进程也可以对信号做出一些操作,比如执行该信号,或者执行为该信号定义的特定的函数,或者忽略某个信号,但不能忽略终止信号。
用户态和内核态的区别
- 权限级别:
- 用户态:用户程序运行的态别,权限较低。在这个状态下,程序不能直接访问硬件资源或引用底层的操作系统功能。
- 内核态:操作系统运行的态别,权限较高。在这个状态下,程序可以执行所有机器指令,包括那些可能影响其他程序或系统稳定性的指令。
- 执行环境:
- 用户态:程序在用户分配的内存空间中运行,受到内存保护机制的限制,以防止其干扰其他程序或操作系统。
- 内核态:程序在操作系统的内存空间中运行,可以直接访问所有的内存空间和硬件资源。
- 执行效率:
- 用户态:由于权限限制,某些操作需要通过系统调用切换到内核态来完成,这会增加额外的开销。
- 内核态:由于没有权限限制,操作系统的核心功能可以高效执行。
- 系统调用:
- 用户态:当需要执行某些特权操作(如文件操作、网络通信等)时,程序必须通过系统调用来切换到内核态。
- 内核态:操作系统可以直接执行所有的系统调用,并处理中断和异常。
- 错误影响:
- 用户态:程序错误通常只会影响到该程序自身,不会导致整个系统崩溃。
- 内核态:内核中的错误可能导致整个系统不稳定甚至崩溃,因为内核直接控制着系统的资源和硬件。
- 上下文切换:
- 用户态:进程在用户态和内核态之间的切换需要保存和恢复大量的上下文信息,这会增加CPU的负担。
- 内核态:内核态的程序在执行时不需要频繁地进行上下文切换,因此在内核态运行的代码通常更高效。
系统调用
系统调用的原因
- 保护内核空间:系统调用确保用户程序不能直接访问内核空间,防止恶意或错误的代码损害操作系统的稳定性。
- 统一接口:系统调用提供了一致的接口,隐藏了底层硬件和操作系统的复杂性,方便程序员开发应用程序。
- 资源共享:通过系统调用,操作系统可以管理多个程序对共享资源(如文件、内存)的访问,确保资源的合理分配和同步。
- 提高效率:系统调用可以优化资源的使用,例如通过缓冲和缓存机制提高I/O操作的效率。
系统调用的流程
- 调用过程:用户程序通过调用函数库中的API(应用程序编程接口)来发起系统调用。这些API通常是操作系统提供的标准函数。
- 中断触发:API的实现会触发一个软件中断(如x86体系结构中的
int 0x80
指令)或特定的指令(如ARM体系结构中的svc
指令),将控制权交给内核。 - 中断处理:发生中断后,CPU从用户态切换到内核态,并根据中断号查找中断向量表,找到对应的中断处理程序,即系统调用的具体实现。
- 执行内核代码:内核执行系统调用的代码,完成用户请求的操作,如读写文件、创建进程等。
- 返回结果:系统调用完成后,内核将结果返回给用户程序,并通过中断返回指令从内核态切换回用户态,用户程序继续执行。
系统调用与进程/线程切换:
- 系统调用本身不一定会引起进程或线程的切换,但它会引起CPU从用户态到内核态的切换,即中断上下文切换。
- 如果系统调用导致当前进程被阻塞(如等待I/O完成),则可能会触发调度器进行进程切换,以便其他进程可以使用CPU。
- 在多线程环境中,系统调用可能引起线程的切换,尤其是当系统调用涉及到线程同步或需要访问其他线程的资源时。
进程调度算法
- 先来先服务(FCFS):
- 非抢占式算法,按照请求CPU的顺序进行调度。
- 优点是简单,缺点是可能导致短作业在长作业后面等待时间过长,这种现象称为“饥饿”。
- 最短作业优先(SJF):
- 非抢占式算法,选择预计运行时间最短的进程。
- 优点是减少平均等待时间,缺点是长作业可能一直等待,导致“饥饿”。
- 最高响应比优先(HRRN):
- 非抢占式算法,根据响应比(等待时间加上要求服务时间除以要求服务时间)进行调度。
- 优点是平衡了长短作业,减少了饥饿现象。
- 时间片轮转(RR):
- 抢占式算法,每个进程分配一个固定的时间片。
- 优点是公平,缺点是时间片大小难以确定,太短导致频繁的上下文切换,太长则可能导致响应时间变长。
- 最高优先级调度:
- 根据进程的优先级进行调度,可以是抢占式或非抢占式。
- 优先级可以是静态的或动态的,动态优先级可以根据进程的行为调整优先级,以避免饥饿。
- 多级反馈队列(MFQ):
- 结合了时间片轮转和最高优先级调度,设置多个队列,每个队列有不同的优先级和时间片。
- 优点是兼顾了长短作业,缺点是实现复杂。
磁盘调度算法
- 先来先服务(FCFS):
- 按照请求到达的顺序服务。
- 简单但可能导致磁头频繁移动,造成较差的性能,尤其是当请求分布不均匀时。
- 最短寻道时间优先(SSTF):
- 选择距离当前磁头位置最近的请求先服务。
- 减少了寻道时间,但可能导致某些请求长时间得不到服务,即“饥饿”。
- 扫描(SCAN):
- 磁头从一个方向移动到另一个方向,服务路径上的所有请求。
- 在移动方向上不会有“饥饿”,但可能导致另一方向的请求等待。
- 循环扫描(C-SCAN):
- 类似于SCAN,但当磁头到达磁盘的一端时,它会快速移动到另一端,而不是反向移动。
- 减少了反向移动的时间,但可能导致一端请求的响应时间较长。
- LOOK:
- 类似于SCAN,但磁头只在有请求的区域内移动,到达端点时立即改变方向。
- 减少了不必要的寻道,提高了效率。
- C-LOOK:
- 类似于LOOK,但当磁头到达磁盘的一端时,它会快速移动到另一端的起始请求。
- 进一步减少了不必要的寻道。
操作系统如何管理虚拟地址与物理地址的关系
内存分段
- 虚拟地址与物理地址的映射:通过段表实现。虚拟地址由段选择因子(段号)和段内偏移量组成,段选择因子指向段表,段表中包含段基地址等信息,通过基地址加上偏移量得到物理地址。
- 优点:逻辑上更符合程序的逻辑结构,支持变长内存分配。
- 缺点:容易产生外部碎片,内存交换效率低。
内存分页
- 虚拟地址与物理地址的映射:通过页表实现。虚拟地址由页号和页内偏移量组成,页号作为页表的索引,查找对应的物理页号,再通过偏移量得到物理地址。
- 优点:减少外部碎片,提高内存交换效率,允许非连续内存空间的分配。
- 缺点:可能产生内部碎片,需要更复杂的页表管理。
多级页表
- 解决空间占用问题:通过将页表分为多级,减少每个进程需要管理的页表项数量,从而减少内存占用。
- 优化内存使用:只创建实际需要的页表项,未使用的页表不占用物理内存。
块表TLB
- 提高访问速度:TLB(转换检测缓冲区)是一个高速缓存,存储最近访问的页表项,减少了对内存中页表的访问次数。
- 优化性能:TLB的引入显著提高了虚拟地址到物理地址的转换速度。
段页式内存管理
- 结合分段和分页:先将程序划分成逻辑段,再将每个段划分为固定大小的页。
- 虚拟地址组成:虚拟地址由段号、段内页号和页内偏移组成。
- 映射过程:通过段号访问段表得到页表起始地址,再通过起始地址加上偏移量找到物理地址。
页表项
页表项通常包含以下几个字段:
- 状态位(有效位):指示该页是否被加载到物理内存中。如果状态位为1,表示有效,页在内存中;如果为0,表示无效,页不在内存中,可能被换出到硬盘上。
- 访问字段:记录页在一段时间内的访问次数或最后访问时间,这个信息对于页面置换算法(如LRU)在选择换出哪个页时非常重要。
- 修改位(脏位):表明自页被加载到内存后是否被修改过。如果页未被修改(干净),在置换时可以不写回硬盘,减少I/O操作;如果页被修改过(脏页),则需要将其写回到硬盘上,以保持数据的一致性。
- 硬盘地址(物理块号或页框号):指出该页在硬盘上的存储位置,当需要将该页调入内存时,操作系统会根据这个地址去读取数据。
页面置换算法
- 最佳页面置换算法(OPT):理论上的最优算法,但由于它需要知道未来的访问模式,这在实际中是不可能的,所以它通常用来作为其他算法性能比较的基准。
- 先进先出算法(FIFO):实现简单,但可能会出现Belady异常,即增加内存页数时缺页率反而上升的情况,因为它不考虑页面的使用频率和重要性。
- 最近最久未使用置换算法(LRU):依据最近访问的情况来推测未来访问的可能性,实现较为复杂,性能通常优于FIFO,但需要维护一个复杂的链表,可能导致较高的CPU开销。
- 时钟页面置换算法(Clock):是LRU的一个近似实现,通过使用访问位和修改位来简化页面的替换过程,减少CPU开销,性能接近LRU。
- 最少使用算法(LFU):基于页面访问频率的算法,通过计数器记录页面访问次数,但可能造成某些页面即使在后期不被访问时也一直留在内存中,因此可能需要定期减少计数器的值来适应访问模式的变化。