计算机基础知识八股文(操作系统篇)

二 操作系统篇

文章目录

2.1 简述什么是进程?

进程是系统进行资源分配和调度的基本单位,

进程本质上是运行中的程序是动态的,需要将进程运行的当前状态,所需资源等信息保存到进程控制块中,操作系统为了管理进程设计的数据结构叫进程控制块,里面存的字段可以分成进程标识符、处理机状态、进程调度信息、进程控制信息。

2.2 进程状态模型 —简述阻塞、非阻塞、同步、异步—简述为什么发送阻塞?

五状态模型:创建、就绪、终止、阻塞。运行

就绪状态:其他资源都准备好了,只差CPU资源的状态,只要在获得CPU使用权就可以随时被调度执行。

创建状态:创建进程是拥有PCB(进程控制块)但其他资源没有就绪为创建状态

先分配PCB(进程控制块)然后插入就绪队列。

操作系统提供fork函数接口创建进程

终止状态:进程结束有系统清理或者归还PCB的状态为终止状态。

阻塞状态:进程因为某种原因(其他设备没有就绪包括磁盘、网卡等)无法继续执行而放弃cpu的使用权,把cpu资源让给其他进程。

在这里插入图片描述

进程从就绪状态进行进程调度,分配cpu资源,然后到运行状态,当时间片用完之后进入就绪状态,而在运行状态时因为某些资源没有就绪比如IO请求包括网络IO、磁盘IO、都可能从运行状态切换到阻塞状态,当IO请求完成了进程从阻塞状态切换就绪状态。

阻塞、非阻塞、同步、异步。

阻塞状态(同步):阻塞的话典型的就是IO过程,从调用到返回会经历一段时间。因为比如外围设备包括磁盘。网卡等,在读写数据没有CPU快,所以通常读取数据的时候会经历一段时间,这段时间就是属于阻塞状态。在调用结果返回之前,当前进程会被挂起,只有得到结果才会返回。

同步:进程因为在这段时间并没有干其他事情,而是同步的等待数据返回

非阻塞状态(异步):并没有一直等待数据的返回,而是其他的工作,等到比如IO完成了会通知已经完成,进程再去返回IO任务去读取数据。

异步:进程在IO之后没有进行同步等待,去干其他事情,并且等待IO这个过程准备好之后进行通知,进程接收到通知ready之后再切换回去读取数据。

当一个异步的调用发出之后,调用并不会得到结果而是,被调用者通过状态或者通知,来通知进程获取结果。

总结:同步异步抢到消息通讯机制,阻塞非阻塞抢到进程在等待调用结果的状态。

2.3 PBC是什么?(进程控制块)

PCB是进程控制块。

PCB是进程的唯一标识,操作系统调度进程时就是根据每个进程PCB中的信息进行调度的,当决定执行 某个进程后,会根据该进程PCB中保存的信息去恢复上次执行的现场,当分配到的CPU时间片用完 后,需要将当前状态保存到PCB中,以便下次恢复。

PCB组织方式: 通过链表的方式组织成一个个队列,拥有相同状态的进程组成一个队列。 比如就绪进程就会组成就绪队列、因为某些事件而阻塞的进程组成阻塞队列。 也可以将相同状态的PCB按照其他策略排成多个链表

PCB包含进程的属性和状态信息:

  • 进程标识符(进程ID)
  • CPU状态信息(寄存器、PC)
  • 进程调度信息(进程状态、进程优先级、进程已等待时间等)
  • 进程控制信息(页表地址)

2.3 线程——介绍进程、线程、协程

线程:

线程是任务调度和执行的基本单位。CPU上真正运行的是线程,但线程本身几乎不拥有资源,线程自己只拥有一点运行时必不可少的资源,如PC、寄存器和栈,但线程可以和同一进程下的其他线程共享进程资源。

进程:

进程是系统进行资源分配和调度的基本单位,

进程本质上是“运行中的程序”,单纯的程序只是保存在磁盘中的一段代码,是静态的,而进程是运行中的代码,是动态的,除了需要保存这段代码外,还需要将进程运行的当前状态,所需资源等信息保存到进程控制块中,操作系统为了管理进程设计的数据结构叫进程控制块,里面存的字段可以分成进程标识符、处理机状态(进程当前运行到什么时候什么状态的一些信息)、进程调度信息、进程控制信息。所以进程由3部分组成:程序段、数据段和进程控制块。

**协程:**比线程更加轻量级,协程不被操作系统内核所管理,完全是用户态的

