面试 | 操作系统

news2026/3/14 15:37:37
文章目录操作系统面试完全指南Go 校招向一、进程与线程基本概念进程 vs 线程面试必考进程状态机进程控制块PCB上下文切换Context Switchfork() 与 Copy-on-Write孤儿进程 vs 僵尸进程栈 vs 堆面试高频二、进程调度算法调度目标六种调度算法1. 先来先服务FCFS2. 最短作业优先SJF3. 优先级调度4. 时间片轮转RRRound-Robin5. 多级队列调度6. 多级反馈队列现代 OS 常用三、内存管理内存分层结构虚拟内存面试核心分页 vs 分段页面置换算法OPT最优置换—— 理论最优FIFO先进先出LRU最近最少使用—— 最常用Clock时钟置换—— LRU 的近似实现多级页表为什么需要内存分配算法四、死锁死锁的四个必要条件缺一不可经典死锁场景死锁处理策略预防破坏四个条件之一避免银行家算法检测与恢复五、同步与互斥临界区问题同步原语自旋锁 vs 互斥锁面试高频互斥锁Mutex读写锁RWMutex信号量Semaphore条件变量Cond经典同步问题六、Go GMP 调度模型Go 后端必考GMP 三个概念调度架构关键机制七、内存一致性与 CPU 缓存CPU 缓存层级与缓存一致性内存屏障与可见性七、文件系统文件系统结构文件读取全过程文件描述符与打开文件表八、系统调用与中断系统调用中断机制九、经典面试问题速答十、一张图记住核心操作系统面试完全指南Go 校招向一、进程与线程基本概念概念定义类比程序静态的可执行文件存储在磁盘菜谱进程程序的一次执行实例动态的有生命周期正在做菜的厨师线程进程内的执行单元共享进程资源厨师的一只手协程用户态轻量级线程由程序自己调度厨师快速切换的注意力进程 vs 线程面试必考进程 ┌─────────────────────────────┐ │ 代码段 数据段 堆 栈 │ ← 独立地址空间 │ 文件描述符 信号处理 │ ← 独立资源 │ Thread1 Thread2 │ ← 可含多个线程 └─────────────────────────────┘ 同一进程的线程 共享代码段、数据段、堆、文件描述符、信号处理 独有栈、寄存器、程序计数器PC、线程 ID对比项进程线程资源独立地址空间资源隔离共享进程资源创建开销大复制页表、文件描述符等小只分配栈和寄存器切换开销大需切换页表、TLB 刷新小只切换寄存器和栈通信方式IPC管道、共享内存、Socket 等共享内存直接读写全局变量健壮性进程间互不影响一个线程崩溃影响整个进程并行能力多进程可跑在多核上多线程可跑在多核上面试答法进程是资源分配的基本单位拥有独立的地址空间线程是 CPU 调度的基本单位共享进程的资源。线程比进程轻量创建和切换开销小但线程崩溃会影响整个进程进程间相互隔离。Go 的 goroutine 是用户态协程由 runtime 的 GMP 模型调度初始栈 2KB切换不需要进入内核态比线程更轻量。进程状态机┌────────────────────┐ │ 新建(New) │ └─────────┬──────────┘ │ 创建完成进入就绪队列 ▼ ┌──── 时间片用完 ──── 就绪(Ready) ◀──── 等待事件完成 │ │ │ 调度器选中 │ │ ▼ ▼ 就绪 运行(Running) │ ┌───────────────┼───────────────┐ │ │ │ 等待I/O 进程退出 等待事件 │ │ │ ▼ ▼ ▼ 阻塞(Blocked) 终止(Terminated) 阻塞(Blocked)五种状态详解状态说明Linux 对应新建进程正在被创建—就绪等待 CPU 时间片Rrunnable运行正在 CPU 上执行Rrunning阻塞等待 I/O、锁、信号等S可中断/ D不可中断终止执行完毕等待回收Z僵尸/ X进程控制块PCB操作系统为每个进程维护一个PCBProcess Control Block包含PCB 内容 进程标识符PID、父进程 PID CPU 寄存器状态程序计数器、栈指针等—— 进程切换时保存/恢复 进程调度信息优先级、状态、调度队列指针 内存管理信息页表基地址、内存区间 I/O 状态打开的文件描述符 记账信息CPU 使用时间、创建时间PCB 的作用进程切换时将当前进程的 CPU 状态保存到 PCB再从下一个进程的 PCB 恢复状态实现上下文切换。上下文切换Context Switch进程 A 运行中 │ │ 中断/系统调用/时间片用完 ▼ 内核保存进程 A 的 CPU 上下文到 PCB_A │ 内核选择进程 B 运行 │ 内核从 PCB_B 恢复进程 B 的 CPU 上下文 │ ▼ 进程 B 继续运行从上次中断处上下文切换的开销直接开销保存/恢复寄存器、切换页表进程切换时间接开销TLB 失效进程切换需刷新地址转换缓存、CPU 缓存失效线程切换比进程切换快因为同进程的线程共享页表不需要切换页表和刷新 TLB。fork() 与 Copy-on-Writefork()创建子进程时并不立即复制父进程的全部内存而是采用写时复制Copy-on-Writefork() 后 父进程页表 ──────────────────────────┐ ▼ 子进程页表 ──────────────────────── 同一物理页只读标记 父/子进程试图写该页时 ↓ 触发缺页中断内核复制该页各自拥有独立副本fork 本身极快只复制页表不复制数据只有实际发生写操作的页才会被复制节省内存fork()后立即exec()加载新程序shell 的常见模式则完全不浪费孤儿进程 vs 僵尸进程孤儿进程父进程先于子进程退出 → 子进程被 init 进程PID1收养init 负责回收 → 无害系统自动处理 僵尸进程子进程已退出但父进程未调用 wait() 回收 → PCB 残留在内核占用 PID → 大量僵尸进程会耗尽 PIDLinux 默认上限约 32768 → 处理方式kill 父进程父进程死后僵尸子进程由 init 回收栈 vs 堆面试高频栈Stack 由编译器/运行时自动管理函数调用时分配返回时释放 存储局部变量、函数参数、返回地址 增长方向向低地址增长 大小固定且较小Linux 默认 8MBGo goroutine 初始 2KB 速度快移动栈指针即可 堆Heap 由程序员手动管理malloc/new 分配free/GC 释放 存储动态分配的对象 增长方向向高地址增长 大小受物理内存限制可以很大 速度慢需要内存分配器管理空闲块可能有碎片对比项栈堆管理方式自动编译器手动/GC分配速度O(1)极快较慢需找空闲块大小小且固定大动态增长碎片无有内存碎片问题线程安全每个线程独立栈天然安全共享需要同步Go 的逃逸分析编译器自动判断变量分配在栈还是堆。函数返回后仍被引用的变量逃逸分配在堆否则分配在栈。go build -gcflags-m可查看逃逸情况。二、进程调度算法调度目标CPU 利用率让 CPU 尽量忙碌吞吐量单位时间完成的进程数周转时间进程从提交到完成的总时间等待时间进程在就绪队列中等待的时间响应时间交互式系统中从请求到第一次响应的时间六种调度算法1. 先来先服务FCFS进程队列: P1(24ms) → P2(3ms) → P3(3ms) 执行顺序: P1──────────────────── P2─── P3─── 等待时间: P10, P224, P327 平均等待: (02427)/3 17ms 缺点短进程等待时间长护航效应2. 最短作业优先SJF进程队列: P1(6ms) P2(8ms) P3(7ms) P4(3ms) 执行顺序: P4─── P1────── P3─────── P2──────── 等待时间: P40, P13, P39, P216 平均等待: (03916)/4 7ms最优平均等待时间 缺点需要预知执行时间不现实长进程可能饥饿3. 优先级调度每个进程有优先级数字数字越小优先级越高或反之 高优先级进程先执行 问题低优先级进程可能永远得不到执行饥饿 解决老化Aging——随时间推移提高等待进程的优先级4. 时间片轮转RRRound-Robin时间片 20ms 进程队列: P1(53ms) P2(17ms) P3(68ms) P4(24ms) 执行: P1─20─ P2─17─ P3─20─ P4─20─ P1─20─ P3─20─ P4─4─ P1─13─ P3─28─ 完成 完成 完成 时间片大小的影响 太小 → 频繁上下文切换开销大 太大 → 退化为 FCFS响应时间长 通常设置为 10-100ms5. 多级队列调度队列1前台交互进程时间片轮转高优先级 队列2后台批处理进程FCFS低优先级 高优先级队列不为空时低优先级队列不能运行6. 多级反馈队列现代 OS 常用队列1时间片8ms ← 新进程先进这里 队列2时间片16ms ← 在队列1用完时间片 → 降级到这里 队列3FCFS ← 在队列2用完时间片 → 降级到这里 特点 I/O 密集型进程主动让出 CPU始终在高优先级队列 CPU 密集型进程逐渐降级到低优先级队列 实现了对短进程和交互进程的优待面试答法现代操作系统通常使用多级反馈队列。它的核心思想是进程按行为自动分类——I/O 密集型进程频繁主动让出 CPU始终保持高优先级CPU 密集型进程会不断消耗时间片逐渐降级。这样既保证了交互响应又照顾到了 CPU 密集型任务的吞吐量。三、内存管理内存分层结构速度快→慢 大小小→大 成本高→低 CPU 寄存器 字节级 极高 L1 Cache 32KB 高 L2 Cache 256KB 较高 L3 Cache 几MB~几十MB 中 内存DRAM GB 级 低 SSD GB~TB 级 更低 HDD 磁盘 TB 级 最低虚拟内存面试核心为什么需要虚拟内存程序使用的地址是虚拟地址相互隔离进程A的地址0x1000 和 进程B的0x1000 是不同物理位置程序可以使用比物理内存更大的地址空间通过 Swap交换分区扩展可用内存虚拟地址 → 物理地址转换过程虚拟地址 │ ▼ MMU内存管理单元查页表 │ ├─ 页表项有效 → TLB 查缓存 │ │ │ ├─ TLB 命中 → 直接得到物理地址极快 │ └─ TLB 缺失 → 查内存页表 → 得到物理地址 → 更新TLB │ └─ 页表项无效页不在内存→ 缺页中断 │ 内核处理缺页 │ 从磁盘Swap/文件载入页到内存 │ 更新页表重新执行指令TLBTranslation Lookaside Buffer页表的硬件缓存存储最近使用的虚拟→物理地址映射命中率通常 99%大幅减少页表查找开销。分页 vs 分段对比项分页分段划分依据固定大小4KB逻辑单元代码段/数据段碎片内部碎片最后一页未填满外部碎片段间空隙地址结构页号 页内偏移段号 段内偏移程序员感知透明可感知分段符合程序逻辑现代OS段页式——先分段逻辑隔离每段再分页物理管理。页面置换算法当物理内存满了需要换出一页到磁盘OPT最优置换—— 理论最优置换未来最长时间不会被访问的页 缺点无法预知未来只作理论参考FIFO先进先出访问序列: 1 2 3 4 1 2 5 1 2 3 4 5内存帧数3 装入: 1 | 1 2 | 1 2 3 | 缺页换出1→ 4 2 3 | 缺页换出2→ 4 1 3 | ... 缺页次数9 缺点Belady 异常增加内存帧数缺页次数反而增加LRU最近最少使用—— 最常用置换最近最长时间没有被访问的页 实现方式 精确实现每页记录访问时间戳置换时间戳最小的开销大 近似实现访问位Reference bit 时钟算法 Go 的 GC、Redis 的 maxmemory-policy 都有类似的 LRU 思想Clock时钟置换—— LRU 的近似实现所有页排成环形每页有一个访问位0/1 置换时 指针顺序扫描 访问位1 → 置为0跳过给它一次机会 访问位0 → 置换此页 特点O(n) 时间近似 LRU 效果开销小面试答法LRU 是理论上最接近最优的置换算法基于局部性原理最近用过的将来可能还用。精确 LRU 需要维护访问时间开销大实际系统用 Clock 算法近似实现通过访问位给每个页一次第二次机会性能和效果的平衡较好。多级页表为什么需要32 位系统4KB 页单级页表需要2^20 100 万个页表项每项 4 字节每个进程需要 4MB 页表100 个进程就是 400MB无法接受。多级页表的解决思路按需分配页表只有实际使用的地址范围才创建对应的页表页。32 位两级页表 虚拟地址: [10位页目录号 | 10位页表号 | 12位页内偏移] 查找过程 1. 用页目录号查页目录第一级→ 得到页表的物理地址 2. 用页表号查页表第二级→ 得到物理页框号 3. 物理页框号 页内偏移 物理地址 好处进程只用了少量地址空间时大量页目录项为空对应的二级页表根本不创建64 位系统Linux x86-64用4 级页表PGD → PUD → PMD → PTE每级索引 9 位。内存分配算法管理空闲内存块时如何为进程分配算法策略优点缺点首次适应First Fit找第一个足够大的空闲块快头部碎片多最佳适应Best Fit找最小的足够大的空闲块空间利用率高产生大量小碎片最差适应Worst Fit找最大的空闲块剩余块较大大块被快速消耗伙伴系统Buddy按 2 的幂次分配Linux 内核使用合并简单内部碎片大Slab 分配器为固定大小对象维护对象池Linux 内核使用减少碎片速度快复杂四、死锁死锁的四个必要条件缺一不可1. 互斥条件资源一次只能被一个进程占用 2. 请求与保持进程持有资源同时等待其他资源 3. 不可抢占资源只能由持有者主动释放 4. 循环等待进程形成等待环路 A→B→C→A经典死锁场景// 两个 goroutine两把锁互相等待varmu1,mu2 sync.Mutexgofunc(){mu1.Lock()time.Sleep(1ms)mu2.Lock()// 等待 mu2但 mu2 被另一个 goroutine 持有// ...mu2.Unlock()mu1.Unlock()}()gofunc(){mu2.Lock()time.Sleep(1ms)mu1.Lock()// 等待 mu1但 mu1 被上一个 goroutine 持有// ...mu1.Unlock()mu2.Unlock()}()死锁处理策略预防破坏四个条件之一破坏互斥使用非独占资源如只读文件—— 不总是可行 破坏请求与保持进程一次性申请所有资源 —— 资源利用率低 破坏不可抢占资源可被强制剥夺 —— 可能导致数据不一致 破坏循环等待对资源编号按序申请 —— 实用按序申请锁破坏循环等待// 规定永远先锁 mu1 再锁 mu2消除循环等待gofunc(){mu1.Lock()mu2.Lock()// ...mu2.Unlock()mu1.Unlock()}()gofunc(){mu1.Lock()// 改为先锁 mu1mu2.Lock()// ...mu2.Unlock()mu1.Unlock()}()避免银行家算法进程申请资源前系统检查分配后是否仍处于安全状态能找到一个安全序列让所有进程完成。不安全则拒绝分配。资源 R 总量10已分配7剩余3 进程 A 还需 4进程 B 还需 2进程 C 还需 3 安全序列检查 先满足 B需2 ≤ 剩余3→ B 完成释放资源 → 剩余 325 再满足 C需3 ≤ 5→ C 完成 → 剩余 538 再满足 A需4 ≤ 8→ A 完成 ✓ 安全序列B→C→A检测与恢复检测维护资源分配图检测图中是否存在环恢复杀死部分进程选开销最小的资源抢占挂起进程剥夺其资源进程回滚回到安全状态的检查点面试答法实际系统中死锁预防和避免代价高通常用检测恢复或者直接假设死锁发生概率极低通过超时重试处理。数据库系统用等待图检测死锁并通过回滚代价最小的事务来恢复。Go 中用go vet检测潜在死锁运行时检测 goroutine 全部阻塞时报 deadlock。五、同步与互斥临界区问题临界区访问共享资源的代码段 进入临界区前申请进入加锁 临界区代码 操作共享资源 离开临界区后允许他人进入解锁正确的同步方案必须满足互斥同一时刻最多一个进程在临界区前进空闲让进无进程在临界区时想进入的进程能立即进入有限等待等待进入临界区的时间有上限无饥饿同步原语自旋锁 vs 互斥锁面试高频互斥锁获取失败 → 线程进入睡眠让出 CPU→ 锁释放时被唤醒 自旋锁获取失败 → 线程循环忙等不让出 CPU→ 直到获取成功 for !atomic.CompareAndSwap(lock, 0, 1) { // 自旋等待持续占用 CPU }对比项互斥锁自旋锁等待方式睡眠让出 CPU忙等占用 CPU上下文切换有进入/唤醒开销无适用场景锁持有时间长锁持有时间极短适用环境用户态、内核态主要用于内核、多核面试答法锁持有时间短微秒级且多核环境下用自旋锁避免线程切换开销锁持有时间长用互斥锁避免浪费 CPU。Go 的sync.Mutex内部先自旋几次失败后才睡眠是两者的结合自适应自旋。互斥锁Mutexvarmu sync.Mutex mu.Lock()// 临界区mu.Unlock()// 底层实现CAS 原子操作 自旋 系统调用park/unpark// 自旋一段时间后若还未获取让线程进入睡眠避免持续占用 CPU读写锁RWMutexvarrw sync.RWMutex// 读操作多个读者可以并发rw.RLock()// 读取共享数据rw.RUnlock()// 写操作写者独占rw.Lock()// 修改共享数据rw.Unlock()// 适用读多写少场景缓存、配置读取信号量Semaphore计数信号量控制同时访问资源的数量上限 二进制信号量等价于互斥锁0/1 P 操作wait信号量 -1若 0 则阻塞 V 操作signal信号量 1若 ≤ 0 则唤醒一个等待进程 // Go 中用 channel 模拟信号量 sem : make(chan struct{}, 10) // 同时最多 10 个并发 sem - struct{}{} // P 操作获取 defer func() { -sem }() // V 操作释放条件变量Condvarmu sync.Mutex cond:sync.NewCond(mu)queue:[]int{}// 消费者等待队列有数据mu.Lock()forlen(queue)0{// 必须用 for不能用 if防止虚假唤醒cond.Wait()// 原子地释放锁 进入等待}item:queue[0]queuequeue[1:]mu.Unlock()// 生产者放入数据唤醒消费者mu.Lock()queueappend(queue,item)cond.Signal()// 唤醒一个等待者或 cond.Broadcast() 唤醒所有mu.Unlock()经典同步问题生产者-消费者问题空槽位信号量 emptyN初始值缓冲区大小 满槽位信号量 full0 互斥锁 mutex 生产者: P(empty) // 等待有空槽 P(mutex) // 进入临界区 放入数据 V(mutex) V(full) // 通知消费者有数据了 消费者: P(full) // 等待有数据 P(mutex) 取出数据 V(mutex) V(empty) // 通知生产者有空槽了哲学家进餐问题死锁经典5 位哲学家5 根筷子每人需要左右两根 → 若同时拿左边的所有人等待右边死锁 解决方案 ① 最多允许 4 人同时拿筷子破坏循环等待 ② 奇数哲学家先拿左边偶数先拿右边破坏对称性 ③ 用信号量拿两根筷子作为原子操作六、Go GMP 调度模型Go 后端必考GMP 三个概念GGoroutineGo 协程初始栈 2KB用户态轻量级线程 MMachine OS 线程真正在 CPU 上执行 PProcessor逻辑处理器持有本地 Goroutine 队列连接 G 和 M GOMAXPROCS 决定 P 的数量默认等于 CPU 核数调度架构全局队列Global Queue │ 工作窃取/溢出 ▼ ┌──────────────────────────────────────────────┐ │ P0 P1 P2 P3 │ │ 本地队列 本地队列 本地队列 本地队列│ │ [G][G][G] [G][G] [G][G][G][G] [] │ │ │ │ │ │ │ │ M0 M1 M2 M3 │ └──────────────────────────────────────────────┘关键机制工作窃取Work StealingP3 的本地队列为空从其他 P 的队列尾部偷取一半 Goroutine保证所有 P 始终有活干CPU 利用率最大化Hand Off移交M0 上的 G0 发生系统调用阻塞如文件读写M0 将 P 移交给其他空闲 MP 继续调度其他 GM0 阻塞完成后尝试获取 P若无空闲 PG0 放入全局队列M0 进入休眠抢占式调度Go 1.14 前协作式抢占只在函数调用时检查Go 1.14 后异步抢占基于系统信号SIGURG即使 goroutine 无函数调用也能被抢占为什么 goroutine 比线程快 1. 创建开销小goroutine 初始栈 2KB线程通常 1-8MB 2. 切换开销小用户态切换不进内核不刷 TLB 3. 数量可以很多百万级 goroutine而线程数千就是上限 4. 栈动态增长goroutine 栈按需扩缩2KB → 最大 1GB面试答法GMP 模型是 Go runtime 的核心调度机制。G 是 goroutineM 是系统线程P 是调度器本地 goroutine 队列。P 的数量等于 CPU 核数每个 P 绑定一个 M 运行 G。当 G 发生系统调用阻塞时P 会脱离当前 M 去找新的 M 继续运行其他 G保证 CPU 不空闲。工作窃取机制保证负载均衡。七、内存一致性与 CPU 缓存CPU 缓存层级与缓存一致性多核 CPU Core1: [L1 Cache] [L2 Cache] ─┐ Core2: [L1 Cache] [L2 Cache] ─┼── [L3 Cache] ── [内存] Core3: [L1 Cache] [L2 Cache] ─┘ 问题Core1 修改了变量 xCore2 的缓存中 x 还是旧值 解决MESI 协议Modified/Exclusive/Shared/Invalid Modified该缓存行被修改与内存不一致 Exclusive该缓存行只在本核与内存一致 Shared该缓存行在多个核与内存一致 Invalid该缓存行无效不可使用内存屏障与可见性CPU 指令重排CPU 和编译器会对指令重排以优化性能但可能破坏多线程的预期执行顺序。// 没有同步时可能被重排// 线程1:a1// 可能被重排到 flag1 后面flag1// 线程2:ifflag1{print(a)// 可能打印 0因为 a1 被重排了}// 解决使用 sync/atomic 或 mutex它们隐含内存屏障Go 内存模型Go 的 happens-before 规则channel 发送 happens-before 对应的接收sync.Mutex.Lock()happens-before 上一次Unlock()之后的操作sync/atomic操作提供顺序一致性七、文件系统文件系统结构磁盘布局 ┌──────────┬────────────┬──────────┬──────────────────────┐ │ 引导块 │ 超级块 │ inode 表 │ 数据块 │ │ (Boot) │ (FS元数据) │ │ │ └──────────┴────────────┴──────────┴──────────────────────┘ 超级块文件系统类型、总块数、空闲块数、inode 总数 inode 表所有 inode 的数组 数据块存储实际文件内容和目录条目文件读取全过程open(/home/user/file.txt, O_RDONLY) 1. 从根目录 inode 开始 2. 读根目录数据块找 home 目录项 → 获取 home 的 inode 号 3. 读 home 的 inode找到 home 的数据块 4. 读 home 数据块找 user → 获取 user 的 inode 号 5. 重复直到找到 file.txt 的 inode 6. 返回文件描述符fd内核在进程的文件描述符表中记录 read(fd, buf, 1024) 1. 通过 fd 找到对应的 inode 2. inode 中找到数据块地址 3. 读取数据块内容到内核缓冲区Page Cache 4. 从内核缓冲区拷贝到用户空间 buf文件描述符与打开文件表进程A 内核 fd 表: 打开文件表: inode 表: fd 0 → ──────────────▶ [file0: pos0, flags] ──▶ inode(stdin) fd 1 → ──────────────▶ [file1: pos0, flags] ──▶ inode(stdout) fd 3 → ──────────────▶ [file2: pos128, O_RDONLY] ──▶ inode(file.txt) ↑ 进程B │ fd 3 → ──────────────▶ [file3: pos0, O_RDONLY] ──────────┘ 不同文件描述符各自独立的读写位置 但共享同一个 inode也就是同一文件八、系统调用与中断系统调用用户态 vs 内核态用户态CPU 运行在限制模式不能直接访问硬件不能执行特权指令 内核态CPU 运行在特权模式可以访问所有硬件和内存 为什么要区分保护内核防止用户程序破坏系统 切换时机 用户态 → 内核态系统调用syscall、硬件中断、异常缺页 内核态 → 用户态系统调用返回、中断处理完成系统调用流程用户程序调用 read(fd, buf, n) │ │libc 封装设置参数到寄存器执行 syscall 指令 ▼ CPU 切换到内核态保存用户态寄存器 │ 内核的 sys_read() 执行 │ 可能睡眠等待 I/O │ 数据就绪拷贝到用户空间 │ 恢复用户态寄存器切换回用户态 │ ▼ read() 返回读取的字节数常见系统调用类别系统调用进程fork, exec, exit, wait, getpid文件open, read, write, close, lseek, stat内存mmap, munmap, brk网络socket, bind, listen, accept, connect, send, recv同步futexmutex/cond 的底层中断机制中断类型 硬中断硬件设备发出键盘按键、网卡收包、磁盘 I/O 完成 软中断软件触发系统调用 int 0x80、调试 breakpoint 异常CPU 执行出错除零、缺页、段错误 中断处理流程 1. 硬件保存当前 CPU 状态压栈 2. 查中断向量表跳转到对应的中断处理程序 3. 执行中断处理内核态 4. 恢复 CPU 状态继续被中断的程序 中断与进程调度的关系 时钟中断timer interrupt是进程调度的触发器 每次时钟中断内核检查当前进程时间片是否用完 用完则触发上下文切换调度下一个进程九、经典面试问题速答Q进程、线程、协程的区别进程是资源分配单位有独立地址空间切换开销大崩溃不影响其他进程。线程是调度单位共享进程资源切换比进程快一个线程崩溃影响整个进程。协程是用户态的轻量级线程由程序自己调度不进内核切换开销极小Go 的 goroutine 就是协程初始栈只有 2KB可以轻松创建百万个。Q进程间通信有哪些方式各有什么特点共享内存最快直接读写同一块内存零拷贝需配合信号量同步管道简单但只能亲缘进程单向消息队列有结构、可设优先级适合解耦Socket 可跨机器最通用信号用于异步通知如 kill 命令。实际项目中同机进程通信用 Unix Domain Socket 或共享内存跨机器用 TCP Socket。Q什么是死锁如何避免死锁是多个进程/线程因竞争资源而互相等待陷入无法前进的状态必须同时满足互斥、请求与保持、不可抢占、循环等待四个条件。最实用的避免方法① 统一加锁顺序破坏循环等待② 使用超时机制加锁失败则回退重试③ 尽量减少锁的粒度和持有时间。Go 的sync.Mutex没有超时可以用tryLock 重试或通过 channel 实现带超时的锁。QLRU 如何实现时间复杂度是多少用哈希表 双向链表实现 O(1) 的 get 和 put。哈希表存 key → 链表节点的指针双向链表按访问时间排序头部最近访问尾部最久未访问。get 时把节点移到头部put 时若 key 存在则更新并移到头部若缓存满则删除尾部节点再插入头部。这也是 LeetCode 146 题的标准解法。Q虚拟内存有什么作用三个作用① 进程隔离——每个进程有独立的虚拟地址空间互不干扰② 内存超卖——进程可以使用比物理内存更大的地址空间不常用的页换出到磁盘Swap③ 内存共享——多个进程可以映射同一物理页如共享库节省内存。Q缺页中断是什么进程访问的虚拟页不在物理内存中时MMU 触发缺页中断内核找到对应的数据在 Swap 或文件中分配一块物理内存把数据载入更新页表然后重新执行触发中断的指令。整个过程对程序透明。Q用户态和内核态的区别为什么要区分用户态 CPU 运行在非特权模式不能直接访问硬件、不能执行特权指令内核态可以执行任何指令访问所有内存和硬件。区分是为了保护防止用户程序误操作或恶意破坏内核数据和硬件。系统调用是用户程序请求内核服务的标准通道每次系统调用都要切换到内核态有一定开销微秒级所以高性能程序尽量批量操作减少系统调用次数。Q什么是上下文切换开销来自哪里上下文切换是 CPU 从运行一个任务切换到另一个任务需要保存当前任务的寄存器、程序计数器等状态再恢复下一个任务的状态。开销来自① 直接开销保存/恢复寄存器几十纳秒② 间接开销进程切换时需刷新 TLB地址转换缓存失效可能引发大量 TLB miss导致后续内存访问变慢。这是为什么 goroutine用户态切换不刷 TLB比线程快的重要原因。Q进程调度算法中为什么现代 OS 用多级反馈队列因为进程的特性I/O 密集型还是 CPU 密集型在运行时才能体现无法事先知道。多级反馈队列让进程的行为暴露自己的特性I/O 密集型进程频繁主动让出 CPU始终保持在高优先级队列获得快速响应CPU 密集型进程不断消耗时间片自然降级到低优先级队列获得高吞吐。这是自适应的调度策略不需要预知进程类型。Q信号量和互斥锁的区别互斥锁是二值信号量的特例用于互斥访问0/1。信号量更通用计数信号量可以控制同时访问资源的进程数量如连接池限制最大连接数为 10。另一个关键区别互斥锁必须由加锁的线程解锁所有权信号量可以由其他线程执行 V 操作无所有权适合生产者-消费者这种跨线程的同步场景。十、一张图记住核心进程与线程 进程资源隔离独立地址空间 通信需IPC 线程轻量调度共享内存 切换快 崩溃影响全进程 goroutine用户态协程2KB栈 runtime调度 channel通信 内存管理 虚拟内存 → 页表 → MMU → 物理地址 TLB缓存热点页表项缺页中断从磁盘载入 置换算法LRU哈希表双向链表 O(1) 实现 死锁四条件互斥 请求保持 不可抢占 循环等待 死锁预防最实用按序加锁 超时回退 同步原语对比 Mutex互斥锁独占访问 RWMutex读写锁读多写少场景 Semaphore信号量控制并发数量 Cond条件变量等待某个条件成立 系统调用用户态→内核态→执行服务→内核态→用户态 每次切换有微秒级开销高性能代码要减少 syscall 次数QGo 的 GMP 模型是什么GMP 是 Go runtime 的协程调度模型。Ggoroutine是协程任务MMachine是系统线程PProcessor是调度器持有本地 G 队列。GOMAXPROCS 个 P 并行运行每个 P 绑定一个 M。G 发生阻塞系统调用时P 脱离 M 去找空闲 M 继续调度保证 CPU 不浪费。P 队列为空时从其他 P 窃取Work Stealing。goroutine 比线程快的原因用户态切换不进内核、初始栈只有 2KB、数量可以百万级。Q栈内存和堆内存的区别栈由编译器自动管理存局部变量和函数调用信息分配/释放极快移动栈指针但大小有限默认 8MB。堆由程序员/GC 管理存动态分配的对象大小灵活但分配慢且有碎片问题。Go 通过逃逸分析决定变量分配在哪函数返回后仍被引用的变量逃逸到堆否则分配在栈。Q自旋锁和互斥锁怎么选锁持有时间极短微秒级且是多核环境用自旋锁避免线程切换开销锁持有时间较长用互斥锁避免长时间空转浪费 CPU。Go 的sync.Mutex内部先自旋若干次失败后才睡眠是自适应的结合方案不需要手动选择。Q什么是 fork-exec 模式Copy-on-Write 有什么用shell 执行命令时先fork()复制当前进程再exec()在子进程中加载新程序。fork 本身很快因为 Copy-on-Write 不立即复制内存父子进程共享同一物理页只有写操作发生时才复制那一页避免了立即复制大量内存的开销。总结操作系统面试核心考点是进程线程协程区别、GMP 调度模型、死锁四条件与解决方案、虚拟内存与缺页中断、LRU 实现、用户态内核态切换、栈与堆的区别。这七个方向吃透操作系统面试就稳了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2411561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…