C++岗位求职面试八股文第三十四篇(操作系统)

系列文章目录

第一篇:语言基础
第二篇:设计模式
第三篇:数据库
第四篇:计算机网络
第五篇:操作系统
第六篇:LInux
第七篇:数据结构
第八篇:智力题

[121]管程

信号量提供了一种方便且有效的进程同步机制,但是它们的使用错误可能导致难以检测的时序错误,因为这些错误只有在特定执行顺序时才会出现,而这些顺序并不总是出现。

为了处理这种错误,研究人员开发了一些高级语言工具,一种重要的、高级的同步工具,即管程(monitor)。
管程中包含了面向对象的思想,它将表征共享资源的数据结构及其对数据结构操作的一组过程,包括同步机制,都集中并封装在一个对象内部,隐藏了实现细节。封装于管程内部的数据结构仅能被封装于管程内部的过程所访问,任何管程外的过程都不能访问它;反之,封装于管程内部的过程也仅能访问管程内的数据结构。所有进程要访问临界资源时,都只能通过管程间接访问,而管程每次只准许一个进程进入管程,执行管程内的过程,从而实现了进程互斥。

[122]死锁产生的条件,如何解决前提是进程或者线程死锁: 是指多个进程在执行过程中,因争夺资源而造成了互相等待。此时系统产生了死锁。

产生的条件:死锁发生有四个必要条件:
(1)互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问,只能等待,直到进程使用完成后释放该资源;
(2)请求保持条件:进程获得一定资源后,又对其他资源发出请求,但该资源被其他进程占有,此时请求阻塞,而且该进程不会释放自己已经占有的资源;
(3)不可剥夺条件:进程已获得的资源,只能自己释放,不可剥夺;
(4)环路等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

如何解决:
(1)资源一次性分配,从而解决请求保持的问题
(2)可剥夺资源:当进程新的资源未得到满足时,释放已有的资源;
(3)资源有序分配:资源按序号递增,进程请求按递增请求,释放则相反。(破坏环路等待条件)
在这里插入图片描述

[123]有了进程,为什么还要有线程?1原因

进程在早期的多任务操作系统中是基本的执行单元。每次进程切换,都要先保存进程资源然后再恢复,这称为上下文切换。但是进程频繁切换将引起额外开销,从而严重影响系统的性能。为了减少进程切换的开销,人们把两个任务放到一个进程中,每个任务用一个更小粒度的执行单元来实现并发执行,这就是线程。

2线程与进程对比
(1)进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换。
但多个线程共享进程的内存,如代码段、数据段、扩展段,线程间进行信息交换十分方便。
(2)调用 fork() 来创建进程的代价相对较高,即便利用写时复制技术,仍然需要复制诸如内存页表和文件描述符表之类的多种进程属性,这意味着 fork() 调用在时间上的开销依然不菲。
但创建线程比创建进程通常要快 10 倍甚至更多。线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表。

[124]单核机器上写多线程程序,是否要考虑加锁,为什么

是的。因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改引起冲突。

[125]多线程和多进程的不同

(1)一个线程从属于一个进程;一个进程可以包含多个线程。
(3)进程系统开销显著大于线程开销;线程需要的系统资源更少。
(4)多个进程在执行时拥有各自独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。
(5)多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。
(6)通信方式不一样。
7)多进程适应于多核、多机分布;多线程适用于多核

[126]互斥锁的机制,互斥锁与读写的区别?互斥锁机制:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。

用互斥锁保护临界区,从而防止竞争条件。也就是说,一个线程在进入临界区时应得到锁,它在退出临界区时释放锁