2.4 进程与线程区别

  • 一个进程包含多个线程,一个线程只能属于一个进程
  • 创建和销毁线程的速度快,因为创建线程不需要单独分配资源了。
  • 线程间切换的速度较快,只需要保存和恢复寄存器内容即可,而进程切换就需要保存当前CPU状态到PCB中,然后 将下一个进程的状态进行恢复。
  • 由于线程间共享进程的内存和文件,所以线程间通信不需要其他复杂的机制,也不需要涉及内核,既简便又快速
  • 进程的健壮性更强,一个线程挂掉会导致整个进程挂掉,但一个进程挂掉不会导致其他进程挂掉。
  • 进程是系统进行资源分配和调度的基本单位,线程是任务调度和执行的基本单位。

2.5 进程和线程的切换流程

进程切换流程: 进程之间的虚拟地址空间是不同的,所以要先切换页表,清空快表缓存。 切换内核栈和硬件上下文

进程上下文:当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文

线程切换流程: 线程之间共享进程的虚拟地址空间,所以线程切换不需要切换页表 直接切换内核栈和硬件上下文即可

2.6 并发和并行的区别?

并发和并行在一个时间段内的表现都是多个进程得到了运行。

  • 并发在任意一个时刻下,只有一个进程在运行;
  • 并行在任意一个时刻下,有多个进程在同时运行。

2.7 进程调度策略

非抢占式:进程获得CPU资源后,一直运行直到进程结束或阻塞才释放CPU使用权。

  • 优点:简单、上下文切换开销小
  • 缺点:紧急任务不能马上执行、后到的短进程需要等待长进程完成,导致短进程周转时间变长

抢占式:操作系统可以在进程执行时剥夺CPU,分配给其他进程使用。 典型的有:优先级原则、短进程优先原则、时间片原则

进程调度算法

  • 短作业优先: 抢占式、难以准确预测进程执行时间
  • 优先级调度 - 最常用: 抢占式、分为静态优先级和动态优先级,动态优先级系统开销较大,静态优先级是在进程创建时就决定的
  • 时间片轮转 - 分时系统 :抢占式、为每个执行进程设置时间片,时间片用完则交出CPU
  • 先进先出算法 - FIFO: 非抢占式、容易造成短进程等待时间变长
  • 最高响应比优先: 非抢占式、对短进程有利,长进程由于等待时间变长会提高响应比,不至于让长进程饿死
  • 前后台调度 :前台执行分时程序,用时间片轮转调度;后台执行批处理程序,用FIFO调度。只有当前台没有程序 时,CPU才交给后台使用。
  • 多级反馈队列轮转算法: 将就绪队列分成多个优先级不同的队列。 刚创建和进程 和 因IO未用完时间片的进程排在最高优先级队列,2-3个时间片还未执行完的进程放入 较低优先级队列,只有优先级更高的队列为空时才会调度当前队列。

2.8 僵尸进程 孤儿进程 守护进程

  • 孤儿进程:子进程的父进程退出,子进程被ID号为1的inti进程收养,作为子进程的父进程.然后init进程会执行wait或waitpid操作,去释放孤儿进程的资源.

    危害:孤儿进程没有危害

  • 僵尸进程:子进程先退出,父进程没有通过wait()或者waipid()去回收释放子进程的资源,此时子进程虽然 退出了,但还占有原本的PCB.

    危害:当有大量僵尸进程在内存中时,会占据大量PCB,而PCB资源是有限的,所以会导致无法创建新的进程.

    处理:父进程退出,让僵尸进程成为孤儿进程,由init进程收养,处理资源回收工作.

  • 守护进程:在后台运行不受终端控制的进程.

2.9 进程的上下文切换需要哪些开销?线程呢?

进程切换的开销 :1、切换页表 2、切换内核态堆栈 3、将寄存器中的上下文保存到PCB中 4、刷新跳表 TLB是为了更快的进行地址翻译而将部分的页表内容缓存

线程切换的开销 :1、切换内核栈 2、切换硬件上下文

进程切换和线程切换,开销差距大就大在页表切换上,因为这个步骤涉及将寄存器中的内容切换出。 还有,进程切换后,由于跳表刷新,所以一段时间内命中率就很低。

2.10 堆和栈的区别

堆:程序员分配和释放的空间,不释放则由操作系统回收,形式是链表。

栈:操作系统自动分配释放的空间,保存函数的参数和局部变量,形式是栈。

2.11 用户态&内核态

通过系统调用将操作系统整个体系分为用户态和内核态。

一般的操作系统对执行权限进行分级,分别为用户态和内核态。用户态相较于内核态有较低的执行权限,很多操作是不被操作系统允许的,内核态相当于一个介于硬件与应用之间的层,有最高权限,可以执行任何cpu指令,也可以引用任何内存地址,包括外围设备, 例如硬盘, 网卡,权限等级最高。

