文件管理
文件的逻辑结构
文件内部的数据应该是如何组织起来的。
无结构文件
流式文件:由二进制流或字符流组成 .txt
有结构文件
有一组相似的记录组成,又称为记录式文件,每条记录有一个数据项可作为关键字(key)
定长记录
:各条记录的长度(所占用的空间)相等,你用2b,我也用2b
,反之不定长就可能你用2b,我用4b
- 顺序文件
- 索引文件
- 索引顺序文件
顺序文件
存储
顺序存储
(逻辑上相邻物理也相邻)链式存储
(逻辑上相邻物理不一定相邻)
结构
串结构
:记录之间的顺序与关键字无关顺序结构
:记录之间的顺序按照关键字排序
思考:假设已知了文件的起始地址(第一个记录存放的位置),能否快速找到第i个记录对应的地址(即实现随机存取)?
答案:定长记录的顺序文件,若物理上采用顺序存储,则可以实现随机存取,若能再保证记录的顺序结构,则可以实现快速检索(即根据关键字款素找到对应记录)
缺点:的确顺序存储的顺序文件能够快速检索,但是对于增加/删除一个记录比较困难(顺序表和链表的区别)
索引文件
- 索引表
本身是定长记录的顺序文件
- 索引号:关键字
- 增删记录时,就直接对索引表进行修改
- 可以根据不同的数据项建立多个索引表
索引顺序文件
索引顺序文件 = 索引文件 + 顺序文件(关键字排)
索引顺序文件的效率分析:
多级索引顺序文件
上面写的挺清楚的!
文件的目录结构
目录本身就是一种有结构的文件,即是由一条条记录构成的,每条记录对应一个放在这个目录下的一个文件(记录–>文件)
-
单级目录结构
- 整个系统只建立一个目录表,每个文件占一个目录项
- 缺点:不适合多用户操作系统
-
两级目录结构
- 分为主文件目录MFD(记录用户名以及用户文件的目录存放位置)和用户文件目录UFD(里面就包含了这个用户的所有文件FCB)
- 缺点:已经改进了,但是用户不能对自己的文件进行分类
-
多级目录结构
- 用户要访问某个文件时要用文件路径名表示文件,文件路径名是个字符串,用/隔开,从根目录出发的叫做
绝对路径
。 - 从根目录出发,读目录表,找文件位置,再从文件继续读
- 从当前目录出发就叫做
相对路径
- 相对路径可以减少磁盘IO次数,从而提升访问文件的效率
- 用户要访问某个文件时要用文件路径名表示文件,文件路径名是个字符串,用/隔开,从根目录出发的叫做
-
无环图目录结构
索引节点(FCB的改进)
文件的物理结构
文件块、磁盘块:类似于内存分页,磁盘中的存储单元也会被分为一个个的块,磁盘块或者物理块。
内存与磁盘的数据交换(读写操作,IO)都是以块为单位进行的,即每次读入一块或者写出一块。
相当于,用户(进程)操作的是逻辑地址,系统帮忙把逻辑地址映射到物理地址。
连续分配
如何进行映射?
连续分配优点
:
- 支持顺序访问和直接访问;连续分配的文件在顺序访问时速度最快
连续分配缺点
:
- 文件不好扩展,如果需要给文件新增加一个磁盘块,但是在这段连续分配的前后都没有了。
- 存储空间利用率低,就会产生难以利用的磁盘碎片。
链接分配
可以为文件分配离散的磁盘块
- 隐式链接
- 显式链接
1. 隐式
i+1次磁盘IO
- 采用链式
- 只支持顺序访问,
不支持随机访问
,查找效率低。 - 每个块中的指针占用存储空间
方便文件扩展,不会有碎片问题,外存利用率高
2. 显式
相当于隐式,各个物理块的指针形成了一个文件分配表FAT
一个磁盘仅设置一张fat
,在开机时,fat读入内存,并常驻内存
- 逻辑地址转换到物理地址不需要读磁盘操作,因为常驻内存,就在内存的fat文件中找这个块的下一个块是什么,相反如果隐式链接的话,下一个块号是保存在物理块内的,那么获取下一个就必然要进行读磁盘操作。
- 支持随机访问
- 也不会产生外部碎片,方便扩展
对比:
索引分配
- 索引块保存索引表,索引表里有对应的数据块
- 显示链接方式式一个磁盘一个fat文件,索引分配一个文件对应一个表
1. 链接方案
链接始终考虑最坏的情况,占满最大,还只访问最后的那个索引块,那么就必须顺序读入前255个索引块,效率非常差。
2. 多层索引
一级索引表+二级索引表+物理块等于3次IO
3. 混合索引
小结
文件管理空闲磁盘块算法
空闲表
和内存分配差不多,首次适应、最佳适应、最坏适应、等。。
回收注意,临近分区要合并
空闲链表
以盘块或者盘区
-
位示图法
-
成组链接法
文件的基本操作原理
打开文件
理解用户进程的打开文件表
和系统的打开文件表
(系统中只有一张)
因此打开文件的流程
- 根据文件路径存放的
目录文件
- 目录文件就有打开的
文件目录项
- 目录项复制到
内存的打开文件表
,并返回对应的表的内容给用户。 - 之后用户使用
打开文件表
的编号来指明操作的文件
关闭文件
- 将进程的
打开文件表
对应的表项删除 回收分配
改文件的内存空间等资源(用不上了)打开文件表
的打开计数器coun
t减1,count = 0
则删除对应表项。
文件共享
文件保护
文件系统的层次结构
示例:
讲的很清楚!
磁盘管理
磁盘结构
磁盘调度算法
-
最短寻找时间优先算法(SSTF)
-
扫描算法(SCAN)
-
LOOK算法
相当于比scan多增加了一个判断边界条件 -
循环扫描算法(S-SCAN)
-
C-LOOK算法
I/O管理概述
操作系统控制I/O设备
CPU可控制IO控制器,IO控制器来控制设备的机械部件(如键盘)
IO控制器
地址线
指明要操作的设备数据线
取出输入和输出的数据,数据放在寄存器里控制线
发出指令- IO控制器可能会对应多个设备
- 数据寄存器&控制寄存器&状态寄存器可能有多个
- 有时让寄存器占用内存的一部分,成为·
内存映像IO
- 有一些计算机采用IO专用地址,即
寄存器独立编址
操作系统控制I/O设备的方式
程序直接控制方式
中断驱动
由于程序直接控制方式CPU利用率低,忙等,所以提出了中断驱动方式。
DMA方式
虽然中断驱动方式解决了程序直接控制方式的问题,但是每一次只能读/写一个字
,导致CPU频繁切换,耗费了很多时间。于是人们又发明了DMA方式。
DMA控制器:
一条指令只能对应一个数据块,因此如果要读写多个离散存储的数据块,就需要多次IO指令,从而要完成多次中断指令
通道控制方式
通道控制方式是为了解决DMA方式连续存储的问题
四种方式总结
I/O软件的层次结构
用户层软件
:实现了与用户交互的接口,用户可直接使用该层提供的库函数对设备进行操作,然后操作会被软件翻译成IO请求,并且通过系统调用请求操作系统内核的服务。
设备独立性软件
:向上提供接口(系统调用)、设备保护(权限)、差错处理、设备的分配和回收、缓冲区数据(缓冲技术交换数据)、建立逻辑设备与物理设备的映射(物理的打印机1叫啥名)
设备驱动程序
:IO设备有很多种,从上层接受到的命令转为设备能听懂的操作,不同的厂家有不同的硬件特性,因此需要驱动程序向IO控制器发出具体命令
中断处理程序
:等待IO完成的进程应该被阻塞,因此需要进程切换,进而必然需要中断处理
I/O核心子系统以及功能
假脱机技术(SPOOLing)
: 需要请求“磁盘设备”的设备独立性软件服务。
IO调度
:用一些算法来确定一个好的顺序来处理各个IO请求
设备保护
:实现文件保护功能,不同的用户对文件会有不同的访问权限(r、rw…)
缓冲区管理
1. 单缓冲
2. 双缓冲
3. 循环缓冲区
4. 缓冲池
面试题
- 创建进程的步骤
1.在内存中申请空白PCB
2.为新进程分配资源
3.初始化PCB
4.将进程插入就绪队列
- 进程和线程的区别?
进程
是资源管理的基本单位、线程
是程序执行的基本单位
线程的上下文切换
比进程上下文切换快的多
进程是拥有资源的一个独立单位,线程不拥有资源
,但是可以访问进程的资源
- 一个进程可以创建多少线程,和什么有关?
理论上,一个进程可用虚拟空间是2G,默认情况下,
线程的栈的大小是1MB
,所以理论上最多只能创建2048个线程
。如果要创建多于2048的话,必须修改编译器的设置。
因此,一个进程可以创建的线程数由可用虚拟空间和线程的栈的大小共同决定,只要虚拟空间足够,那么新线程的建立就会成功。如果需要创建超过2K以上的线程,减小你线程栈的大小就可以实现了,虽然在一般情况下,你不需要那么多的线程。过多的线程将会导致大量的时间浪费在线程切换上,给程序运行效率带来负面影响
。
- 协程和线程的区别?
线程和进程都是同步机制,而协程是异步机制。
一个线程可以有多个协程
线程是抢占式,而协程是非抢占式的,因此同一时间只有一个协程有运行权,相当于单线程,并且能够保留上一次调用的状态。
协程不被内核管理,而完全由程序控制。
- 并发和并行的区别?
并发
就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。
并行
是在同一时刻,多个任务同时执行。
在单核处理器能够并发,在多核能并行。
- 进程和线程切换流程
1、进程
切换页表以使用新的地址空间
,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
2、切换内核栈和硬件上下文
线程和进程的最大区别就在于地址空间
,对于线程切换,第1步是不需要做的,第2步是进程和线程切换都要做的,并且线程切换时只需保存和设置少量寄存器内容,因此开销很小
因为每个进程都有自己的虚拟地址空间
,而线程是共享所在进程的虚拟地址空间的
,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换
5.为什么虚拟地址空间切换会比较耗时?
每个进程都有自己的虚拟地址空间
(外存),进程也有自己的页表
,那么进程切换后页表肯定也会改变,那么TLB
(TLB本质上就是一个Cache,是用来加速页表查找的)就失效了,那么虚拟地址映射物理地址命中率变低,就需要不断请求页面和置换,这样程序就会比变慢。
然而线程的切换不会导致TLB失效,因为线程不拥有资源,无需切换虚拟地址空间,因此通常说线程切换比进程快
。
- 进程通信的方式?
管道
:半双工、数据单向流动,只能在父子进程关系使用
信号量
:信号量是一个计数器,可以用来控制多个进程对共享资源的访问,它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
消息队列
:类似于寄信和收信,进程向一个队列中写入消息,需要则从队列中拉取
共享内存
:这段共享内存由一个进程创建,但多个进程都可以访问,要注意保持同步吧,不要一个在写,一个又在读
Socket
:它可用于不同机器间的进程通信
- 进程间同步的方式有哪些?
临界区
:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问,只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
互斥量
:为协调共同对一个共享资源的单独访问而设计的。互斥量跟临界区很相似,比临界区复杂, 互斥对象只有一个,只有拥有互斥对象的线程才具有访问资源的权限。
信号量:
为控制一个具有有限数量用户资源而设计。它允许多个线程在同一时刻访问同一资源,但是 需要限制在同一时刻访问此资源的最大线程数目。互斥量是信号量的一种特殊情况,当信号量的最大资 源数=1就是互斥量了。
- 线程同步的方式?
临界区
:当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问 被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止, 以此达到用原子方式操 作共享资源的目的。
事件
:事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。
互斥量
:互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,但是更节省资源,更有效率。
信号量
:当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。
区别:
互斥量,信号量,事件都可以使用在多个进程之间,如果用在线程上,创建互斥量需要更多资源,因此在进程内部使用临界区更好
临界区,同一个进程的多个线程。
- Linux下同步机制?
- POSIX信号量:可用于进程同步,也可用于线程同步。
- POSIX互斥锁 + 条件变量:
只能用于线程同步
。
- 线程的分类?
从线程的运行空间来说,分为用户级线程(user-level thread, ULT)和内核级线程(kernel-level, KLT)
内核级线程
:这类线程依赖于内核,又称为内核支持的线程或轻量级进程。无论是在用户程序中的线程 还是系统进程中的线程,它们的创建、撤销和切换都由内核实现。比如英特尔i5-8250U是4核8线程,这 里的线程就是内核级线程
用户级线程
:进程用线程库来完成创建和管理。
- 如何理解多线程?
首先说一下进程和线程之间的区别
一个进程中会有多个线程,都共享进程的资源,这一进程中所有并发线程都能读取和修改内存中的全局变量,而多个线程之间CPU的调度顺序是不可控的,因此对于临界资源的控制是格外的重要。
就好比在,做一次简单的i = i + 1在计算机中并不是原子操作,涉及内存取数
,计算
和写入
内存几个环节,而线程的切换有可能发生在上述任何一个环节中间,所以不同的操作顺序很有可能带来意想不到的结果。
要解决的问题
- 线程之间有无先后访问顺序(
线程依赖
问题)- 多个线程共享访问同一变量(
同步互斥
问题)线程不仅有自己的tid,也会有自己的栈空间,线程彼此之间是无法访问其他线程战上的内容的。
线程调度
只需要保存栈空间
内容,寄存器
数据,pc
程序计数器执行到那儿,这样相比进程还要切换虚拟地址的开销就要少很多。
- 上下文切换是什么?
内核管理PCB,而PCB记录了进程的全部状态信息。每一次进程调度就是一次上下文切换,所谓
上下文就是当前运行状态
,状态的内容包括了寄存器
、程序计数器
、内核数据结构
(页表、文件表)等。
进程执行时刻,内核可以决定抢占执行哪个进程,这个过程由内核调度器完成,当调度器选择了某个进程时,则称为该进程被调度,而这个被调度的过程必然会发生上下文切换(运行状态改变了)。
一次完整的上下文切换通常是指,进程原先运行于用户态,之后因为系统调用切换到了内核态,执行一些指令,完成上下文切换后回到用户态,切换了进程。
- 外中断和异常的区别?
外中断
:CPU 执行指令以外的事件引起,如 I/O 完成中断,时钟中断(时间片用完了)等等
异常
:异常时由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等
- 临界资源和临界区,解决冲突?
临界区
:每个进程中访问临界资源的那段程序
临界资源
:一次只能一个进程访问的资源
规定一次只能有一个进入,其他试图进入临界区的都只能等待。
进入临界区的进程要在有限时间内退出。
如果一个进程不能进入临界区,让出CPU,防止忙等
- 死锁,产生死锁的条件?
死锁:在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗就是,两个或两个以上进程一直阻塞,一直等待的情况。
四个必要条件:
互斥
:一个资源一次只能被一个进程使用
不剥夺条件
:不能从进程手里强行剥夺持有的资源
请求和保持
:即保持,又请求
循环等待
:若干进程形成一种环形等待资源的关系
- 进程调度策略的方法?
先来先服务
:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作 业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造 成了短作业等待时间过长。
短作业优先
:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
最短剩余时间优先
:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的 作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂 起当前进程,运行新的进程。否则新的进程等待。
时间片轮转
:将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队 首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止 该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
优先级调度
:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
多级反馈队列
:时间片轮转+优先级结合,分成多个时间片不一、优先级也不一的队列。避免多次上下文切换。
- 进程有哪些状态?
进程一共有 5 种状态,分别是
创建
、就绪
、运行
、终止
、阻塞
。
还要记住,五种状态转换发生的情况
- 说一下你理解中的内存?他有什么作用呢?
内存是用于存放数据的硬件,程序执行钱呢需要放在内存中才能被CPU处理。给内存编址(按字节/字),形成一个个小房间存储数据。
- 分页?
把内存空间划分为页 ,作为内存的基本单位。因为程序数据存储在不同的页面中, 而页面又离散的分布在内存中,页表记录映射关系,逻辑地址到物理地址之间的联系。
访问内存数据需要两次
(无快表的情况)
一次是从内存中访问页表,找到对应的起始地址,加上偏移地址得到物理地址
第二次就是根据得到的物理地址访问获取数据
- 分段?
分页是为了提高内存利用率,而分段是为了满足在编写代码的逻辑需求,比如共享数据。
分段内存管理中,地址是二维的,一维是段号,二维是段内地址,其中每个段的长度是不一样的。段内部是连续内存分配,但是段和段之间是离散分配的。
- 分页和分段的区别
分页是透明的,分段需要显式划分
分页地址是一维
的,分段是二维
的
页的大小不可变
,段可长短不一
分页主要是为了实现虚拟内存
,内存和外存之间切换
,而分段是共享数据
和保护独立的地址空间
- 内存交换和内存覆盖有什么区别?
交换:把RAM分成一块块小内存,每一块叫做页,当内存不足的时候,就将页的内容置换给外存中的空间上,以释放空间。
交换
:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度),把把处于等待状态(或CPU调度原则下被剥夺运行权力)的程序从内存移到辅存,把内存空间腾出来。
覆盖
:由于程序并不是在运行时就会把所有数据放入内存,因此内存空间就分为了固定区和若干个覆盖区,将活跃的放在固定区中,不变动。覆盖区就放那些需要进行需要调用的时候调用的数据区。
内存交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率
明显下降,就可以暂停换出。
- 在发生内存交换时,有些进程是被优先考虑的?
阻塞进程、优先级低的、或者停留考虑驻留在内存中的时间长度
PCB 会常驻内存,不会被换出外存
- 什么是快表,你知道多少关于快表的知识?
快表,又称
联想寄存器(TLB)
,是一种访问速度比内存快很多的高速缓冲存储器
,用来存放当前访问的若干页表项,以加速逻辑地址转为物理地址的过程。与此对应,内存中的页表常称为慢表。
- 如果系统中具有快表后,那么地址的转换过程变成什么样了?
① CPU给出逻辑地址,由某个硬件算得页号、页内偏移量,将页号与快表中的所有页号进行比较。
②
如果找到匹配的页号
,说明要访问的页表项在快表中有副本,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表命中,则访问某个逻辑地址仅需一次访存即可。③
如果没有找到匹配的页号
,则需要访问内存中的页表,找到对应页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表未命中,则访问某个逻辑地址需要两次访存(注意:在找到页表项后,应同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照-定的算法对旧的页表项进行替换)
因此只要快表命中,就可以节省很多时间。 因为局部性原理,–般来说快表的命中率可以达到90%以上。
- 物理地址,逻辑地址?
物理地址
:最终xx地址都会映射成物理地址,内存里的
逻辑地址
:是一个虚拟的,需要经过页表之类的查询在物理中真正的地址
- 逻辑地址转换为物理地址的基本过程?
通常会在系统中设置一个页表寄存器(PTR),寄存器存放了页表在内存中的起始地址F和页表长度M,进程未执行时,页表的始址和页表长度放在进程控制块(PCB) 中,当进程被调度时,操作系统内核会把它们放到页表寄存器中,页表的起始地址就是这个页表在内存中的位置,而且由于页表中包含多个页,是一片连续的区域,也需要一个页表长度。
- 根据逻辑地址计算页号和页内偏移量(公式+二进制)
- 判断是否越界,越界就发生中断,进程切换相关的内核程序恢复现场
- 查询页表,找到页号对应的块号,块号是一个物理地址
- 物理地址 = 内存块号 + 偏移地址
- 就可以利用物理地址区访问内存中的信息了。
因此,页式管理中地址是-维的。即,只要给出一个逻辑地址,系统就可以自动地算出页号、页内偏移量两个部分,并不需要显式地告诉系统这个逻辑地址中,页内偏移量占多少位。
- 页面替换算法有哪些?
最优算法
,将不会用的内存置换出去,但是实现不了
先进先出
,跟踪内存加载的顺序,并把页面放入一个链表中。有可能删除存在时间最长但是还在使用的页面。
LRU
,最近最少使用的页面就置换,需要TLB的支持。
WSClock
,基于工作集和LRU
- 动态分区分配算法有哪几种?可以分别说说吗?
1、首次适应
:按照地址顺序分配,能够分配就直接给,优先使用第一个满足条件的空闲区,但是这样会产生很多碎片,不利于大进程分配
2、最优适应
:按照空间大小分配,优先使用小的空闲区,这样就能保证大进程能够有大片空闲区。
3、最大适应
(反着):按照空间大小分配,最优会产生很多无法利用的小碎片,那么先给最大的进行分配,同样也会出现在之后不利于大进程分配
4、临近适应
:不从链头开始找,按照地址顺序分配,每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区,会使高地址的大空闲区快速分配完
- 缓冲区溢出
缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上
- 虚拟技术你了解吗?
主要有两种虚拟技术:
时分复用技术
和空分复用技术
。
多进程与多线程:多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占用处理器,每次只执行一小个时间片并快速切换。
虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。
- 虚拟内存
虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用部分加载的技术,让一个进程或者资源的某些页面加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存
- 程序从堆中动态分配内存时,虚拟内存上怎么操作的
在进行动态内存分配时,例如malloc()函数或者其他高级语言中的new关键字,操作系统会在硬盘中创建或申请一段虚拟内存空间,并更新到内存中的页表,分配一个页表条目(PTE),然后这个页表项就建立了虚拟地址和物理地址之间的关系。
- 虚拟内存的实现方式有哪些?
请求分页存储管理。
请求分段存储管理。
请求段页式存储管理。
-
IO多路复用
-
硬链接和软链接
硬链接
:各个用户的目录项指向同一个索引节点,这个节点有一个链接计数,某个用户想删除文件的时候,count–,但是count = 0 的才能真正删除文件数据和索引节点。
软链接
:在一个link型文件中记录共享文件的存放地址,操作系统就根据路径找,即使软连接指向的共享文件已被删除,link文件也存在,只是会查询失败,而且软链接会涉及到多次磁盘IO,因此软链接访问共享数据的速度比硬链接更慢。
- 中断处理过程
保护现场
:将当前程序执行相关数据保存在寄存器中,入栈
开中断
:实现执行中断时,能够响应更高级别的中断
中断处理
关中断
:保证恢复现场时不被新中断打扰
恢复现场
:从栈中按序取出程序,恢复中断前的执行状态
- 中断和轮询的区别
中断:通过特定事件的触发来提醒CPU,容易遗漏问题。
轮询:CPU对特定设备轮流访问,时间长,CPU利用率不高
- 中断和异常的相同点和不同点
相同点
- 最后都是由CPU发送给内核,由内核去处理
- 处理程序的流程设计上是相似的
不同点
- 产生源不相同,异常是由CPU产生的,而中断是由硬件设备产生的
- 内核需要根据是异常还是中断调用不同的处理程序
- 中断不是时钟同步的,这意味着中断可能随时到来;异常由于是CPU产生的,所以它是时钟同步的
- 当处理中断时,处于中断上下文中;处理异常时,处于进程上下文中
- 用户态和内核态
是操作系统的两种运行状态
内核态
:内核态运行的程序可以访问计算机的资源,不受限制。处于内核态的CPU能够从一个进程切换到另一个进程,并且占用CPU不会发生抢占情况。
用户态
:用户运行的程序只能受限的访问内存,用户态下CPU不允许独占,也就是CPU能够被其他程序获取。
分为两种,主要是对访问能力进行限制,比如设置时钟、内存清理、都需要在内核态下进行的。
- 用户态和内核态是如何切换的?
利用系统调用,一般用户态 -> 内核态的转换我们都称之为 trap 进内核,也被称之为 陷阱指令(trap instruction) 。
- unix常见的io模型
(1)同步阻塞IO(Blocking IO)
:即传统的IO模型。
(2)同步非阻塞IO(Non-blocking IO)
:默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。
(3)IO多路复用
(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
(4)异步IO(Asynchronous IO)
:即经典的Proactor设计模式,也称为异步非阻塞IO。
同步和异步的概念描述
的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞和非阻塞的概念
描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。
- select、poll、epoll的区别
(1)select==>时间复杂度O(n)
它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
(2)poll==>时间复杂度O(n)
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.
(3)epoll==>时间复杂度O(1)
epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)
- 介绍一下几种典型的锁?
读写锁
- 多个读者可以同时进行读
- 写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
- 写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)
互斥锁
- 一次只能一个线程拥有互斥锁,其他线程只有等待
- 互斥锁是在抢锁失败的情况下主动放弃CPU进入睡眠状态直到锁的状态改变时再唤醒,为了实现锁的状态发生改变时唤醒阻塞的线程或者进程,需要把锁交给操作系统管理,所以互斥锁在加锁操作时涉及上下文的切换
条件变量
互斥锁一个明显的缺点是他只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,他常和互斥锁一起使用,以免出现竞态条件。当条件不满足时,线程往往解开相应的互斥锁并阻塞线程然后等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。总的来说互斥锁是线程间互斥的机制,条件变量则是同步机制。
自旋锁
- 如果进线程无法取得锁,进线程不会立刻放弃CPU时间片,而是一直循环尝试获取锁,直到获取为止。
- 如果别的线程长时期占有锁,那么自旋就是在浪费CPU做无用功,但是自旋锁一般应用于加锁时间很短的场景,这个时候效率比较高。
- 描述互斥锁和自旋锁
互斥锁(mutex)
- 互斥锁属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程A和B,它们分别运行在core 0和core 1上。
- 假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞,此时会通过上下文切换将线程A置于等待队列中,此时core 0就可以运行其他的任务(如线程C)。
自旋锁(spin)
- 自旋锁属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,如果自旋锁已经被线程B所持有,那么线程A就会一直在core 0上进行忙等待并不停的进行锁请求,检查该自旋锁是否已经被线程B释放,直到得到这个锁为止。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。
- 虽然它的效率比互斥锁高,但是它也有些不足之处:
自旋锁一直占用CPU,在未获得锁的情况下,一直进行自旋,所以占用着CPU,如果不能在很短的时间内获得锁,无疑会使CPU效率降低。- 在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁。
自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。
- 怎么回收线程?有哪几种方法?
等待子线程都结束,主线程先等待子线程退出并回收资源
子线程执行,结束线程
分离线程
- 守护进程、僵尸进程和孤儿进程
孤儿进程
- 如果父进程先退出,子进程还没退出,那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)。
- 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程
- 如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵尸进程。(父进程并不知道子进程是否存活,因此是僵尸)
- 设置僵尸进程的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(也就是说init进程将wait它们,从而去除它们的僵尸状态)。
守护进程
- 指在后台运行的,没有控制终端与之相连的进程。它独立于控制终端,周期性地执行某种任务。Linux的大多数服务器就是用守护进程的方式实现的,如web服务器进程http等
- 抖动你知道是什么吗?它也叫颠簸现象
刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动,或颠簸。
产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数(分配给进程的物理块不够)
为进程分配的物理块太少,会使进程发生抖动现象。
为进程分配的物理块太多,又会降低系统整体的并发度,降低某些资源的利用率 为了研究为应该为每个进程分配多少个物理块,Denning 提出了进程工作集” 的概念
- 从堆和栈上建立对象哪个快?(考察堆和栈的分配效率比较)
从两方面来考虑:
分配和释放
,堆在分配和释放时都要调用函数(malloc,free),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成内存碎片),这些都会花费一定的时间,具体可以看看malloc和free的源代码,函数做了很多额外的工作,而栈却不需要这些。访问时间
,访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正的数据,而栈只需访问一次。另外,堆的内容被操作系统交换到外存的概率比栈大,栈一般是不会被交换出去的。
- 常见内存分配方式有哪些?
(1)
从静态存储区域分配
。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建
。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配
,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
- 内部碎片与外部碎片?
内部碎片:分配给某些进程的内存区域中有些部分没用上,常见于固定分配方式,分页。
外碎片:内存中某些空闲区因为比较小,而难以利用上,一般出现在内存动态分配方式中,按需分配,分段。
消除碎片文件的方式:紧凑,就是操作系统不时地对进程进行移动和整理,而整理的过程发生在内存交换,回收内存时要尽可能地将相邻的空闲空间合并。
- 冯诺依曼结构有哪几个模块?分别对应现代计算机的哪几个部分?(百度安全一面)
存储器:内存
控制器:寄存器、译码器、pc等
运算器:CPU
输入设备:键盘
输出设备:显示器、网卡