有朋友问我关于操作系统详细的面经,之前的分享里面经写的的确不是很详细,这里重新整理一份分享给大家。我是因为做了一个简易的操作系统,面试问题也主要是围绕着操作系统展开,这里就面试遇到的问题以及我自己的想法整理一下,可以参考参考,有什么问题也还请批评指正。
这个是实际问到我的问题
自己引申出来的问题
黑色普通文字是我的“回答”或者与面试官闲聊的内容
启动
-
启动过程
,BIOS->MBR->Bootloader->OS,多 CPU 情况下 BSP 启动 APs,叙述每个阶段做得主要事情。 上电那一刻,CS:IP = 0xf000:0xfff0,此地址上的 16 字节是个跳转地址:jmp f000:e05b(据然还真问到了具体跳转地址)
- BIOS
-
干了些啥事
,自检程序,将 MBR 加载到 0x7c00 提问其他的固件方面的知识,BIOS,UEFI等,不太了解
- 实模式、保护模式
-
区别
,前者16位,地址线只用了20根,后者解开限制,前者的段寄存器里面是段基址,后者段寄存器里面是段选择子 为什么有实模式,兼容?
-
如何进入保护模式
,构建GDT、打开A20,CR0.PG = 1
- MBR
-
MBR 构成
,引导程序->64字节分区表->魔数(0x55和0xAA) -
在哪儿
,启动盘最开始那个扇区 -
加载到哪儿
,加载到 0x7c00 -
主要干的事情
,根据分区表找到一个活动分区,然后加载 Bootloader -
如何判断是否是启动盘
,魔数 0x55 和 0xAA
- 开启分页机制(x86)
- 构建页表
- 页表地址给 CR3
- CR0.PE = 1
- BSP 启动 AP 过程(x86,中断控制器为APIC),主要通过 LAPIC 发送 INIT-SIPI-SIPI 消息… 详见 Multiprocessor Specification
OS 第一个 init 进程做了些什么
- xv6 里面打开 0 1 2 号文件
- fork 出 shell
- 然后 wait(等待孤儿进程过继给init)
-
提问现在的 Linux 里面干了些什么
,有简单看了看,有些复杂待研究
-
汇编和 C 交互的一些问题
,遵循调用约定,全局变量等在汇编时就要处理好
内存管理
寻址方式
,x86 段基址:段内偏移
- 实模式下段寄存器为实际的段基址,保护模式下为段选择子
分段分页特点,为什么分页
- 分段同类型数据放在一起,分页逻辑上连续物理上分散实现离散化存储
- x86 有段寄存器,硬件上原生支持分段,其他架构硬件上似乎不支持
- x86 可以设置平坦模式,“隐藏”分段
地址转换过程
,段级转换(GDT)、页级转换(查页表)
- GDTR 中存放 GDT 的线性地址,CR3 里面存放页目录的物理地址,页表项里面存放的也是物理地址。
物理内存管理
,目前 xv6 里面简单的空闲链表法,引申到伙伴系统 Slab 分配器,不管什么地方,空间管理的方式一般就两种,链表和位图,万变不离其宗。虚拟地址空间如何布局的
- 堆、共享区等的管理方式,xv6 里面堆的管理方式也是链表,看侯捷老师关于 C++ 内存部分讲解,也是类似伙伴系统的思想,申请空间过大时也是 mmap 在共享区分配空间
堆空间分配算法,C++ new 出来的,能否用 free 返回
,一个 C++ 相关的问题,从堆分配的空间有个头部,头部记录了大小信息,而 new 不一定,所以最好不要这么干。缺页异常三种,写时复制、延迟分配、页面置换
-
写时复制的实现思想
,要点:利用页表项保留位设置为COW标志位,复制内存时(主要fork)不实际复制内存,只复制页表,并将COW=1,R/W 设置为只读,其他共享计数、回收等略,详见 xv6 写时复制实现 - 延迟分配,页表项全0,只在页表中建立了映射,并未实际分配物理内存,详见 xv6 延迟分配实验
-
页面置换
,页表项非空,页表项 P = 0;常见页面置换算法
;页面置换交换区实现方式
,具体实现有些复杂,详见 ucore 的例子,有配套的手册讲解。
Linux kmalloc 的特点
,分配的物理内存连续
IO 管理/中断
中断、异常区别
,外、内中断异常详细分类
,据 CSAPP 中断、陷阱、故障、终止中断、异常返回的 PC 是什么
,据CSAPP,中断、陷阱下一条,异常(故障)可能返回当前 PC,终止不会返回中断过程
- 中断控制器干了什么事,见上图
如何定位的中断服务程序
,见上图
- 上图 x86 的情况是硬件识别,向量中断的方式
- 还有软件识别的方式,跳到一个固定地址,然后再查询异常状态寄存器看是什么异常,再跳到特定的处理程序,一般精简指令集这么干。
-
开关中断的事
,一般我们说中断时保存上下文前要关中断,x86 下,关中断就是 EFLAGS 的 IF = 0 系统调用过程
- 没有中断控制器这个过程,其他阶段基本相同
- 传参两种方式,寄存器,压栈(进入内核后从上下文中的栈指针寄存器获取用户态栈顶地址)
- x86下系统调用可以用中断门实现,也可以使用陷阱门实现,使用中断门进入中断自动关中断,如果使用陷阱门则不会自动关中断,这是二者唯一的区别
-
按下一个键到显示在屏幕上
,键盘中断 + 显卡、写显存 过程,太多略,详见@@@@ -
DMA 过程
,不是很清楚,百度google,我也是搜的。 -
磁盘寻址
,CHS(柱面磁头扇区)、LBA(逻辑块地址),实际上似乎不是想问这个,但面试官当时也没说清楚就下一个问题,emm??? -
内核态、用户态的理解
,实际上就是特权级,RPL、CPL、DPL 三者之间不同情况下的各种比较变化,特复杂。 -
用户态到内核态栈的变化
,x86 下根据 TR 可以找到 TSS,TSS 里面有内核栈的 CS 和 ESP;RISC-V 下 sscratch 有内核上下文的地址。
文件管理
-
分区布局
,引导块->超级块->inode、位图、数据区 -
写磁盘如何保证数据一致性,设计日志层
(当时某度二面,全程基本就讨论这个) inode、路径、目录、目录项、全局文件表和文件结构体、进程打开文件表和文件描述符等概念以及它们之间的关系
open close dup write read 等常见系统调用
,做了什么事情
- 比如 open 的要点分配文件结构体、文件描述符。
- 这个函数只是用户接口,整个是一个是系统调用过程。
inode 相当于树形结构的索引、假如设计为哈希索引,如何设计,Cache 缓存,优劣等等讨论
硬链接、软链接区别
- 硬链接与目录项挂钩,多个目录项一个 inode 一个文件
- 软链接是个文件有自己的 inode,文件内容是路径
-
目录项缓存
,Linux 使用目录项缓存 dentry cache 缓存提高目录项对象的处理效率,我也只知道这个东西,待研究 命令获取某个目录下的文件数量
进程
-
谈进程、线程、协程的理解,聊区别
,简易设计 -
第一个进程干了些什么事
,见前 -
切换过程
,重点换上下文、换栈 -
为什么切换进程比切换线程慢
,除了进程“体量”大,另外切换进程要切换页表,切换页表要刷新TLB -
切换时机
,自己阻塞让出 CPU,时间片到了 -
状态转换,发生中断时进程状态如何转换
,时间中断设置为就绪,其他情况在 xv6 里面没变,Linux 不了解。 fork 实现
exec 实现
-
孤儿进程
:父进程先退出了,过继给 init 进程;僵尸进程
:子进程退出父进程没有wait -
僵尸进程孤儿进程的理解
,进程要退出时调用 exit,exit 会关闭文件等资源,另一部分资源是父进程调用 wait 来释放,wait 释放子进程的栈、上下文、页表、任务结构体等资源。 -
如何解决僵尸进程
,父进程总会退出,那么在 exit 中检测是否有将是状态的子进程,如果有,将其过继给 init 进程,然后唤醒 init 让它来处理。 -
进程间的通信
,信号、信号量、共享内存、消息队列、匿名管道、有名管道,聊简单设计 -
调度算法
,Linux 的 CFS -
Go 的协程模型
,不太了解卒 -
关于进程的命令
,如何查看进程状态等等
其他
-
锁的实现
,自旋锁和休眠锁 -
自旋锁
,while 循环 -
内存一致性
,硬件如何支持的原子操作,聊了指令 Lock 锁总线等等,CAS、Acquire/Released 等 休眠锁涉及进程的休眠唤醒机制如何实现
-
Cache 缓存一致性
,MESI -
设计操作系统需要研究研究设计模式,所以设计模式
?????? 复杂指令集和精简指令集区别
elf 文件格式介绍,可装载段,装载地址,filesz、memsz 等
一些具体答案,可以关注公众号:Rand_CS 获取