互斥锁和读写锁:
(1) 读写锁区分读者和写者,而互斥锁不区分
(2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

[127]信号量,有什么作用?

实现进程、线程的互斥、同步

[128]进程、线程的中断切换的过程是怎样的?

上下文切换指的是内核(操作系统的核心)在CPU上对进程或者线程进行切换。
而非内核态与用户态之间切换
进程上下文切换
(1)保护被中断进程的处理器现场信息
(2)修改被中断进程的进程控制块有关信息,如进程状态等
(3)把被中断进程的进程控制块加入有关队列
(4)选择下一个占有处理器运行的进程
(5)根据被选中进程设置操作系统用到的地址转换和存储保护信息
切换页目录以使用新的地址空间
切换内核栈和硬件上下文(包括分配的内存,数据段,堆栈段等)
(6)根据被选中进程恢复处理器现场

线程上下文切换
(1)保护被中断线程的处理器现场信息
(2)修改被中断线程的线程控制块有关信息,如线程状态等
(3)把被中断线程的线程控制块加入有关队列
(4)选择下一个占有处理器运行的线程
(5)根据被选中线程设置操作系统用到的存储保护信息
切换内核栈和硬件上下文(切换堆栈,以及各寄存器)
(6)根据被选中线程恢复处理器现场

[129]自旋锁和互斥锁的使用场景

互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑
  (1)临界区有IO操作
  (2)临界区代码复杂或者循环量大
  (3)临界区竞争非常激烈
  (4)单核处理器
自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下。

[130]多线程编程要注意什么,多线程加锁需要注意什么

1多线程编程需要考虑同步的问题。线程间的同步方式包括互斥锁、信号量、条件变量、读写锁。
2多线程加锁,主要需要注意死锁的问题。破坏死锁的必要条件从而避免死锁。

[121]sleep和wait的区别?

(1)sleep是一个延时函数,让进程或线程进入休眠。休眠完毕后继续运行。
(2)wait是父进程回收子进程PCB(Process Control Block)资源的一个系统调用。

[131]线程池的设计思路,线程池中线程的数量由什么确定?设计思路:生产者消费者模型

实现线程池有以下几个步骤:
(1)设置一个生产者消费者队列,作为临界资源。
(2)初始化n个线程,并让其运行起来
(3)当任务队列为空时,所有线程阻塞。
(4)当生产者队列来了一个任务后,先对队列加锁,把任务挂到队列上,然后使用条件变量去通知阻塞中的一个线程来处理,解锁
(5)消费者队列加锁,从消费者队列里取任务,执行,然后解锁。(没任务,阻塞解锁;有任务,不阻塞不解锁)

线程数量:
线程数量和哪些因素有关:CPU,IO、并行、并发
如果是CPU密集型应用,则线程池大小设置为:CPU核心数+1
如果是IO密集型应用,则线程池大小设置为:2CPU核心数+1
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)
CPU数目
所以线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

[132]为什么要创建线程池

创建线程和销毁线程的花销是比较大的,这些时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程,再加上业务工作线程,消耗系统资源的时间,可能导致系统资源不足。同时线程池也是为了提升系统效率。

[133]线程池的核心线程与普通线程

线程池中有两类线程:核心线程和非核心线程。核心线程默认情况下会一直存在于线程池中,即使这个核心线程什么都不干,而非核心线程如果长时间的闲置,就会被销毁。

线程总数量最大值=核心线程数+非核心线程数

任务队列可以存放100个任务,此时为空,线程池里有10个核心线程,若突然来了10个任务,那么刚好10个核心线程直接处理;若又来了90个任务,此时核心线程来不及处理,那么有80个任务先入队列,再创建核心线程处理任务;若又来了120个任务,此时任务队列已满,不得已,就得创建20个普通线程来处理多余的任务。
在这里插入图片描述在这里插入图片描述

[134]进程和线程相比,为什么慢?

1进程系统开销显著大于线程开销;线程需要的系统资源更少。

2进程切换开销比线程大。多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。

3进程通信比线程通信开销大。进程通信需要借助管道、队列、共享内存,需要额外申请空间,通信繁琐;而线程共享进程的内存,如代码段、数据段、扩展段,通信快捷简单,同步开销更小。

[135]Linux零拷贝的原理?

什么是零拷贝:
「零拷贝」描述的是计算机操作系统当中,CPU不执行将数据从一个内存区域,拷贝到另外一个内存区域的任务。通过网络传输文件时,这样通常可以节省 CPU 周期和内存带宽。

零拷贝的好处:
(1)节省了 CPU 周期,空出的 CPU 可以完成更多其他的任务
(2)减少了内存区域之间数据拷贝,节省内存带宽
(3)减少用户态和内核态之间数据拷贝,提升数据传输效率
(4)应用零拷贝技术,减少用户态和内核态之间的上下文切换