为什么要有用户态和内核态:为了限制不同程序对数据的访问能力,防止部分程序获取其他程序的数据,或者获取外围设备的数据,发送到网络上提高安全性.

2.12 系统调用:

从应用程序访问到系统内核资源,称之为系统调用。

当一个程序从需要操作系统资源的时候,实际上是从【用户态】->【内核态】->【用户态】这样的一个过程。在这个过程中也需要一定的开销,(strace命令监控一次系统调用的开销,可以使用strace -cp 查看一个进程一段时间内的系统调用,我们可以看到每个系统调用的次数以及总时间。)

简单来说有下面几点:

  1. 保存用户态现场
  2. 复制用户态参数并且校验
  3. 执行内核态代码
  4. 复制执行结果
  5. 恢复用户态现场

2.13 如何从用户态进入内核态

  • 系统调用:这是用户态进程主动要求切换到内核态的一种方式。例如fork()就是执行了一个创建新进程的系统调用。系统调用的机制和新是使用了操作系统为用户特别开放的一个中断来实现,所以系统调用本身就是中断,但是软件中断,跟硬中断不同。
  • 异常:如果当前进程运行在用户态,如果这个时候发生了异常事件,就会触发切换。例如:缺页异常。
  • 外设中断:当外设完成用户的请求时,会向CPU发送中断信号。这时CPU会暂停执行下一条即将要执行的指令而转到与中断信号对应的处理程序去执行,如果前面执行的指令时用户态下的程序,那么转换的过程自然就会是 由用户态到内核态的切换。如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等。

2.14 程序运行类型分析——什么是IO密集型任务/CPU密集型任务

CPU密集型:也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要高,此时读写IO(硬盘/内存)时可以在很短的时间内完成,而CPU还有许多运算要处理,因此CPU负载很高。

通过多线程提高CPU利用率,通常线程数只需要设置为CPU核心数的线程个数。

因为**频繁的切换线程或进程**也是要消耗时间的。因此对于 CPU 密集型的任务来说,线程数/进程数等于 CPU 数是最好的了。

IO密集型:指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等IO (硬盘/内存) 的读写操作,因此,CPU负载并不高。

当线程进行 I/O 操作 CPU 空闲时,启用其他线程继续使用 CPU,以提高 CPU 的使用率。

2.15 系统设计中缓存的作用——存储器的层次结构

存储器层次结构分为寄存器、主存、辅存。

局部性原理:CPU访问存储器时。无论是存取指令还是数据,访问的存储单元都趋向于聚集在一个较小的连续区域中。

在这里插入图片描述

缓存-主存层次:根据局部性原理通过在CPU与主存之间增加Cache以解决主存速度不足的问题。

主存-辅存层次:根据局部性原理通过增加辅助存储器包括磁盘啥的以解决主存容量不足的问题。

缓存(如redis)的设计:原理也是局部性原理,借鉴操作系统的原理,分离冷热数据,降低热点数据负载,提升吞吐量、并发量、提升服务质量。

2.16 什么是缓冲区溢出?有什么危害?原因是什么?

缓冲区溢出是指向计算机缓冲区填充数据时超过了缓冲区最大容量,导致数据被写到缓冲区以外。

危害: 程序崩溃 跳转到恶意代码

原因:程序中没有仔细检查用户输入

2.17 操作系统内存管理——缺页中断、内存页

什么是分页?

分页是将内存从物理上划分成大小相同的页(linux一般4K),进程所需的数据分别存放在多个页中, 这些页离散地分布在内存中,因此需要一张记录内存逻辑地址到物理地址映射关系的表,也就是页表,具体记录了页号到物理块号的映射。 分页系统中,访问数据需要两次访问内存,第一次访问的是内存中的页表,根据页号和页内偏移量查找并计算出实际物理地址,第二次根据物理地址访问内存取数据。

什么是分段?

分段是将进程的逻辑空间划分为若干不同大小的段(长度由连续逻辑的长度决定),段地址是二维的,一维是段号、二维是段内偏移。因为多个段之间也是离散分布在内存中的,所以需要段表来记录逻辑地址到物理地址的映射关系。

分页和分段的区别?
  • 页是物理单位,方便管理内存空间,用户不可见;段是逻辑单位,根据用户需要自行划分,对用户可见
  • 段的大小不固定,页的大小固定,一般是4K
  • 段向用户提供二维地址空间,页向用户提供一维地址空间
  • 分页的主要目的是为了实现虚拟内存,提高内存利用率;而分段是为了程序和数据能根据逻辑进行划分,从而更好地进行管理
什么是段页式存储管理?

先将逻辑空间按段式管理分成若干段,在将段空间按页式管理分成若干页,段页地址就可以通过段号、段内页号、页内地址找到属于那一段那一页。综合两者优点

