当前位置:   article > 正文

【八股】操作系统八股速记版

操作系统八股

1. 进程和线程的区别

进程是操作系统资源分配和系统调度的基本单位。
线程是操作系统系统调度的最小单位。线程是进程的子任务,一个进程继续包含线程,一个进程可以处理多个线程,这些线程之间共享同一片内存块。

  • 资源开销:
    • 进程: 由于每个进程都有独立的内存空间,创建和销毁进程的消耗较大。进程间切换需要保存和恢复整个进程的状态,因此上下文切换的开销较高。
    • 线程:由于线程共享相同的内存空间,因此创建和销毁线程的开销较小。线程间切换只需要保存和恢复少量的线程上下文,因此上下文切换的开销较小
  • 通信与同步:
    • 由于进程间是相互隔离的,所以进程之间的通信需要一些特殊机制,例如管道、消息队列、共享内存等。
    • 由于线程共享相同的内存空间,因此它们之间可以直接访问共享数据,线程间通信更加方便。
  • 安全性:
    • 进程 :由于进程间相互隔离,一个进程的崩溃不会直接影响其他进程的稳定性
    • 线程 :由于线程共享相同的内存空间,一个线程的错误可能会影响整个进程的稳定性。

2. 进程调度算法

进程调度算法是操作系统中用来管理和调度进程(也称为任务或作业)执行的方法。这些算法决定了在多任务环境下,系统如何为每个进程分配CPU时间,以实现公平性、高吞吐量、低延迟等不同的调度目标。

  1. 先来先服务调度算法: 按照进程到达的先后时间进行调度,即最早到达的进程先执行,直到完成或阻塞
  2. 最短作业有限调度算法:优先选择作业时间最短的进程来运行
  3. 高响应比优先调度算法:综合考虑等待时间和服务时间的比率,选择具有最高响应比的进程来执行
  4. 时间片轮转调度算法:将CPU时间划分为时间片,每个进程在一个时间片内运行,然后切换到下一个进程
  5. 最高优先级调度算法:为每个进程分配一个优先级,优先级较高的进程先执行。这可能导致低优先级进程长时间等待,可能引发饥饿问题。
  6. 多级反馈队列调度算法:将进程划分为多个队列,每个队列具有不同的优先级,进程在队列之间移动。具有最高优先级队列的进程会更早执行,而长时间等待的进程会被提升到最高优先级队列。
  7. 最短剩余时间优先算法:每次选择剩余执行时间最短的进程来执行
  8. 最大吞吐量调度:旨在最大化单位时间内完成的进程数量

3. 进程间有哪些通信方式

  1. 管道: 是一种半双工的通信方式,数据只能单向流动且只允许父子进程之间通信。
  2. 命名管道:也是一种半双工的通信方式,但是它允许无亲缘关系的进程间通信。
  3. 信号量:是一个计数器,主要用来控制多个进程对共享资源的访问,常作为一种锁机制。用来控制当一个进程访问共享资源时其他进程对该共享资源的访问,主要作为不同进程之间以及同一进程内不同线程之间同步的一种手段。
  4. 消息队列:消息队列是消息的链表,主要存在内核区并由消息队列标识符标识。消息队列克服了信号量传递信号少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  5. 信号:用于通知接收进程某个时间已发生,从而迫使进程执行信号处理程序。
  6. 共享内存:就是映射一段能够被其他进程所访问的内存。 这段内存由一个进程创建,但可以被其他进程所使用。共享内存是进程间通信的最快方式,它就是为了解决其他进程间通信方式效率低才被创建出来的。它往往与其他通信机制,比如信号量配合使用,来实现进程间的通信与同步。
  7. Socket套接字:是支持TCP/IP的网络通信的基本单元,主要用于在客户端和服务器之间通过网络进行通信。

4. 什么是死锁, 如何避免死锁

死锁是指两个或多个资源在争夺系统资源时,由于互相等待对方释放的资源而无法继续执行的状态。
死锁的条件(四个条件必须同时满足才能发生死锁):

  1. 互斥:当一个进程占有某个资源时,其他进程不可以同时占有该资源
  2. 请求保持:当一个线程因为等待某个资源而阻塞时,它不会释放它所占有的资源
  3. 不可剥夺:不可以强制剥夺进程持有的资源,除非进程自愿释放
  4. 循环等待:多个进程之间形成一个循环等待资源的链,每个进程都在等待下一个进程所占有的资源

只需要破坏上面一个条件就可以避免死锁

  1. 破坏请求保持条件:一次性申请所有资源
  2. 破坏不可剥夺条件:占有资源的线程进一步申请其他资源时,如果申请不到可以主动释放它所占有的资源
  3. 破坏循环等待条件:按序申请资源。所有进程都按照同样的顺序申请资源,释放资源时则反过来。