零拷贝原理:(用户态和内核态之间没有拷贝,内核态和硬件发生拷贝)
在这里插入图片描述Linux 提供了 sendfile() 用来减少我们的数据拷贝和上下文切换次数。
在这里插入图片描述a. 发起 sendfile() 系统调用,操作系统由用户态空间切换到内核态空间(第一次上下文切换)
b. 通过 DMA 引擎将数据从磁盘拷贝到内核态空间的输入的 socket 缓冲区中(第一次拷贝)
c. 将数据从内核空间拷贝到与之关联的 socket 缓冲区(第二次拷贝)
d. 将 socket 缓冲区的数据拷贝到协议引擎中(第三次拷贝)
e. sendfile() 系统调用结束,操作系统由用户态空间切换到内核态空间(第二次上下文切换)
根据以上过程,一共有 2 次的上下文切换,3 次的 I/O 拷贝。我们看到从用户空间到内核空间并没有出现数据拷贝,从操作系统角度来看,这个就是零拷贝。内核空间出现了复制的原因: 通常的硬件在通过DMA访问时期望的是连续的内存空间。

在这里插入图片描述

[136]多进程多线程和IO复用区别

I/O的多路复用定义:单个线程通过记录、跟踪每一个I/O流的状态来同时管理多个I/O流。

当有多个并发连接请求时,多线程或者多进程模型需要为每个连接创建一个线程或者进程,而这些进程或者线程中大部分是被阻塞起来的(受cpu个数限制)

使用I/O多路复用时,处理多个连接只需要1个线程监控就绪状态,对就绪的每个连接开一个线程处理(由线程池支持)就可以了,这样需要的线程数大大减少,减少了内存开销和上下文切换的CPU开销。
在这里插入图片描述

[137]同步异步阻塞非阻塞

在这里插入图片描述
同步和异步:描述用户线程与内核的交互方式。(同步:用户线程发起I/O请求后需要等待或轮询内核I/O操作完成后才能继续执行。异步:用户线程发起I/O请求后继续执行,当内核I/O操作完成则会通知用户线程,或调用用户注册的回调函数。)

阻塞和非阻塞:描述用户线程调用内核I/O操作的方式。(阻塞:I/O操作在没有接收完数据或者没有得到结果之前不返回,需要彻底完成后才返回到用户空间。非阻塞:I/O操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完
成。)

[138]简述epoll和select的区别,epoll为什么高效区别:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;而epoll保证了每个fd在整个过程中只会拷贝一次。
(2)每次调用select都需要在内核遍历传递进来的所有fd;而epoll只需要轮询一次fd集合,同时查看就绪链表中有没有就绪的fd就可以了。
(3)select支持的文件描述符数量太小了,默认是1024;而epoll没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048。

epoll为什么高效:其他两个需要拷贝、轮询
(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。
(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把当前进程往设备等待队列中挂一次,而epoll只要一次拷贝,而且把当前进程往等待队列上挂也只挂一次,这也能节省不少的开销。

[139]同步/异步io,同步异步区别在哪⾥根据系统IO操作的就绪状态分为阻塞和非阻塞;

根据应用程序和内核的交互方式分为同步和异步,其中同步才区分阻塞和非阻塞,异步则一定是非阻塞

同步IO和异步IO的区别就在于: 数据拷贝的时候进程是否阻塞

阻塞IO和非阻塞IO的区别就在于: 应用程序的调用是否立即返回

同步:指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行
异步:指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程或者调用用户线程注册的回调函数
阻塞:指IO操作需要彻底完成后才返回到用户空间,如果不就绪就一直等待
非阻塞:指IO操作被调用后立即返回给用户一个状态值,无需等待IO操作彻底完成,不管就不就绪,都立即返回
同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还会抢占cpu去执行其他逻辑,也会主动检测io是否准备好

异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

阻塞是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。

非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回

  1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。
  2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)。
  3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
  4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者

这四种在描述的时候以用户线程描述,不要加主线程子线程这类型的

[140]多路IO复用技术有哪些,区别是什么?

select,poll,epoll都是IO多路复用的机制,I/O多路复用就是通过一种机制,可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。
区别:
(1)poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次
(2)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。
(3)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把当前进程往设备等待队列中挂一次,而epoll只要一次拷贝,而且把当前进程往等待队列上挂也只挂一次,这也能节省不少的开销。

[续]C++岗位求职面试八股文第三十五篇(操作系统)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IT与Fintech

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

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

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

打赏作者

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

抵扣说明:

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

余额充值