2.18 虚拟内存

有些进程实际需要很大的内存,超过物理内存容量,再加上多道程序设计使每个进程可用的物理内存更加稀缺,物理内存总有不够的时候。

虚拟内存概述:作为操作系统的内存管理技术,把程序使用的内存划分,(根据局部性原理)将部分暂时不用的内存放置到辅存。替换策略发生在缓存-主存层次/主存-辅存层次。解决速度/容量问题。如果访问页不在内存,则发出缺页中断,发起页面置换。

逻辑地址空间:指进程可以使用的内存空间,大小仅受CPU地址长度限制(比如32位的最大逻辑空间为4G-- 2^32字节, 2^32字节)进程运行时可以使用的相对地址空间。

物理地址空间:物理内存的存储空间,进程运行时在物理内存分配和使用的地址空间。

逻辑地址空间需要转换到物理地址空间,是映射关系。

缺页中断:当进程读取的页号在页表中不存在时,会触发缺页中断,处理缺页中断的方法是从外存中找出目标页,然后装入内存中.

具体做法:

  • 从外存中找到目标页

  • 检查当前内存中的空闲是否足够目标页装入

  • 若空间足够,则直接装入目标页,更新页表,中断处理结束

  • 若空间不够,则执行页面置换算法,淘汰页面直到空间足够为止,然后装入目标页,中断处理结束

    注意如果淘汰页被修改过,则要执行淘汰页的备份

2.19 页面置换算法——LRU/LFU算法

页面置换时机:高速缓存(需要从主存载入数据)以及主存页面(需要从辅存载入数据)的替换。

先进先出算法(FIFO):队列式算法,页面以链表形式组织,新页装入时,淘汰链表头的最旧页(双向链表实现)

优点:简单、易于实现

缺点:效率不高,驻留最久的页不一定就是最长时间不使用的页

最不经常使用算法(LFU):优先淘汰最不经常使用的字块,需要额外的空间记录字块的使用频率。加入新页时增加频率,淘汰最低的

最近最少使用算法(LRU)

1)链表:

优先淘汰一段时间没有使用的页面,表头存最近访问的页,表尾存最久未用的页。如果访问目标页,则将该页移向表头,其他页相应地也移动,挤掉表尾的页。

优点:效果好

缺点:维护链表的开销太大

2)链表+哈希表=哈希链表:

  • 想要查询和插入的时间复杂度都是O(1), 需要满足:必须是有序的, 以区分最近使用的和很久没有使用的数据, 当容量满了之后删除很久没有使用的数据,
  • 要能够快速找到某个key是否存在, 并返回value;
  • 每次访问某个key, 需要将这个元素变为最近使用的, 也就是说, 要支持在任意位置快速插入和删除元素;

查找快, 可以想到哈希表, 但是哈希表是乱序的;有序, 可以想到链表, 链表插入、和删除都很快, 但是查询是O(n);集合一下就是哈希链表: , 结构如下:

img

借助上图结构, 分析上面的三个条件:

  • 每次在链表尾部添加元素, 那么越靠近尾部的元素就越是最近使用的, 链表头部的元素就是越久未使用的;
  • 对于某一个key, 可以通过哈希表快速定位到链表中的节点, 取到value, 不用从头遍历;
  • 链表支持在任意位置插入和删除, 修改指针即可, 可以借助哈希表, 快速映射到任意一个链表节点, 进行插入和删除。
为什么这里要用双链表呢,单链表为什么不行?

涉及到链表的删除操作, 因为通过哈希表定位到某一个链表中的节点, 想要删除时, 需要知道前驱节点的指针。

哈希表里面已经保存了 key ,那么链表中为什么还要存储 key 和 value 呢,只存入 value 不就行了

如果链表中的结点,只有 value 没有 key,那么我们就无法删除哈希表的 key。

时钟算法:类似LRU,首先需要给每个页设置访问位,当某页被访问时,访问位为1,该算法按照先进先出算法的顺序依次遍历内存中所有页,若该页的访问位为0,则将该页换出,若为1,则将该页访问位置为0,暂时不换出给他第二次驻留内存的机会,若遍历到最后一个页,访问位仍然是1,则再从第一个页开始遍历。该算法也叫做最近未使用算法

优点:性能和开销较均衡

缺点:没有考虑到换出被修改过的页所造成的置换代价

改进时钟算法:淘汰的既要是未被访问过的页面,也要是未被修改过的页面。给页面设置访问位A和修改位M。

A = 0, M = 0 该页最近未被访问,也未被修改过,是最佳淘汰页

A = 0, M = 1 该页最近未被访问,但被修改过,不是最佳的淘汰页

