占坑,C++问的更符合linux而且更难,想去腾讯可能还要在这个基础上深挖
说说操作系统组成/用户态与核心态区别
操作系统位于软件和硬件的中间层次,它就像一个管家,负责管理协调硬件,软件等计算机资源的工作。从上往下看,它主要作为系统资源的管理者,为上层用户,应用程序提供简单易用的服务。从下往上看,它是最接近硬件的系统软件。
因为不同指令之间存在权限差异,CPU分为了用户态和内核态,内核态用来运行特权指令和内核程序。所以对应的操作系统分成非内核功能和内核功能,其中内核是操作系统最主要最核心的部分,内核又分为微内核和大内核两个体系。
微内核是只把最基本的功能保留在内核,比如时钟管理,中断处理,原语。优点是内核功能少,方便维护。缺点就是需要频繁在核心态和用户态之间切换,性能低
大内核是把操作系统主要的功能模块作为系统内核运行在核心态,主要在微内核的功能上加了对系统资源的管理。(进程管理,存储器管理,设备管理)。优点是性能高,缺点是内核代码庞大,难以维护
说说中断的类型
发生中断之后,操作系统才能获得计算机的控制权,开展管理工作,比如进程切换,I/O设备分配等。只有中断才可以使CPU从用户态切换到核心态(中断是用户态到核心态唯一途径)。
中断的类型:
- 内中断(也称异常,陷入)-CPU内部,与当前执行的指令有关
- 自愿中断(指令中断,程序自身调用陷入命令)
- 强迫中断(硬件故障,软件异常中断)
- 外中断(狭义的中断)-CPU外部,与当前的指令无关
- 外设请求(I/O操作完成)
- 人工干预(用户强行终止某个进程)
外中断的过程
- 执行完每个指令之后,CPU都要检查当前是否有外部中断信号
- 如果检测到外部中断信号,则需要保护被中断进程的CPU环境(如程序状态字PSW,程序计数器PC,各种通用寄存器)
- 根据中断信号类型转入相应的中断处理程序
- 恢复原进程的CPU环境并退出中断,返回原进程继续往下执行
进程状态生命周期-七状态模型
- 运行态:占有CPU,并在CPU上运行
- 就绪态:已经具备运行条件,但由于没有空闲CPU,暂时不能运行
- 阻塞态(等待态):因等待某一事件而暂时不能运行
- 创建态:进程正在被创建,操作系统为进程分配资源,初始化PCB
- 终止态:进程正在从系统中撤销,操作系统会回收进程拥有的资源,撤销PCB
- 就绪挂起:挂起就是指内存不够了,将暂时不能运行的进程调到外存等待。等它重新具备了运行条件且内存有空闲后,重新调入内存。这里就是调就绪队列中的进程挂起
- 阻塞挂起:挂起就是指内存不够了,将暂时不能运行的进程调到外存等待。等它重新具备了运行条件且内存有空闲后,重新调入内存。这里就是调就绪队列中的进程挂起
- “挂起”和“阻塞”区别:两种都是暂时不能获得CPU服务,但阻塞态进程还在内存中,挂起态除了PCB其他都不在内存中了。
两种条件
- 一种是CPU准备就绪
- 一种是其他条件就绪
![5784bfb20b9569cca99bb43355d88852.png](https://i-blog.csdnimg.cn/blog_migrate/9e1bea011ce7f5ae5d447dca51e82d41.jpeg)
进程切换的过程
进程通过操作系统进行切换。操作系统会使用时钟中断机制,每隔一段时间产生中断来实现从用户态切换到内核态,从而执行进程的切换。
进程具体的切换过程:
- 更新PCB中的信息(修改进程状态标志,将运行环境保存到PCB,从PCB恢复运行环境)
- 将PCB插入合适的队列(就绪队列或者阻塞队列)
- 分配/回收资源
进程和线程区别
标准答案:进程是资源分配的最小单位,线程是CPU调度的最小单位
我个人的理解是 首先为什么要引入进程?
没有进程的操作系统只能运行单个程序,对于多个程序在内存分配的位置和大小都需要管理,所以进程是系统资源的分配单位。尤其引入线程之后,进程作为除了CPU之外系统资源的分配单元(如打印机,内存空间)。线程只占了CPU,剩下还是以进程为单位划分。
为什么要引入线程?
引入进程可以使各个程序之间并发执行,但是对于一个程序内部来说所有的功能如果都限制为顺序执行显然对用户体验不好。为此我们引入线程的概念,来增加进程内部的并发度,比如我们用QQ发送文件的时候还可以聊天。
所以线程是CPU的调度单位。因为它的出现就是为了将进程进行更细粒度的执行单元划分。
![351a02617f9bffbf1636eca60c5e5d84.png](https://i-blog.csdnimg.cn/blog_migrate/065fedd31f45129636d19dc16b21e467.png)
补充,引入线程之后,并发带来的系统开销减少,因为线程上下文切换的代价更低,因为线程共享同一个进程的地址空间,它的切换不需要使用新的地址空间。而进程需要地址空间的切换,开销更大。
进程给线程划分了什么资源
线程共享的环境包括:
1.进程代码段
2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)
3.进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。
线程独立的资源包括:
1.线程ID
每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。
2.寄存器组的值
由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上 时,必须将原有的线程的寄存
器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。
3.线程的堆栈
堆栈是保证线程独立运行所必须的。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的
函数堆栈, 使得函数调用可以正常执行,不受其他线程的影响。
4.错误返回码
由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了errno值,而在该 线程还没有处理这个错误,
另外一个线程就在此时被调度器投入运行,这样错误值就有可能被修改。所以,不同的线程应该拥有自己的错误返回码变量。
5.线程的信号屏蔽码
由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都 共享同样的信号处理器。
6.线程的优先级
由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。
进程之间怎么通信的/线程是怎么通信的
进程通信的三种方式
- 共享存储
- 操作系统负责提供共享空间和同步互斥工具,进程对共享空间的访问必须是互斥的。
- 在内存中画出一块共享存储区
- 消息传递
- 进程间的数据交换以格式化的消息(message)为单位。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。
- 管道通信
- 管道”是用于连接读写进程的一个共享文件,其实就是在内存中开辟一个大小固定的缓冲区
- 管道是半双工通信,只能实现单向传输
- 当管道写满时,写进程将被阻塞,等待读进程将数据读走。如果管道为空,读进程将被阻塞,等待写进程写入数据
- 如果没写满,就不允许读。如果没读空,就不允许写
线程通信的三种方式
- 每个进程有自己的地址空间,所以进程间的通信是通过操作系统进行;
- 多线程共享地址空间和数据空间,所以多线程通信可以直接多线程使用,而不必通过操作系统。所以线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
- 锁机制:包括互斥锁、条件变量、读写锁
- 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
- 信号机制(Signal):类似进程间的信号处理
一个进程最大能占有多大的内存空间(考虚拟化)
如果不采用虚拟化技术,就是内存最大容量
如果采用虚拟化技术,就是min(内存与外存的容量之和,CPU寻址范围)
如果某计算机地址结构为32位,按字节编址,内存大小为512MB,外存2GB
最大容量:2^32B
实际容量:min(2^32B,512MB+2GB)
- 虚拟内存的最大容量受计算机地址位数决定(因为问的是最大容量,所以是假设磁盘空间无限大)
- 虚拟内存的实际容量=min(内存和外存容量之和,CPU寻址范围) (换出的进程只能到外存空间里)
进程调度方式(是否需要往深了解Linux的实际调度算法)
(抢占式是指当新进程到达时会重新触发CPU分配机制)
先到先服务(FCFS)调度算法 :
-
- 算法思想:按照进程到达就绪队列的先后顺序进行服务。
- 优点:公平,算法实现简单
- 缺点:排在长作业后的短作业需要等待很长时间,带权周转时间很大,短作业用户体验不好。对长作业有利,对短作业不利。
短作业优先(SPF)调度算法 :
- 算法思想:最短的作业/进程优先得到服务(“最短”是指运行时间最短)
- 优点:在所有进程同时可运行时,采用短作业优先算法的平均等待时间,平均周转时间,平均带权周转时间最少。(因为类似贪心,对于减少了短作业的等待时间)
- 缺点:不公平。对短作业有利,对长作业不利。可能产生饥饿现象
高响应比优先调度算法
- 算法思想:综合考虑FCFS算法和SPF算法,每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务。响应比=(等待时间+要求服务时间)/要求服务时间
- 优点:综合考虑了SPF和FCFS的优点,当等待时间相同时,要求服务时间短的优先(SPF优点);当运行时间相同时,等待时间长的优先(FCFS优先)。同时对于长作业来说,随着等待时间越来越久,响应比也会越来越大,从而避免了长作业饥饿问题。
- 缺点:
时间片轮转调度算法 :
- 算法思想:公平地,轮流地位各个进程服务,让每个进程在一定时间间隔内都可以得到响应
- 优点:公平,响应快
- 缺点:由于高频率地进程切换,因此有一定开销;不区分任务地紧急程度
- 时间片大小:
- 太大使得每个进程都可以在一个时间片内就完成,退化为先来先服务调度算法
- 太小,进程切换过于频繁,导致不必要地切换开销。
优先级调度
- 算法思想:每个进程有各自的优先级,调度时选择优先级最高的进程
- 优点:可以区分任务紧急程度
- 缺点:可能导致饥饿:
多级反馈队列调度算法
- 算法思想:各级队列优先级从高到低,时间片从小到大(每个队列有固定的时间片),新进程到达时先进入第1级队列,按FCFS原则排队等待被分配时间片,若用完时间片进程还未结束,则进程进入下一级队列队尾,若此时已经在最下级队列,则重新放回最下级队列队尾
(它是抢占式算法,所以如果新进程到达第1级队列可以进行优先级抢占,那么原运行进程会放到原队列队尾)
- 优点:对各类型进程相对公平(FCFS优点);每个新到达的进程都可以很快就得到响应(RR的优点);短进程只用较少时间就可以 完成(SPF优点);不必实现估计进程的运行时间(避免用户作假);可灵活地调整对各类进程的偏好程度,比如将因I/O阻塞的进程重新放回原队列,这样I/O进程就可以保持较高优先级)
- 缺点:如果一直有新进程到达,可能导致饥饿
僵死进程怎么产生的
- 孤儿进程:父进程在子进程终止之前终止,那么此时子进程被称为孤儿进程,他们的父进程会被更改init进程。init进程收养的子进程永远不会变成僵死进程,因为只要有一个子进程终止,init就会调用一个wait函数取得其终止状态。
- 僵死进程:一个已经终止,但父进程并没有调用wait或waitpid进行善后处理(获取终止进程信息,释放它所占用的资源)的进程称之为僵死进程。
避免僵死进程的方法是调用fork()两次,将第一次fork子进程终止,这样第二次fork的子进程就会被init进程收养,交给init进程来获取子进程的终止状态。
fork子进程在linux里时两倍的进程开销吗
内存子进程经过 fork 操做产生,占用内存大小等同于父进程,理论上须要两倍的内存。
但是可以采用写时复制(copy-on-wirte)技术,即父子进程会共享相同的物理内存区域,当父子进程试图修改这些区域时,会建立对应的副本。
操作系统内存模型
(关注内存碎片)
- 内部碎片:分配给某进程的内存区域大于其实际需要的大小
- 外部碎片:指内存中因为分配与回收的过程产生的空闲分区由于太小而无法利用。(如果是一开始划定的区域太小这种不属于。所以固定分区分配无外部碎片)
- 连续分配管理方式:为一个进程分配一个连续的内存空间
- 连续分配会引入外部碎片或者内部碎片,其本质问题就在于要求进程占用的必须是一整段,连续的内存区域。这个区域长度不固定且无法估计。
- 所以如果允许进程分散地按固定大小的装入各个不相邻的分区中,便可以充分利用内存。
- 非连续分配管理方式:允许一个进程使用离散的内存空间
单一连续分配-连续分配
- 内存被分为系统区和用户区,用户区只能存放一道用户程序
- 优点:实现简单,无外部碎片,不需要内存保护
- 缺点:只适用于单用户,单任务的操作系统中;有内部碎片
固定分区分配-连续分配
- 将整个用户空间划分为若干各固定大小/不定大小的分区,每个分区只装入一道作业。
- 优点:实现简单,无外部碎片
- 缺点:有内部碎片,内存利用率低。(比如说10M的程序,占用了12M的一个分区)
动态分区分配-连续分配
- 它不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程需要。因此系统分区的大小和数目可变
- 优点:无内部碎片,提高内存的利用率
- 缺点:有外部碎片(多次换入换出后,可能产生过小的空闲区),但是外部碎片可以用“紧凑技术”解决(对内存中的程序进行移动使之紧凑)
![ea8637e4fe96f809003a40180063fe7d.png](https://i-blog.csdnimg.cn/blog_migrate/48855e12f85a4738558eaf93a6b1fd3b.jpeg)
(分了一个就会把空闲区划分为两个)
分页存储管理-非连续分配
- 算法思想:
- 把内存分为一个个相等的分区-页框,把分区大小把进程拆分成一个个相等部分-页面。
- 进程的页面与内存的页框是一一对应的关系,否则就会出现内部碎片
- 逻辑地址转换过程(两次访问内存(查页表+访问目标内存单元)):
- 算出逻辑地址的页号
- 获得该页号对应页面在内存页框的起始地址
- 算出逻辑地址在页面内的“偏移量”
- 物理地址 = 页面始址+页内偏移量
- 访问物理内存对应的内存单元
![e87751c98c0a5d5210bb78957c739e30.png](https://i-blog.csdnimg.cn/blog_migrate/e8eb7dc64daec5e9b75bb0ea46238a6b.png)
页表管理中很重要两个问题
虚拟地址到物理地址的转换要快--快表变换机制
快表(TLB)--实际上就是一个二级缓存。 是一种访问速度比内存快很多的高速缓冲存储器,用来缓存使用的页表。正常的页表查询需要两次访存,但是如果缓存命中的话只需要一次访存(任何只要用快表都是一次访存)
![44596deaeacc97f5c2b3d12c680835d0.png](https://i-blog.csdnimg.cn/blog_migrate/bbaf2eeb37bfdbcc59ae5640229b79bf.jpeg)
![c69d34c87a102b467def52e4fedf9774.png](https://i-blog.csdnimg.cn/blog_migrate/5ecef550354aad3f7669f41ad1f90b92.png)
当进程很大时,页表也会很大--二级页表
可将页表进行分组,使每个内存块刚好可以放入一个分组,进行离散存储(比如,页面大小4KB,每个页表项4B,每个页面可存放1K个页表项,因此每1K个连续页表项为一组,每组刚好占一个内存块,再将各组离散地放到各个内存块中)
地址变换过程:
- 第一次访存:访问内存中的页目录表
- 第二次访存:访问内存中的二级页表
- 第三次访存:访问目标内存单元
![561d3f2a4081353564a2e9a0531f0c8b.png](https://i-blog.csdnimg.cn/blog_migrate/7f3c0fdd0acf36323443627894e7efba.png)
段式内存管理-非连续分配
分段和分页的对比
- 页是信息的物理单位。分页的主要目的是为了实现离散分配,提高内存利用率。分页仅仅是系统管理的需要,完全是系统行为,对用户是不可见的。页的大小固定
- 段是信息的逻辑单位。分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。段的大小由用户决定
- 分页的用户进程地址空间是一维的,程序员只需要给出一个逻辑地址
- 分段的用户进程地址空间是二维的,程序员需要给出段名和段内地址(因为各段长度不一致所以无法自动划分段号)
- 分段比分页更容易实现信息的共享和保护。(因为可以根据逻辑对共享区域和互斥区域进行划分)
- 分页和分段都需要两次访存
优点 | 缺点 | |
---|---|---|
分页管理 | 内存空间利用率高,不会产生外部碎片,只会有少量页内碎片 | 不方便按照逻辑模块实现信息的共享和保护 |
分段管理 | 很方便按照逻辑模块实现信息的共享和保护 | 如果段长过大,为其分配很大的连续空间会很不方便。另外段式管理会产生外部碎片(相当于动态分配) |
段页式内存管理-非连续分配
逻辑地址由段号,页号,页内地址(页内偏移量)组成
- 段号的位数决定了每个进程最多可以分几个段
- 页号位数决定了每个段最大有多少页
- 页内偏移量决定了页面大小,内存块大小是多少
地址转换过程
查询内存的次数:
- 第一次访存:查内存中的段表
- 第二次访存:查内存中的页表
- 第三次访存:访问目标内存
![b2f3f6cf8b025d2533ca80360767bfbb.png](https://i-blog.csdnimg.cn/blog_migrate/91cb07bb21efd48ef23a1fcfb4a21aab.jpeg)
- 由逻辑地址得到段号,页号,页内偏移量
- 段号与段表寄存器中的段长度比较,检查是否越界
- 由段表中记录的页表长度,检查页号是否越界
- 由段表中的页表地址,页号得到查询页表,找到相应页表项
- 由页面存放的内存块号,页内偏移量得到最终的物理地址
- 访问目标单元
虚拟内存管理-非连续分配
基于局部性原理
- 在程序装入时:可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
- 在程序执行过程中:当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
- 在程序执行过程中:若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存
- 虚拟内存的实际容量=min(内存和外存容量之和,CPU寻址范围) (换出的进程只能到外存空间里)
(局部性原理是什么,不问不用介绍)
- 时间局部性:如果执行了程序的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问
- 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近存储单元页很有可能被访问。(因为内存连续性,很多数据都是连续存放的)
(虚拟存储器)
就是通过虚拟内存管理为用户提供了一个比实际内存大的多的存储器,虚拟内存管理实际上是一种时间换空间的策略,用 CPU 的计算时间,页的调入调出时间,换来了一个虚拟的更大的内存空间来支持程序的运行。
页面置换算法-虚拟内存
虚拟内存访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存
![b1358df3cfcb3951e5f9b35fb17d2ca9.png](https://i-blog.csdnimg.cn/blog_migrate/ae61c8a4efa11c917c1119947b22f7b4.jpeg)