5. 什么是虚拟内存, 为什么需要虚拟内存

虚拟内存在每一个进程创建加载过程中,为进程分配一段连续的地址空间,这个地址空间不是真实存在的,而是通过映射使得虚拟内存与实际地址空间对应。使得每一个进程都看似拥有一个独立的连续内存空间,并允许程序可以访问比实际物理内存RAM更大的空间,使得每一个程序都认为自己有足够的内存来运行。

  1. 内存扩展:虚拟内存使得每一个程序可以使用比实际内存更多的内存,从而允许运行更大的程序或处理更多的数据
  2. 内存隔离:虚拟内存还提供了进程之间的内存隔离,每一个进程有自己独立的虚拟内存空间,因此一个进程无法直接访问另一个进程的内存
  3. 物理内存管理:虚拟内存允许操作系统动态地将数据和程序的部分加载到物理内存中,以满足当前正在运行的进程的内存需要。当物理内存不足时,操作系统可以将不常用的数据或程序暂时移动到硬盘上,从而释放内存,以便其他进程使用。
  4. 页面交换:当物理内存不足时,操作系统可以将一部分数据从物理内存写入到硬盘的虚拟内存中,这个过程称为页面交换。当需要时,数据可以再次从虚拟内存中加载到物理内存中。这样可以保证系统可以继续运行,尽管物理内存有限。
  5. 内存映射文件:虚拟内存还可以用在将文件映射到内存中,这使得文件的读取和写入都可以像访问内存一样高效。

6. 什么是内存分页和分段,作用是什么

概念:

内存分段是指把一个程序的内部空间分成不同大小的逻辑段,每一个段代表程序的一个功能模块或数据类型,比如代码段、栈段、数据段等,这些段之间都有自己的大小和权限。
内存分页是指把整个虚拟内存和物理内存分成固定大小的页,这些页是连续且尺寸固定的内存空间。

作用:

  1. 逻辑隔离:内存分页和分段都实现了逻辑隔离,使不同功能的模块或数据类型能够被单独管理和保护,提高了程序的可靠性和安全性
  2. 内存保护:通过将不用的段或页面设置为可读、可读写、不可执行等权限,操作系统可以确保程序不会月结访问或修改其他段的内容,从而提高了系统的稳定性。
  3. 虚拟内存:分页和分段都有助于实现虚拟内存的概念,允许应用程序认为它们在使用的是一个比实际物理内存更大的内存空间。
  4. 内存共享:通过分页,操作系统可以实现内存页面的共享,从而节省内存空间,多个进程可以共享相同的代码或数据页面。
  5. 内存管理:分页更加灵活,允许操作系统将不同进程的页面分散存放在物理内存中,从而提高内存利用率。分段则是更适用于管理不同的逻辑模块。

分段与分页的区别:

  1. 分页对用户是不可见的,分段对用户是可见的
  2. 分页的地址是一维的,分段的地址是二维的
  3. 分页、分段访问一个逻辑地址都需要两次访存,分段可以使引入快表机制
  4. 分段更容易实现信息的共享和保护

分段与分页的优缺点:

分页: 内存空间利用率高。不会产生外部碎片,只会产生少量的页内碎片。但是不方便按照逻辑对内存进行分块,不方便进行信息的共享和保护。
分段:方便按照逻辑实现对信息的共享和保护,但是如果分段过大,会很难分配连续的地址空间,容易产生外部碎片。

7. 用户态和核心态

用户态和核心态是操作系统中两种不同的执行模式,用于控制进程或程序访问硬件资源的权限和操作范围。
用户态:
在用户态下,进程或程序只能访问受限的资源和受限的操作集合,不能访问操作系统的核心,也不能访问硬件资源,CPU不是独占的,也就是说CPU可以被其他进程抢占。

核心态:
核心态是操作系统的特权级别,允许进程或程序执行特权指令和访问操作系统的核心部分。在核心态下,进程可以直接访问硬件资源,执行系统调用,管理内存、文件系统等操作。处于内核态的CPU可以从一个程序切换到另外一个程序,并且占用CPU不会发生抢占情况,一般处于特权级0的状态我们称之为内核态。

8. 页面置换算法(LRU、FIFO等)