A = 1, M = 0 该页最近被访问过,但未被修改,很可能会再次被访问

A= 1, M = 1 该页最近被访问过,也被修改过,很可能会再次被访问

算法执行流程和简单Clock算法类似:

1、第一轮扫描,目标是 A = 0, M = 0 的页,找到的第一个就作为淘汰页

2、若第一轮失败,则进行第二轮扫描,目标是 A = 0, M = 1 的页,找到的第一个作为淘汰页,且在第二轮扫描期间,将经过的页面 A置为0

3、若第二轮失败,则进行第三轮扫描,目标是第一类页

4、若第三轮失败,则进行第四轮扫描,目标是第二类页,此轮一定能找到淘汰页。

优点:由于换出的是最近未被修改过的页,所以可以减少磁盘IO次数

缺点:该算法执行时扫描轮次变多,开销大

2.20 什么是交换、交换区?

操作系统将内存中阻塞的进程暂时移出内存,并将已经就绪的进程移入内存的操作叫做交换。

从操作系统中暂时移出的进程,放在外存的交换区中。

2.21 为什么虚拟地址切换会比较耗时?

进行虚拟地址到物理地址的转换就需要去查页表,而查找页表是一个很慢的操作,所以为了加快该过程就会用快表去缓存最近查找过的虚拟地址,但每个进程都有自己的页表,一旦发生进程切换,页表就需要切换,那么快表就会被清空,所以导致进程切换后查快表的命中率非常低,此时大量时间花在查页表上,表现出来的就是程序运行的速度变慢了。

线程切换就不会导致快表失效,因为线程切换无需切换地址空间,所以线程切换比进程切换快。

2.22 什么是内存碎片?如何解决?

内存碎片分为内部碎片和外部碎片.

内部碎片分配出去但无法被利用的内存空间,分页系统是以页为单位分配内存空间的,而且页的大小是固定的,这就使得分配给一个进程的多个页中,最后一页往往无法全部利用,由空闲空间,这部分就是内部碎片,直到进程执行结束,其他进程都无法使用这部分空间.

解决方法:采用分段存储,每个段的长度可以不一样.

外部碎片是指那些还没有被分配出去,但是因为空间太小而无法分配给进程的空闲空间,外部碎片因为彼此之间不连续,本身又太小,所以无法被利用,只有通过将外部碎片地址整合在一起,才可以利用起来,但整合外部碎片的开销又比较大.

解决方法:采用分页存储,页与页之间连续,不会产生外部碎片

综合解决方法:采用段页式存储,先对内存分段,然后再将每一段分成大小相同的页

2.23 虚拟地址和物理地址之间是如何进行映射的?

通过MMU(内存管理单元)

2.24 LInux软连接与硬链接区别

EXT文件系统:

在这里插入图片描述在这里插入图片描述

Inode Table :每一个文件(目录)的索引节点,存放Inode的地方,每一个文件(目录)都有一个Inode

