操作系统相关知识
进程与线程
1. **定义与关系**:进程是操作系统进行资源分配的最小单位,而线程则是CPU进行任务调度的最小单位。一个进程内部可以包含多个线程。进程和线程均描述了CPU的工作时间段,二者的区别在于颗粒度大小不同 。
2. **数据共享特性**:不同进程之间的数据共享较为困难,而同一进程下的不同线程间数据共享相对容易。
3. **资源占用与切换开销**:每个进程都拥有独立的代码和数据空间,因此相比线程,进程会消耗更多计算机资源。线程可视为轻量级的进程,同一类线程共享代码和数据空间,并且每个线程都具备独立的运行栈和程序计数器,这使得线程之间的切换开销较小。
4. **故障影响范围**:进程之间相互独立,不会相互影响;但一个线程出现故障,往往会导致整个进程崩溃。
5. **资源分配机制**:系统在运行时会为每个进程分配独立的内存空间;对于线程而言,除CPU资源外,系统不会专门为其分配内存(线程所使用的资源均来自所属进程),线程组之间仅能共享资源。
多线程与单线程
线程数量并非越多越好,其最优配置取决于具体的业务场景和硬件资源。在纯计算型(CPU密集型)业务且仅有一个CPU核心的情况下,单线程是最佳选择。因为额外增加线程会引入线程切换开销,导致CPU无法全力投入业务计算,反而降低CPU利用率(业务计算时间/总时间)。然而,在WEB等IO密集型业务场景中,单线程则无法充分发挥CPU性能。当一个线程处于IO等待时,将CPU资源分配给其他线程,能够有效提高CPU资源利用率。线程数量存在一个计算公式:最佳启动线程数=[任务执行时间/(任务执行时间-IO等待时间)]×CPU内核数。超出该数量,会因过多的线程切换浪费计算能力;低于该数量,则会使CPU因等待IO而无法充分利用计算能力。理想状态是CPU保持饱和运行,同时避免不必要的线程切换,此时服务性能达到最优,若再增加并发量,反而会导致性能下降。
进程的组成
进程主要由进程控制块(PCB)、程序段、数据段三部分构成。
进程的通信方式
1. 无名管道:属于半双工通信方式,数据仅能单向流动,常用于具有亲缘关系的进程间通信。其可视为特殊文件,支持使用普通的`read`、`write`等函数进行读写操作,但它不属于任何文件系统,仅存在于内存中。
2. FIFO命名管道:是一种特殊的文件类型,能够实现无关进程间的数据交换。与无名管道不同,FIFO具有与之关联的路径名,以特殊设备文件形式存在于文件系统中。
3. 消息队列:本质是存放在内核中的消息链接表,通过一个标识符(队列ID)进行标识。
4. 信号量:作为一个计数器,主要用于实现进程间的互斥与同步,而非用于存储进程间通信数据。
5. 共享内存:允许两个或多个进程共享同一块存储区,通常需要配合信号量使用,以确保数据访问的同步与安全。
进程间五种通信方式比较
1. 管道:通信速度较慢,容量有限,仅适用于父子进程间的通信。
2. FIFO:支持任意进程间通信,但通信速度相对较慢。
3. 消息队列:容量受系统限制,在首次读取时,需考虑是否存在上次未读完的数据。
4. 信号量:无法传递复杂消息,主要用于进程间的同步操作。
5. 共享内存区:易于控制容量,通信速度快,但需要特别注意数据同步问题,类似于线程安全机制。尽管共享内存区也可用于线程间通讯,但由于线程本身已共享同一进程内的内存空间,因此通常没有必要。
内存管理方式
1. 块式管理:将主存划分为较大的块,当程序片段不在主存时,会分配一整块主存空间将其载入。即便程序片段仅需少量字节,也需占用一整块空间,这导致内存空间浪费严重,平均浪费约50%,不过管理相对简单。
2. 页式管理:把主存划分为较小的页,相比块式管理,显著提高了内存空间利用率。
3. 段式管理:将主存进一步划分为更小的段,在空间利用率上比页式管理更优。然而,由于一个程序片段可能被分割为多个段,计算每段物理地址会消耗较多时间。
4. 段页式管理:融合了段式管理和页式管理的优势,先将程序划分为若干段,再将每个段划分为若干页。但在数据读取时,每获取一个数据需要访问3次内存。
页面置换算法
1. 最佳置换算法(OPT):该算法仅具有理论价值,主要用于评估其他页面置换算法。其置换策略是将当前页面中在未来最长时间内不会被访问的页替换出去。
2. 先进先出置换算法(FIFO):是一种较为简单直接的置换算法,未考虑页面访问频率,每次淘汰最早调入的页面。
3. 最近最久未使用算法(LRU):为每个页面设置访问字段,记录自上次访问以来所经历的时间t,每次置换时选择t值最大的页面(可通过寄存器或栈实现)。
4. 时钟算法(Clock,也称最近未使用算法NRU):为页面设置访问位,并将页面组织成环形队列。当页面被访问时,将访问位设置为1。在进行页面置换时,若当前指针指向页面的访问位为0,则进行置换;否则将其置为0,循环查找直至找到访问位为0的页面。
5. 改进型Clock算法:在Clock算法基础上增加修改位,置换时综合考虑访问位和修改位。优先置换访问位和修改位均为0的页面,其次是访问位为0且修改位为1的页面。
6. LFU最少使用算法:通过设置寄存器记录页面访问次数,每次置换时选择当前访问次数最少的页面。
操作系统中进程调度策略
1. 先来先服务调度算法(FCFS):基于队列实现,采用非抢占方式,按照进程请求CPU的先后顺序分配资源,既适用于作业调度,也适用于进程调度。该算法对长作业较为有利。
2. 最短作业优先调度算法(SJF):主要用于作业调度,从就绪队列中选取估计执行时间最短的作业进行处理,直至作业完成或无法继续执行。此算法可使平均等待时间最短,但难以准确预估下一个CPU区间长度,存在不利于长作业、未考虑作业重要性以及运行时间预估不准确等缺点 。
3. 优先级调度算法:支持抢占式和非抢占式两种模式,优先级高的进程优先获得CPU资源,相同优先级的进程按照先来先服务原则调度。该算法存在的主要问题是,低优先级进程可能因长期无法获取CPU资源而陷入无穷阻塞或饥饿状态。
4. 时间片轮转调度算法:属于可抢占式调度算法。将进程按到达顺序放入队列,为队首进程分配CPU时间片。当时间片耗尽时,计时器触发中断,暂停当前进程并将其移至队列尾部,循环执行。在该算法下,除非系统中仅有一个可运行进程,否则任何进程获得的CPU时间不会超过一个时间片,若进程的CPU执行区间超过一个时间片,将被抢占并放回就绪队列。
死锁的四个必要条件
1. 互斥条件:同一时刻,一个资源仅能被一个线程占用。
2. 请求与保持条件:线程因请求资源被阻塞时,不会释放已获得的资源。
3. 不剥夺条件:进程已获得的资源,在使用完毕前,不会被强行剥夺。
4. 循环等待条件:多个线程之间形成资源循环等待链,即每个线程都在等待其他线程释放其所需资源。
死锁的避免(预防)方法
1. 破坏“请求和保持”条件:
- 让进程在申请资源时一次性获取所有所需资源,若部分资源不可用,则进程进入等待状态。此方法可能导致资源浪费,使进程频繁处于饥饿状态。
- 要求进程在申请新资源前,先释放已持有的资源。
2. 破坏“不可抢占”条件:
- 当进程请求资源被拒时,主动释放自身已拥有的资源。
- 操作系统允许优先级高的进程抢占优先级低的进程的资源。
3. 破坏“循环等待”条件:对系统中的所有资源进行统一编号,进程可随时申请资源,但必须按照资源编号顺序进行申请,通过指定获取锁的顺序(顺序加锁)来避免循环等待。