之前在三月份的时候,曾经推送过cxuan的《爆肝操作系统》一文(见本次推送的第二个链接),现在为了让OS持续加成,推出《操作系统必问38题》,掌握好这些题目,且能对一些要点做适度的挖掘,面试和笔试一般不成大问题。
提升OS技术深度的硬核实践
- Linux 内核:https://github.com/torvalds/linux
- Harmony OS 内核:https://github.com/Awesome-HarmonyOS/HarmonyOS
- MIT xv6 教学系统:https://github.com/mit-pdos/xv6-public
什么是操作系统?
操作系统管理计算机硬件和软件,是计算机资源调度的管家,是用户和计算机硬件之间的中介。
操作系统的发展历程
- 无操作系统:用户独占全机;装带、卸带操作时CPU等资源是空闲的;效率低
- 单道批处理系统:需要监督程序;多个作业先后顺序使用CPU;计算机控制权在监督程序和作业之间交替使用
- 多道批处理系统:计算机中同时有几道作业(程序)在运行,提高了计算机资源(CPU、内存、I/O)的使用效率;宏观上,多个作业“并行”(并发);微观上,多个作业串行;无交互能力
- 分时系统:分时系统是为了满足用户需求(人机交互、共享主机、便于用户上机)而发展起来的;你家用的windows,imac和你做实验用的一般Ubuntu,都是属于这个范畴
- 实时系统:实时系统能及时(或即时)响应外部事件的请求,在规定的时间内完成对该事件的处理,并控制所有实时任务协调一致地运行,这种系统主要用于工业控制领域,如造车、飞机票仓库等。同时,这个领域也是现代操作系统科研方向的一大热点。
软实时/硬实时是什么?
上面我们提到实时操作系统。实时≠同时,所以从作业提交到作业响应,还是存在着一定的时间gap,虽然这个gap几乎不可见。对于软实时系统而言,任务提交能尽早完成就尽早完成,但偶尔超过了规定的期限,不会造成十分严重的影响;而对于硬实时系统,任务必须要在规定期限内完成,不然会对系统造成严重的影响。我们来看一些实时系统的数据,直观地感受一下“实时”:
- 基于时间片轮转的实时调度:数s响应
- 基于非抢占优先权的实时调度:数百ms响应
- 基于时间中断抢占优先权的实时调度:几ms至几十ms的响应
- 基于立即抢占的优先权实时调度:几ms
现代操作系统的基本特性
- 共享性:系统资源可供内存中多个并发的进程共同使用
- 异步性:内存中多个并发的进程以异步方式运行
- 虚拟性:系统物理资源可虚拟为更大、更多的逻辑资源(时分复用、空分复用)
- 并发性:多道用户程序可在同一时间间隔中运行
现代操作系统的基本功能
- 进程管理:进程控制、同步、通信和调度
- 内存管理:内存分配、保护、映射和扩充
- 文件管理:文件存储空间管理、目录管理、文件读写保护等
- 设备管理:缓冲管理、设备分配和处理
单一内核和微内核
单一内核理解起来,其实就是操作系统系统把所有功能性的代码都编译打成“一团”,运行时如果中间环节有哪个功能crash了,整个操作系统有可能也跟着crash。微内核其实就是把操作系统内核精简了,原来的内核还需要设备管理和文件管理,在微内核的架构下,内核只剩下进程调度、内存管理等核心功能,剩下的诸如文件管理都抽离到用户空间中以进程方式执行。鸿蒙Harmony OS就是基于微内核架构设计的。
进程和线程的区别
- 进程是资源分配基本单位,线程是处理机调度的基本单位
- 进程是程序的一次执行实例,线程是进程执行中的一条任务流
- 进程创建调度的开销大,线程创建调度的开销小
- 进程拥有独立的地址空间,同个进程中的线程共享进程空间
【ps】现在后台面试也越来越喜欢协程了,关于协程可以参看:【小林Coding】https://mp.weixin.qq.com/s/BKm60as1vz3bfvzJjU7uTw
进程的状态模型
PCB包含了哪些内容
PCB是进程存在的唯一标志,它描述进程的变化过程,记录进程的外部特征,记录进程与其他进程的联系,系统通过PCB控制和管理进程。PCB包含:
- 进程标识符信息
- 处理机状态信息:包括通用寄存器、指令计数器、程序状态字PSW、用户堆栈指针等信息
- 进程调度信息:进程状态、优先级、阻塞事件等
- 进程控制信息:程序数据地址、同步通信、资源清单、链接指针
- 其他信息:父子关系
操作系统组织和管理数据常见的三种方式
- 线性表
- 链接
- 索引
引发进程阻塞的可能原因
- 请求某项系统服务
- 启动某操作
- 新数据尚未到达
进程同步原则
- 空闲让进:提高效率
- 忙则等待:解决互斥
- 有限等待:等待进入临界区的要求应在一有限时间满足(以免死等)
- 让权等待:放弃占用CPU(以免忙等)
信号量的数据结构包含哪些
由一个整型数和一个阻塞等待进程链表构成的记录型结构
什么是管程
已经证明管程和信号量机制是等价的。之所以要有管程,是因为信号量一旦多了之后,整个系统的控制将变得复杂,非常容易就产生死锁,管程相当于是信号量的管家,当进程想要申请访问某项临界资源时,需要通过管程申请,管程做统一的分配调度,每次进入管程的进程只能有一个。
信号量机制的一些问题:
- 临界区分散在各进程之中,不便于管理和控制
- 很多临界区的操作是相同的,重复编写,使进程结构不清晰
- 如果编程出现差错,不便于检查,且会带来严重后果
管程的定义:
- 一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据
- 管程实际上是一种能实现进程同步的特殊的子程序(函数、过程、数据)的集合
生产者-消费者的伪代码(记录型信号量机制)
// variable definitiion
var mutex, empty, full : semaphore := 1, n, 0
buffer : array[0, 1, ..., n-1] of item
in, out : integer := 0, 0
procedure producer // 生产者
begin
repeat
生产一个产品p
wait(empty);
wait(mutex);
buffer[in] := p;
in := (in+1) % n;
signal(mutex);
signal(full);
until false;
end;
procedure consumer // 消费者
begin
repeat
wait(full);
wait(mutex);
p := buffer[out];
out := (out+1) % n;
signal(mutex);
signal(empty);
消费一个产品p
until false;
end;
读写者模式的伪代码(读者优先)
var rmutex, wmutex, reader_count := 1, 1, 0
procedure writer
begin
repeat
wait(wmutex);
do writing...
signal(wmutex);
until false;
end;
procedure reader
begin
repeat
wait(rmutex);
if (reader_count == 0) // 之所以叫读者优先,就在于这一行
wait(wmutex);
reader_count := reader_count + 1;
signal(rmutex);
do reading...
wait(rmutex);
reader_count := reader_count - 1;
if (reader_count == 0)
signal(wmutex);
signal(rmutex);
until false;
end;
进程通信方式
- 共享存储器系统:基于共享数据结构(如生产者-消费者模型),基于共享存储区(并行计算中的OpenMP编程模型)
- 消息传递系统:直接通信、间接通信(并行计算中的MPI编程模型)
- 管道通信系统:有名管道、无名管道(它俩都是半双工通信,区别在于有名管道可以在任意两个进程之间通信,而无名管道只能在具有父子关系的进程之间通信)
- 消息缓冲队列
线程同步方式
- 互斥锁:利用开锁/关锁实现互斥访问
- 信号量:公有信号量和私有信号量(私用信号量:用于同一进程中不同线程之间的同步和通信;公用信号量:用于不同进程中的线程之间的同步和通信)
- 条件变量:在进入临界区之前,先关上互斥锁,进行判断,是否条件可以满足;不能满足时,自行转为等待状态,并打开互斥锁;满足条件,则继续执行临界区,完成后,打开互斥锁。
什么是ULT和KLT
- ULT:User Level Thread(用户级线程)线程的处理不通过内核实现,线程的状态信息等全部存放在用户空间中,内核不能感知用户级线程
- KLT:Kernel Level Thread(内核支持线程)线程的处理均由内核实现,内核为线程保留TCB,并通过TCB感知线程
调度的三个级别
- 高级调度(作业调度):它决定允许哪些作业参与竞争CPU和其它系统资源
- 中级调度:决定哪些进程可参与竞争CPU;将进程从活动态(活动就绪、活动阻塞)变为静止的挂起态(静止就绪、静止阻塞);或相反;中级调度实际上是实现“挂起”和“激活”操作
- 低级调度(进线程调度):决定哪个进程可获得CPU
调度评估准则
- 周转时间:从提交(进入时刻)到完成的时间称为该作业的周转时间
- 平均周转时间:n个作业(进程)周转时间的平均值
- 带权周转时间:周转时间与实际运行时间之比称为该作业的带权周转时间
- 平均带权周转时间:n个作业带权周转时间的平均值
- 平均等待时间:从进入就绪队列到获得CPU的时间
进程调度算法
- FCFS 先来先服务
- SF 短作业优先
- HRN 高响应比优先
- HPF 优先级
- RR 时间片轮转
- 多级队列调度
- 多级反馈队列调度:设置多个就绪队列,并从高到低赋予不同的优先级;每个队列采用FCFS算法,时间片长度从高优先级到低优先级依次增加
上面是普通进程调度的常用算法,还有实时进程调度算法:
- EDF 最早开始截至时间优先
- LLF 最低松弛度优先
死锁产生的必要条件
- 进程对临界资源互斥访问
- 进程请求被另一个进程保持的资源
- 一个进程已有的资源不能被强制剥夺
- 循环等待
死锁的处理方法
- 预防:摒弃“请求和保持”条件 / 摒弃“不剥夺”条件 / 摒弃“环路等待”条件
- 避免(著名算法:银行家算法)
- 检测 :通过资源分配图(系统或任务处于死锁状态的充分必要条件是:当且仅当系统或任务的资源分配图是不可完全简化的)
- 解除:剥夺资源 / 撤销进程(全部撤销法 / 最小代价撤销法)
连续/离散内存分配各有哪些方法
连续内存分配
- 单一连续分配
- 固定分区分配
- 动态分区分配
- 动态重定位分区分配
离散内存分配
- 基本分页分配
- 基本分段分配
- 基本段页式分配
页表/二级页表/段表/段页式 地址变换机构
- 页表地址变换机构
- 具有快表机制的页表地址变换机构
- 二级页表地址变换机构
- 段表地址变换机构
- 段页式地址变换机构
什么是虚拟存储
虚拟内存是计算机系统内存管理的一种技术,使应用程序认为它拥有一个连续完整的地址空间的假象,而实际上,它通常是被分割成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行交换。
描述缺页中断机制
描述缺段中断机制![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629123810346.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDAyNjYwNA==,size_16,color_FFFFFF,t_70)
请求分页机制的页面调入策略有哪些
何时调入
- 预调页
- 请求调页
何地调入
- 文件映射页
- 匿名页
【注:百度百科上给出了mmap的一个程序实例】
/*-------------map_normalfile1.c-----------*/
#include<sys/mman.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include <errno.h>
typedef struct{
char name[4];
int age;
}people;
void main(int argc,char **argv)//map a normal file as shared mem:
{
int fd,i;
people *p_map;
char temp;
fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,sizeof(people)*5-1,SEEK_SET);
write(fd,"",1);
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if (p_map == (void *)-1)
{
fprintf(stderr, "mmap: %s\n", strerror(errno));
return ;
}
close(fd);
temp='a';
for(i=0;i<10;i++)
{
temp+=1;
(*(p_map+i)).name[1] = '\0';
memcpy((*(p_map+i)).name,&temp,1);
(*(p_map+i)).age=20+i;
}
printf("initializeover\n");
sleep(10);
munmap(p_map,sizeof(people)*10);
printf("umapok\n");
}
/*-------------map_normalfile2.c-----------*/
#include<sys/mman.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include <errno.h>
typedef struct{
char name[4];
int age;
}people;
void main(int argc,char **argv)//map a normal file as shared mem:
{
int fd,i;
people *p_map;
fd=open(argv[1],O_CREAT|O_RDWR,00777);
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if (p_map == (void *)-1)
{
fprintf(stderr, "mmap: %s\n", strerror(errno));
return ;
}
for(i=0;i<10;i++)
{
printf("name:%s age%d;\n",(*(p_map+i)).name,(*(p_map+i)).age);
}
munmap(p_map,sizeof(people)*10);
}
页面置换算法
- 最佳置换算法
- FIFO
- LRU
- NRU(CLOCK算法)
- 改进CLOCK算法
- PBA(Page Buffering Algorithm)
文件系统有哪些接口
- 命令接口
- 程序接口
- 图形接口
文件存储空间管理有哪些方式
- 空闲表法
- 空闲链表法
- 位图法
- 成组链接法
IO系统如何分类
- 按传输速率分:低 / 中 / 高速设备
- 按信息交换单位分:块设备 / 字符设备
- 按设备共享属性分:独占 / 共享 / 虚拟设备
- 按设备外部特征分:存储 / 输入 / 输出设备
IO控制方式有哪些
- 程序控制
- 中断
- DMA
- 通道
缓冲管理手段有哪些
- 单缓冲
- 双缓冲
- 循环缓冲
- 缓冲池
简述SPOOLing技术
又称假脱机技术,由系统中的两个专门负责I/O的进程,模拟I/O外围机的功能,实现(假)脱机输入/输出。
- 输入井和输出井
- 在磁盘上开辟的两个大的存储空间。
- 输入井是模拟脱机输入时的磁盘,用于收容I/O设备输入的数据
- 输出井是模拟脱机输出时的磁盘,用于收容用户进程的输出数据
- 输入缓冲区和输出缓冲区
- 在内存中开辟的两个缓冲区
- 输入缓冲区用于暂存由输入设备送来的数据,以后再传送到输入井
- 输出缓冲区用于暂存由输出井送来的数据,以后再传送到输出设备
- 输入进程SPi和输出进程SPo
- 输入进程SPi模拟脱机输入时的外围控制机,实现将用户要求输入的数据从输入设备,通过输入缓冲区再送到输入井的控制功能
- 输出进程SPo模拟脱机输出时的外围控制机,实现将用户要求输出的数据从输出井,通过输出缓冲区送到输出设备的控制功能
磁盘调度算法
- FIFO
- SSTF
- SCAN
- CSCAN
- FSCAN
- N-Step-Scan
有了操作系统基础,相信读者在各类业务中比如Java开发,会对编写的代码将有更好的理解,如可重入锁、synchronized同步锁、偏向锁、COW、公平锁、NIO等,以上。