Inode:保存文件相关的原信息,包括文件类型、状态、长度、等等。(文件名存放在目录的Inode节点上,而不是文件的Inode

linux文件=Inode(文件源信息)+block(文件数据)

硬链接直接指向源文件的Inode。

软连接有一个完整的文件包括Inode以及block,block存放的是源文件路径。(快捷方式)

2.25 磁盘冗余阵列——服务器部署的RAID存储

RAID(磁盘冗余阵列):利用虚拟化存储技术把多个硬盘组合起来,成为一个或多个磁盘阵列,提示性能减少冗余。

RAID 0:性能是单块性能的N倍,不提供数据校验和冗余,若是某一个磁盘损坏,数据直接丢失,可靠性低

RAID 1:相对RAID0做了冗余,增加镜像磁盘备份,可以做数据恢复

RAID 5:(最常用)增加校验码、海明码,

RAID 1 0:结合RAID 0和RAID 1

2.26 常见的磁盘调度算法?

  • 先来先服务:根据进程请求访问磁盘的先后顺序进行调度

    优点:实现简单 缺点:本质上接近于随机读取

  • 最短寻道时间优先:选择距离当前磁头最近的访问请求进行服务

    优点:平均服务时间较短 缺点:容易让部分服务饿死

  • 扫描算法(电梯算法):选择当前磁头移动方向上与当前磁道距离最近的请求进行服务

    优点:扫描算法既考虑了距离,又考虑了方向

2.27 什么是死锁,

死锁就是多个进程间彼此持有对方需要的资源,但又在等待对方释放自己需要的资源的状态。

2.28 为什么产生死锁

  • 竞争资源引起死锁:竞争非剥夺性资源(CPU和内存是可剥夺资源,IO设备是非剥夺资源)和临时性资源。非剥夺性资源分配不当、使用未加限制都会产生死锁
  • 进程推进顺序不当:相似逻辑的程序,指令顺序不相同。

2.29 死锁必要条件的理解?

  • 互斥条件。进程请求的资源具有互斥性,一段时间内该资源只能被一个进程独占。
  • 请求和保持条件。当进程因请求资源而阻塞时,不会释放已经持有的资源。
  • 不剥夺条件。进程获得的资源,在进程未使用完前,不用被剥夺,只能用完后主动释放。
  • 环路等待条件。进程集合中每一个进程都在等待下一个进程正在占用的资源。

2.30 解决死锁的基本策略

  • 预防死锁。从根本上破坏死锁出现的四个条件之一,但会降低资源利用率
  • 避免死锁。使用某种机制防止系统进入死锁状态,可以有较高的资源利用率
  • 检测死锁。允许发生死锁,系统可以检测出死锁的发生,然后消除死锁
  • 解除死锁。与检测死锁配套使用,常用方法是撤销或挂起一些进程,释放出一些资源,这些资源分配给阻塞的进程,等于是疏通了环路。

1、预防死锁

(1)破坏互斥条件:进程申请资源时,为资源进行一次拷贝,进程可以获取资源的拷贝

缺点:实用性不强,拷贝资源的成本极高,当线程数较多时,不适用

(2)摒弃请求和保持条件:必须一次性将进程所需的资源全部分配给进程,否则可以分配的资源也不分配给进程

优点:简单、易于实现、安全

缺点:

  • 进程要在执行期间独占所需的全部资源,其中一些资源可能用的很少,造成资源浪费
  • 进程运行延迟,可能出现一些进程长时间申请不到全部资源而等待。

(3)摒弃不剥夺条件:当进程得不到新申请的资源时,释放自己已经持有的资源,等到需要时再重新申请;或者设置计时机制,当进程持有资源超过一定时间仍不能完成任务时,会释放已经持有的资源(参考CPU无法成为死锁资源,因为CPU的时间片轮转,让每个进程只能)。

缺点:实现复杂、反复申请资源使得进程无限推迟,降低系统吞吐量。

(4)摒弃环路等待条件:对所有资源进行标号,进程对资源的申请顺序必须按照资源标号升序申请。

优点:资源利用率高、系统吞吐量大

缺点:限制了新资源的加入、使用顺序与申请顺序不同,导致资源浪费。

2、避免死锁

(1)安全与不安全状态。进程动态申请资源,每次申请资源时系统都会评估分配该资源的安全性,若 此次分配不会导致系统进入不安全状态,就将资源分配给进程。

(2)银行家算法。可利用资源向量、最大需求矩阵、分配矩阵、需求矩阵。 进程P的请求向量

1)若请求向量 <= 需求矩阵,转入第二步,否则出错,因为请求资源数大于它宣布的最大数

2)若请求向量 <= 可利用资源,转入第三步,否则表示资源不够,进程等待

3)系统试探性地将资源分配给进程,修改 可利用资源向量、分配矩阵、需求矩阵

4)系统执行安全性算法,检查资源分配后是否处于处于安全状态,若安全则执行此次分配,若不安全 则撤销此次分配。

3、检测死锁

(1)资源分配图 + 死锁定理

(2)死锁检测算法 = 银行家算法中的安全检测方法

4、解除死锁

(1)剥夺资源。从其他进程中剥夺足够的资源给死锁进程。

(2)撤销进程。简单的是清除全部死锁进程,温和一点的是逐个清除死锁进程,直到死锁状态消除为 止。

2.31 简述各种锁的概念

乐观锁/悲观锁

悲观锁每次操作都会加锁,乐观锁默认不加锁

悲观锁适合写操作多的场景,乐观锁适合读操作多的场景,可以提升并发度

无锁/偏向锁/轻量级锁/重量级锁:

  • 无锁:不锁资源,多线程只有一个线程修改成功,其他线层会重试。
  • 偏向锁:锁会对有一个线程有偏向性,同一个线程执行临界资源会自动获取资源
  • 轻量级锁:多个线程竞争同步资源时,没有获得资源的线程自旋(不放弃CPU)等待锁释放。
  • 重量级锁:多个线程竞争同步资源时,没有获得资源的线程阻塞等待唤醒。

公平锁/非公平锁:

公平锁:有一个先进先出的等待队列,不会出现饥饿等待

非公平锁:可以插队,没有插上的话也是插在等待队列尾部。会出现饥饿等待

可重入锁/非可重入锁:

重入:任意线程获得锁之后,这个线程再次获取该锁时不会阻塞

可重入锁:又名递归锁,同一个线程在外层方法获取锁的时候,在进入该线程的内层方法会自动获取锁,不会因为之前已经获取过还没释放而阻塞。