当页面发生缺页中断时,如果物理内存中没有空闲的物理页面可用的话,操作系统就必须将物理内存中的一个页面淘汰出去,这样就可以腾出新的空间来加载新的页面了。用来选择淘汰哪一个物理页的规则叫做页面置换算法。页缺失太频繁会非常影响性能,一个好的页面置换算法应该是可以减少页缺失出现的次数。
常⻅⻚⾯置换算法有最佳置换算法(OPT)、先进先出(FIFO)、最近最久未使⽤算法(LRU)、时钟算法(Clock):

  1. 最佳置换算法: 该算法根据未来的⻚⾯访问情况,选择最⻓时间内不会被访问到的⻚⾯进⾏置换。那么就有⼀个问题了,未来要访问什么⻚⾯,操作系统怎么知道的呢?操作系统当然不会知道,所以这种算法只是⼀种理想情况下的置换算法,通常是⽆法实现的。
  2. 先进先出算法:也就是最先进⼊内存的⻚⾯最先被置换出去。这个算法⽐较简单明了,就不过多解释了。但是先进先出算法会存在⼀个问题,就是Belady问题,即随着分配给进程的空闲⻚⾯数增加,缺⻚的情次反⽽也会增加。 这和我们常识是相悖的,因为我们通常认为如果⼀个进程经常发⽣缺⻚,那么就应该应该为他多分配⼀点内存。然⽽使⽤FIFO算法时,反⽽可能导致更多缺⻚情况出现。这就是Belady问题,Belady问题只会在使⽤FIFO算法时出现。
  3. 最近最久未使⽤算法:LRU算法基于⻚⾯的使⽤历史,通过选择最⻓时间未被使⽤的⻚⾯进⾏置换。LRU算法的核⼼思想是,最近被访问的⻚⾯可能在未来被再次访问,⽽最⻓时间未被访问的⻚⾯可能是最不常⽤的,因此将其置换出去可以腾出空间给新的⻚⾯。LRU算法通常是使⽤⼀个数据结构去维护⻚⾯的使⽤历史,维护使⽤历史就是通过访问字段实现的。访问字段的位数和操作系统分配给该进程的⻚⾯数有关,⽐如分配4个⻚⾯,访问字段就是2位,16个⻚⾯,访问字段就是4位,依次类推。如此,每⼀个⻚⾯的访问字段都可以不同,通过访问字段的不同,我们就可以判断⻚⾯的使⽤历史。
  4. 时钟算法:Clock算法基于⼀个环形链表或者循环队列数据结构来管理⻚⾯的访问情况,⽤于选择被置换的⻚⾯。Clock算法的核⼼思想是通过使⽤⼀个指针(称为时钟指针)在环形链表上遍历,检查⻚⾯是否被访问过。这个访问过同样需要我们上⾯说到的访问字段来表示,此时访问字段只有⼀位。每个⻚⾯都与⼀个访问位相关联,标记该⻚⾯是否被访问过。当需要进⾏⻚⾯置换时,Clock算法从时钟指针的位置开始遍历环形链表。 如果当前⻚⾯的访问位为0,表示该⻚⾯最久未被访问,可以选择进⾏置换。将访问位设置为1,继续遍历下⼀个⻚⾯。 如果当前⻚⾯的访问位为1,表示该页面最近被访问过,它仍然处于活跃状态。将访问位设置为0,并继续遍历下⼀个⻚⾯如果遍历过程中找到⼀个访问位为0的⻚⾯,那么选择该页面进⾏置换。

9. 进程同步和互斥,以及解决方法

进程同步是指多个并发的进程之间协调和管理它们的执行顺序,以确保它们按照一定的顺序或时间间隔执行。
互斥指的是在某一时刻只允许一个进程访问某个共享资源。当一个进程正在使用共享资源时,其他进程不能同时访问该资源。
解决进程同步和互斥的问题有很多方法,其中一种常见的方法是使用信号量和PV操作。信号量是一种特殊的变量,它表示系统中某种资源的数量或者状态。PV操作是一种对信号量进行增加或者减少的操作,它们可以用来控制进程之间的同步或者互斥。

举个例⼦,假设有⼀个信号量 s 表示⼀个祭坛是否可⽤,初始值为 1。如果 s 的值为 1,表示祭坛空闲;如果 s 的值为 0,表示祭坛被占⽤;如果 s 的值为 -1,表示有⼀个⼈在等待使⽤祭坛。那么我们可以⽤ PV 操作来实现对祭坛的互斥访问:
如果你想要使⽤祭坛,你就执⾏ P(s) 操作,将 s 的值减 1。如果结果为 0 或者正数,表示你可以使⽤祭坛;如果结果为负数,表示有⼈在使⽤祭坛,你就必须等待。
如果你使⽤完了祭坛,你就执⾏ V(s) 操作,将 s 的值加 1。如果结果为正数或者 0 ,表示没有⼈在等待使⽤祭坛;如果结果为负数,表示有⼈在等待使⽤祭坛,你就需要唤醒他们中的⼀个。
这样就可以保证每次只有⼀个⼈能够使⽤祭坛,实现了进程互斥。
除此之外,下面的方法也可以解决进程同步和互斥问题:

  • 临界区:将可能引发互斥问题的代码段称为临界区。为了实现互斥,每个进程在进入临界区前必须获取一个锁,退出临界区后释放该锁。这确保同一时间内只有一个进程可以进入临界区。
  • 互斥锁:互斥锁是一种同步机制,用于实现互斥。每个共享资源都关联一个互斥锁,进程在访问该资源前需要先获取互斥锁,使用完后释放锁。只有获得锁的进程才能访问共享资源。
  • 条件变量:条件变量用于在进程之间传递信息,以便他们在特定的条件下等待或唤醒。通常与互斥锁一起使用,以确保等待和唤醒操作在正确的时机执行。