非可重入锁:会出现死锁

共享锁/排它锁:

共享锁:该锁可以被多个线程持有,获得共享锁的线程只能读不能写。

排它锁:又名互斥锁。一次只能被一个线程持有。

2.32 线程间通信——互斥锁和自旋锁的区别——线程间同步?

互斥锁:互斥锁就是可以拒绝另一个线程访问临界资源的一种锁。会让出CPU,可以通过互斥锁保证串行访问资源以保护这个临界资源。是最简单的线程间通信方法。

自旋锁(spin_lock):拒绝另一个线程访问临界资源的一种锁,不过与互斥锁的实现原理不同。自旋锁不会让出CPU,是一种忙等待状态。为了避免进程或者线程上下文切片的开销,不适合单核CPU。

读写锁:是一种特殊的自旋锁,准许多个读者同时访问提高读性能,不过写操作则是互斥锁,适合多读少写的情况,因为读取的时候不会改变临界资源。

条件变量:条件变量本质也是一个条件变量,它的功能是阻塞线程,直至接收到“条件成立”的信号后,被阻塞的线程才能继续执行。

相对复杂的线程同步方法。允许线程睡眠,直到满足某种条件,当满足条件,发送信号通知唤醒。wait——》signal

例如:生产消费关系会用到,缓冲区空或者满。当生产者生产一份数据的时候,唤醒可能等待的消费者,

2.33 进程间通信——进程间通信同步方式?

信号量

允许多个进程同时访问统一资源,描述信号量的结构体,保存了两个变量,一个是整型变量 value,另一个是进程链表L,对信号量的操作只有P和V两个原子操作。执行P操作时,会先将value值减一,若此时 value小于零,则说明临界资源暂时不可访问,于是进程会阻塞,否则顺利访问资源;执行V操作时,会先将value值 加一,如果此时有进程正在睡眠等待此信号量,则唤醒此进程。。

  • 当信号量初始值为 1 时,信号量等同于互斥锁。
  • 当信号量初始值为 0 时,代表是同步信号量,可以用于控制两个进程的前驱关系。

AND信号量机制:

如果进程P和进程Q都需要临界资源A和临界资源B才能工作,那么有可能造成P持有 A、Q持有B的情况,此时两个进程都在等待对方释放资源,互相持有对方需要的临界资源,形成死锁。为了解决死锁,进程引入了AND信号量机制,就是指进程需要的全部资源,要么全部分配给进程,如果有任意一个资源无法分配,就将可以分配的那些资源也放弃,等价于将分配进程所需资源这个操作原子化。

缺点:用信号量实现进程同步会造成系统大量P操作和V操作,效率较低。

管道:例如:匿名管道 :cat 文件 | grep ERROR,命名管道 mkfifo 命名

  1. 进程将数据以先入先出的方式写入缓冲区,缓冲区可以看成是一个循环队列
  2. 管道以半双工的方式工作,一个进程可以向管道中写数据fd[1],另一个进程可以从管道中读数据 fd[0],要想实现双向通信,就必须使用两个管道
  3. 匿名管道是存在与内存中的特殊文件,只支持具有亲缘关系的进程间通信(数据读写涉及内核态和 用户态之间的数据拷贝);命名管道是存在与磁盘中类型为p的文件,支持非亲缘关系进程间通信(数 据读写涉及IO操作)
  4. 进程的生命周期随进程本身,进程销毁则管道销毁

优点:实现简单

缺点: 1、通信效率低,不适合进程间频繁通信,先入先出

​ 2、通信格式只支持字节流数据。

消息队列:

  1. 消息队列是保存在内核中的链表
  2. 链表节点是消息,消息有特定的格式和特定的优先级(表示优先级的整数、消息长度、消息本身)
  3. 消息队列独立于发送进程和接收进程,即使进程终止也不会让消息队列和消息被删除
  4. 通过消息队列进行通讯,可以不必遵循先入先出,而是根据自定义条件接受特定类型的消息
  5. 内核有一个消息队列数组,消息队列数量不能超过消息队列数组的容量
  6. 当进程A要向进程B发送消息时,通过msgsnd命令向队列中添加消息,进程B通过msgrcv从消息队 列中取出消息

优点:

  1. 适合进程间频繁通信,读消息时可以不按照消息顺序进行读取,而是根据自定义条件接收特定类型 的消息。
  2. 可以用序列化对数据进行封装
  3. 可以跨级进程间通信,MQ

缺点:

  1. 使用消息队列进行通信时,会有用户态到内核态之间数据拷贝的开销(因为消息队列在内核中,进 程是在用户态,所以将数据写入队列时,会有一个从用户态到内核态的数据拷贝)。
  2. 不适合大数据传输(消息块有最大长度限制)
  3. 通信不及时

共享内存

  1. 原本不同进程虚拟内存映射的物理内存是不同的,共享内存将一段公共内存区域映射到多个进程的虚拟地址空间,进程间通过写入该共享内存实现通信,避免用户态到内核态的数据拷贝。本质上类似于同一进程中多个线程的同步。
  2. 由于多个进程都可以同时对共享内存进行读写,所以需要额外的同步机制防止读写不一致,常与信号量搭配使用

优点:避免了用户态到内核态的数据拷贝,直接对内存进行读写,是最快的进程间通信。

缺点:缺少进程同步机制,多个进程可能同时对共享内存进行写入,造成数据不一致,需要用到互斥锁或信号量。

信号(事件)

事件机制就是一个进程完成任务后,发送信号主动唤醒另一个sleep的线程执行任务,没有收到唤醒信号的线程就会处于sleep状态,也可以实现不同进程中的线程间的同步。

仅起到通知作用没有数据交流,不同信号使用不同的值,Linux通过kill -l 查看信号列表:比如Ctrl c 用的信号值是2.

在这里插入图片描述
套接字:

跨网络跨主机进程间通信。TCP连接本质上就是由两个套接字组成的跨网络进程间通信。 传输层提供主机不同进程之间通信。

套接字=IP(标识主机)+端口(标识进程)

套接字类型有3种:TCP字节流通信、UDP数据报通信、本地进程间通信。

  • TCP字节流通信:监听套接字(监听)和已完成套接字(真正传送数据)
  • UDP数据报通信:每一个UDP套接字都需要bind
  • 本地进程间通信套接字:套接字bind的是一个文件,可以支持字节流和数据报两种协议,效率远大于 TCP、UDP

UnIx域套接字:可以用于单机进程间通信,提供网络套接字类似的功能,因为无需经过完整的网络协议栈所以在单机上从性能方面考虑的话优先使用域套接字。

2.34 简述无所数据结构原理——CAS原理与无锁技术?

大量使用锁的弊端:

  • 墨菲定律:只要存在锁一定会发生死锁,而死锁的定位也是很麻烦的,可能会要一个很严苛的环境,比如在一定的并发量下才会复现
  • 调度问题:当低优先级线程持有锁的时候会导致高优先级线程无法执行。
  • 性能问题:频繁的加锁解锁对性能有一定影响
  • 锁粒度:锁粒度过小,死锁概率增肌,锁粒度过大的话导致性能问题

原子性:指一系列操作不能被中断的特性,这一些列操作要么全部执行,要不都不执行,不存在部分执行。

无锁是一种乐观的策略,它假设所有对资源的访问都是没有冲突的,因此所有线程都可以不用阻塞的持续执行,一旦遇到冲突,无锁采用CAS(Compare And Swap)来鉴别冲突,一旦检测到冲突,则重试当前操作直到没有冲突为止。

CAS实际上是一个原子性的CPU指令。

具体思想:

CAS包含三个参数(内存地址,旧值,新值)只有当内存地址取的值与旧值一致时,才会把内存中的值更新为最新的值;若不等,则说明该值被其他线程修改了。每次CAS操作时,一定有一个线程会成功更新变量,而其他失败的线程不会挂起,而是允许继续重试,直到成功为止。
优点:

  1. 无锁的方式没有锁竞争带来的系统开销,也没有不同线程间频繁调度的开销,所以性能比锁更优越
  2. 对死锁免疫

缺点:

​ 1 ABA问题
ABA问题是指在CAS操作时,其他线程将变量值A改为了B,但是又被改回了A,等到本线程使用期望值A与当前变量进行比较时,发现变量A没有变,于是CAS就将A值进行了交换操作,但是实际上该值已经被其他线程改变过。
解决办法: 在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A。只要变量被某一线程修改过,变量对应的版本号就会发生递增变化,从而解决了ABA问题。
​ 2 循环时间长开销大

2.35 分布式锁实现方式?——业界常见分布式锁框架?

分布式部署的比如集群、微服务。服务节点之间需要通信的并且数据有强一致性要求和并发量要求。

方案:

1 redis 2 Zookeeoer 3 ETCD(自己查一下)

4 MySQL:用UNIQUE KEY 表级唯一,不能重复插入,通过MySQL保证同一个Key只有一个节点能插入成功,通过删除记录释放锁,存在单点问题。

Linux五种IO模型

阻塞式I/O
非阻塞式I/O
I/O复用(select,poll,epoll等)
信号驱动式I/O(SIGIO)
异步I/O(POSIX的aio_系列函数)

  • 4
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lcy~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值