条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用
当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待

10. 中断和异常

中断和异常是两个不同的事件,它们都会导致CPU暂停当前的进程转而去执行一个特定的处理程序。

中断和异常的区别:

  1. 中断是由外部设备或其他处理器产生的,通常是异步的,也就是说它可以在任何时间发生,不受程序运行的影响。例如:鼠标移动、键盘输入、网络数据到达都有可能引发中断。
  2. 异常是由CPU内部产生的,通常是同步的,它们通常和执行某些特定的程序或指令有关,例如访问非法地址,执行非法指令等都会产生异常信号,通知CPU去处理这些错误或故障。
  3. 中断可以被屏蔽或禁止,这意味着CPU可以设置某些标志位或者寄存器来忽略或者禁止某些中断的产生。这样可以避免中断过于频繁或干扰重要的任务。
  4. 异常不可以被屏蔽或禁止,这意味着CPU必须立即响应异常信号,并进行相应的处理。这样可以保证程序的正确性和系统的稳定性。

中断的定义:

CPU在执行过程中收到某个中断信号,转而去执行预先设定好的代码,然后再返回原指令流中继续执行,这就是中断机制。

中断的作用:

  1. 外设异步通知CPU:外设发生了什么事情或者完成了什么任务或者发送了什么消息都可以异步通知CPU;
  2. CPU之间发送消息:在SMP系统中,一个CPU想要给另一个CPU发送消息,可以给其发送处理器中断;
  3. 处理CPU异常:CPU在执行指令的过程中遇到了异常会给自己发送中断信号来处理异常。
  4. 实现系统调用:早期的操作系统就是靠中断指令来实现的,后期虽然开发了专用的系统调用指令,但基本的原理还是一样的。

中断的产生:

  1. 外部设备。这种中断又叫做硬件中断,一般是异步的,硬件中断根据是否可以屏蔽可分为可屏蔽中断、不可屏蔽中断
  2. CPU。一个CPU向另外一个CPU发送消息产生的中断,叫做IPI(处理器中断),也是异步的,可以看做一种硬件中断。
  3. CPU异常:CPU在执行指令的过程中发现异常会向自己发送中断信号,是同步的,一般也叫做软件中断。根据是否需要修复可以分为:
    1. 陷阱 : 不需要修复,中断处理完成后继续执行下一条指令
    2. 故障: 需要修复且有可能修复好,系统执行完后会继续执行原来的指令
    3. 终止:需要修复但无法修复好,中断处理完成后,进程或内核将会崩溃。
  4. 中断指令。直接调用CPU来产生中断信号,这种中断和CPU异常一样是异步的,也可以叫做软件中断。

11. 几种典型的锁

两个基本的锁:
互斥锁:互斥锁是一种常见的锁类型,用于实现互斥访问共享资源。即在任何时刻,只有一个线程可以持有互斥锁,其他线程必须等待锁释放。这确保了同一时间只有一个线程可以访问被保护的资源。
自旋锁:自旋锁是一种基于忙等待的锁,即线程在尝试获取锁的过程中会不断轮询,直到锁被释放。

其他的锁都是基于这两个锁的:
读写锁:允许多个线程读共享资源,单只允许一个线程进行写操作。分为读(共享)和写(排他)两种状态。
悲观锁:认为多线程同时修改共享资源的概率比较高,所以访问共享资源的时候要上锁。
乐观锁:先不管,修改了共享资源再说,如果出现了同时修改的情况,再放弃本次操作。
读取频繁使用乐观锁,写入频繁使用悲观锁。

12. 线程同步的方式有哪些?

线程同步机制是指在多线程编程中,为了保证线程间互不干扰,而采用的一种机制。常见的线程同步机制:

  1. 互斥锁: 互斥锁是最常见的线程同步机制。它允许只有一个线程同时访问被保护的临界区(共享资源)
  2. 条件变量:条件变量用于线程间通信,允许一个线程等待某个条件满足,而其他线程可以发出信号通知等待线程。通常与互斥锁一起使用。
  3. 读写锁:读写锁允许多个线程同时读取共享资源,但只允许一个线程写入资源。
  4. 信号量:用于控制多个线程对共享资源进行访问的工具。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/742833
推荐阅读
相关标签
  

闽ICP备14008679号