# 1. 为什么多数优化器以 IR(如 LLVM IR、GCC GIMPLE)而非机器码作为主要优化对象? A 机器码优化比 IR 优化更通用 B IR 保留高层语义且目标无关,一套优化覆盖多架构;机器码层仍做指令选择与寄存器分配等目标特有优化 ✓ 正确答案 C IR 优化只能用于单一架构 D IR 优化与源码结构无关
# 2. 设计实验,在 Linux 用 fio 对比 deadline vs bfq vs none 调度器在 SSD 上的 P99? A 实验应固定设备与负载仅切换调度器,用 clat 的 P99/P99.9 度量,多轮重复取稳定值;SSD 上 none 通常最优 ✓ 正确答案 B 调度器对比不需要控制负载参数 C bfq 在任何负载下延迟都最低 D P99 单次测量即可得出结论
# 3. ELF 目标文件的 .text/.data/.bss/.rodata 节区分别存放什么,.bss 为何不占用文件空间? A .rodata 映射为可写 B .bss 也保存初值 C .text 存指令、.rodata 存只读常量、.data 存已初始化数据、.bss 存未初始化数据;.bss 为零初值且以 NOBITS 描述,不占文件空间 ✓ 正确答案 D .data 中的数据在加载时不保留初值
# 4. 汇编伪指令(如 .globl、.align、.section、.quad)与真实机器指令的区别是什么,汇编器如何展开它们? A .align 只影响注释 B 伪指令与机器指令等价 C .globl 会产生一条机器指令 D 伪指令是给汇编器的命令,不产生执行语义:.globl 导出符号、.align 对齐填充、.quad 生成 8 字节数据 ✓ 正确答案
# 5. 调用者保存(caller-saved)与被调用者保存(callee-saved)寄存器如何在函数调用间划分责任? A caller-saved 寄存器被调函数不能使用 B callee-saved 寄存器由被调函数负责保存恢复,caller-saved 寄存器由调用者按需保存,寄存器分配据此决策 ✓ 正确答案 C callee-saved 寄存器调用前后值一定变化 D 参数寄存器属于 callee-saved
# 6. 目标文件中的重定位节(.rela.text)记录了什么信息,为何汇编阶段无法确定绝对地址? A .rela.text 存储机器指令本身 B 汇编器可以直接确定所有绝对地址 C 汇编阶段外部符号与最终布局未知,故以 .rela.text 记录位置、符号、类型与 addend,由链接器按最终地址修补 ✓ 正确答案 D 重定位只发生在静态链接
# 7. 栈对齐(16 字节)要求为何是 ABI 强制的,违反它对 SSE 指令与调用有何后果? A System V ABI 要求 call 前 rsp 16 字节对齐,因 movaps 等对齐访存指令需对齐,违规触发 #GP ✓ 正确答案 B 栈对齐只影响性能不影响正确性 C movaps 不要求操作数对齐 D 编译器会自动修复所有违规调用
# 8. SSA 形式中 φ 函数为何要在控制流汇合点插入?它解决了多条路径赋值到同一变量带来的什么问题? A φ 只在顺序代码中插入 B φ 函数产生机器码 C 汇合点多条路径给同一变量带来多个版本,φ 函数按来路合并版本,恢复每变量单赋值,简化 def-use 链 ✓ 正确答案 D φ 函数不影响数据流分析
# 9. 为什么 SSA 保证每个变量只被赋值一次后,常量传播与死代码消除的实现会显著简化? A SSA 下每个 use 对应唯一 def,常量传播沿 use 链前向求值、死代码靠 use 计数为 0 判定,免去迭代数据流求解 ✓ 正确答案 B SSA 使常量传播需要更多迭代 C SSA 下死代码无法识别 D φ 节点使 def-use 关系更模糊
# 10. 公共子表达式消除(CSE)依赖哪种数据流信息?局部 CSE 与全局 CSE 分别在什么范围内进行? A CSE 不考虑内存读写副作用 B CSE 只做局部优化 C 全局 CSE 使用后向数据流分析 D CSE 依赖可用表达式信息:局部 CSE 在块内扫描哈希表,全局 CSE 需前向数据流分析跨块传播 ✓ 正确答案
# 11. 强度削减如何借助归纳变量把循环内的乘法替换为累加?它对地址计算优化有何意义? A 强度削减会增加循环内指令数 B 强度削减只适用于浮点运算 C 归纳变量与循环无关 D 强度削减把循环内与归纳变量相关的乘法替换为增量累加,地址计算可转为指针递增,减少指令延迟 ✓ 正确答案
# 12. 寄存器分配的图着色算法为何是 NP 完全问题?当物理寄存器(颜色)不够时,溢出(spill)到内存的代价如何衡量? A 溢出只影响代码体积不影响性能 B 图着色寄存器分配是多项式可解的 C 溢出不会增加指令数 D 寄存器分配等价于图 k-着色,一般图 k-着色是 NP 完全,故用启发式近似;溢出代价按循环频率与可重算性加权 ✓ 正确答案
# 13. 内联展开在消除调用开销的同时带来代码膨胀,编译器常用哪些启发式(调用频率、函数体大小)控制膨胀? A 内联用成本模型权衡:函数体大小与调用点数量乘积、调用频率、递归深度、栈帧大小都是膨胀控制启发式 ✓ 正确答案 B 内联总是提升性能,应尽量多用 C 内联不会增加代码体积 D 内联对递归函数无任何限制
# 14. 死代码消除如何判断某条指令的结果永不被使用?带副作用的调用为何不能当作死代码删除? A volatile 读可以安全删除 B 无 use 的调用都可以删除 C DCE 不考虑副作用 D DCE 删除结果无 use 且无副作用的指令;调用因可能写内存、抛异常等副作用而不可随意删除 ✓ 正确答案
# 15. 常量折叠与常量传播的区别是什么?它们在 IR 上为何常常迭代进行? A 常量传播只需执行一遍 B 折叠与传播是同一变换 C 折叠对单条全常量指令求值,传播把常量 def 替换到 use;传播制造新常量、折叠求值,交替迭代到不动点 ✓ 正确答案 D 折叠能处理含变量操作数
# 16. 静态链接的符号解析与重定位是两个独立阶段,它们各自解决什么问题,为什么重定位必须在符号解析之后才能完成? A 符号解析决定段布局 B 重定位不依赖符号解析 C 符号解析把引用绑定到定义,重定位按解析结果与最终段布局计算地址;重定位依赖解析确定的定义位置,故必须后行 ✓ 正确答案 D 两个阶段可任意交换顺序
# 17. 强符号与弱符号同名冲突时链接器遵循什么取舍规则,出现多个弱符号时又如何决定最终归属? A 多个强符号同名会自动合并 B 弱符号与强符号同名时弱符号胜出 C 强符号与弱符号同名时强定义胜出,多个弱符号任选其一,弱引用允许无定义存在 ✓ 正确答案 D 弱引用无定义时链接报错
# 18. 可重定位目标文件中的 R_X86_64_PC32、R_X86_64_PLT32、R_X86_64_GOTPCREL 等重定位类型分别用在什么场合? A GOTPCREL 用于函数调用 B 三种类型都用于直接寻址 C PC32 用于直接 PC 相对寻址,PLT32 用于经 PLT 调用动态函数(懒绑定),GOTPCREL 用于 PIC 下经 GOT 间接访问全局数据 ✓ 正确答案 D PLT32 与懒绑定无关
# 19. 链接器脚本(linker script)如何控制段布局与符号地址,-Ttext 与 SECTIONS 对嵌入式裸机程序意味着什么? A 裸机程序的运行地址由操作系统决定 B 链接器脚本只能设置输出文件名 C -Ttext 只影响动态链接 D 链接器脚本用 SECTIONS 控制段地址、对齐与组成,并定义 __bss_start 等锚点符号;裸机程序靠 -Ttext/脚本钉死入口与内存布局 ✓ 正确答案
# 20. 静态链接与动态链接在启动时间、运行时内存共享和版本升级灵活性上各有哪些取舍? A 动态链接的代码段不能共享 B 动态链接启动总是快于静态 C 静态链接支持库的热升级 D 静态链接启动快、无运行依赖,但内存不共享且升级需重链;动态链接共享库代码页并支持热升级,代价是启动解析与依赖管理 ✓ 正确答案
# 21. 系统调用 ABI,x86-64 中 syscall 指令如何传参(rax 存编号、rdi/rsi/rdx/r10/r8/r9 存参数),内核如何把 -errno 编码返回给用户态? A syscall 指令不破坏任何寄存器 B syscall 第 4 个参数用 rcx 传递 C 内核返回错误码为正数 D syscall 以 rax 存调用号、rdi/rsi/rdx/r10/r8/r9 传参,返回时 rax 为负表示 -errno,由 libc 转为 errno 并返回 -1 ✓ 正确答案
# 22. 静态库(.a)按成员归档,链接时为何会出现成员顺序依赖,--start-group/--end-group 如何解决循环引用? A 静态库按需提取且只扫描一遍,循环引用会导致符号未解析;--start-group/--end-group 组内循环扫描直到无新解析 ✓ 正确答案 B 静态库成员总是全部链接 C 链接顺序不影响静态库 D --start-group 会增加运行时开销
# 23. 动态链接器 ld-linux.so 在启动时如何完成依赖库加载、符号查找与重定位,LD_BIND_NOW 与 RELRO 改变了什么? A LD_BIND_NOW 会降低启动时间 B 懒绑定在启动时完成全部解析 C ld.so 递归加载 DT_NEEDED 库并做符号解析与重定位;LD_BIND_NOW 禁用懒绑定提前解析,配合 RELRO 使 GOT 只读 ✓ 正确答案 D RELRO 与 GOT 权限无关
# 24. TLS 的 General Dynamic 与 Initial Exec 访问模型在动态链接与静态链接下的开销差异来自哪里? A GD 的访问开销为一次立即数加法 B IE 支持 dlopen 加载的模块 C GD 每次访问经 __tls_get_addr 动态查找(支持 dlopen),IE 由加载期解析偏移后直接访问,静态链接可用 LE 零查找 ✓ 正确答案 D 两种模型开销完全相同
# 25. System V AMD64 调用约定下函数序言(push rbp; mov rbp,rsp; sub rsp,N)与尾声如何搭建和拆除栈帧? A 序言的 sub rsp,N 分配的是参数空间 B leave 只恢复 rsp 不恢复 rbp C 序言 push rbp 保存旧帧、mov rbp,rsp 设帧底、sub rsp,N 分配局部区;leave(mov rsp,rbp; pop rbp)+ret 还原并返回 ✓ 正确答案 D ret 从 rbp 指向的位置弹出返回地址
# 26. leave 与 ret 指令分别完成栈帧拆除的哪一步,红区(red zone)在叶子函数中为何可被安全使用? A leave 直接执行返回跳转 B leave 释放局部空间并恢复 rbp,ret 弹出返回地址跳转;红区是 rsp 下方 128 字节,叶函数可安全用作临时存储 ✓ 正确答案 C 红区可以被信号处理程序写入 D 非叶函数也能无条件使用红区
# 27. 循环不变量外提(LICM)把计算移出循环体需要满足哪些安全性条件?为什么不能随意移动可能抛异常的指令? A LICM 不需要别名分析 B 只要表达式每次值相同就可以外提 C 异常指令外提不会改变程序行为 D LICM 外提需满足不变量、支配、无副作用与异常安全四条件;可能抛异常的指令外提会改变异常时机,故保守不移动 ✓ 正确答案
# 28. PLT 与 GOT 如何协作实现动态函数的延迟绑定(lazy binding),第一次调用与后续调用走过的路径有何不同? A 首次与后续调用路径完全相同 B 首次调用经 PLT stub 跳 GOT 初值(回指 stub)触发 _dl_runtime_resolve 解析并回填 GOT;后续调用经已回填 GOT 直跳真实函数 ✓ 正确答案 C GOT 项初始值就是真实函数地址 D 懒绑定在启动时完成全部解析
# 29. _exit 与 exit 在 C 程序退出时的清理顺序有何差异,atexit 与 atfork 钩子分别在何时触发? A _exit 也会运行 atexit 处理器 B exit() 逆序运行 atexit、flush stdio 后 _exit;_exit 直接系统调用终止不清理缓冲;atfork 钩子在 fork 前/父/子三处触发 ✓ 正确答案 C atfork 在程序退出时触发 D exit() 不刷新 stdio 缓冲
# 30. Linux request queue 在块设备层(bio)的角色? A request queue 只用于排序不用于合并 B bio 直接发给设备 C bio 描述页向量与扇区范围的逻辑 I/O,块层把可合并的 bio 组装成 request 并经 request queue 调度下发 ✓ 正确答案 D request 与 bio 无关
# 31. Linux tc qdisc(queuing discipline)在网络出口的流量整形? A tbf 不能限制平均速率 B qdisc 只用于入口流量 C qdisc 挂在设备出口控制排队与发送时机,tbf/htb 按令牌桶限速整形,fq_codel 提供公平队列与 AQM ✓ 正确答案 D 默认 qdisc 是 noqueue
# 32. Linux 中 cfq deadline noop 三种调度器在背压中的差异? A 三种调度器行为完全相同 B noop/none 近 FIFO 直通、背压直接;deadline 按截止时间保延迟有界;cfq/bfq 按进程公平分配时间片,背压传导慢但公平 ✓ 正确答案 C deadline 不区分读写 D cfq 适合延迟敏感数据库
# 33. 为何 credit-based 流量控制(RoCE、InfiniBand)优于 pause frame? A pause frame 不存在死锁风险 B pause frame 支持逐队列精确流控 C credit-based 不需要接收端反馈 D credit-based 按接收缓冲额度精确控制发送,避免 pause frame 暂停整端口带来的 HOL 阻塞与无关流波及 ✓ 正确答案
# 34. 推导为何 NIC Rx ring buffer 满时 NIC 丢包而 paused? A Rx ring 满时 NIC 无可用 DMA 目标只能丢包,成因是消费速度低于到达速率;缓解靠多队列、加大 ring、NAPI 预算与 busy poll ✓ 正确答案 B ring 满时包会等待而不是丢弃 C rx_missed 表示 CPU 处理丢包 D 加大 ring 无任何副作用
# 35. -fomit-frame-pointer 为何能省出 rbp 寄存器,被省略的调用链如何用 .eh_frame(DWARF unwind 信息)恢复? A .eh_frame 只在调试构建中生成 B 省略帧指针后无法再回溯调用栈 C rbp 释放后局部变量无法寻址 D -fomit-frame-pointer 释放 rbp 供计算使用、减少序言指令,栈回溯改由 .eh_frame 的 CFI 信息按 RIP 查 FDE 恢复 ✓ 正确答案
# 36. x86-64 指令编码中的 REX 前缀与 ModRM 字段如何扩展寄存器编号,为什么 r8-r15 需要额外前缀位? A REX 前缀在 32 位模式下也可用 B REX 前缀只影响操作数大小 C r8-r15 使用与 rax-rbx 相同的短编码 D ModRM 的 reg/rm 各 3 位只能编码 8 寄存器,REX 前缀的 R/X/B 位扩展出第 4 位,从而覆盖 r8-r15 ✓ 正确答案
# 37. 调用约定中前六个整型参数用 rdi/rsi/rdx/rcx/r8/r9 传递,超出部分与返回值如何在栈和 rax 上处理? A 所有参数都通过栈传递 B 前六个整型参数经 rdi/rsi/rdx/rcx/r8/r9 传递,第七个起压栈;返回整型用 rax,浮点用 xmm0 ✓ 正确答案 C 返回值最多 32 位 D 浮点参数占用整型寄存器通道
# 38. 宏汇编与条件汇编在预处理期如何展开,它与 C 预处理器宏在展开时机上有何异同? A 汇编宏与 C 宏都是编译/汇编前的文本替换;汇编宏由汇编器处理并可嵌套与生成唯一局部符号,C 宏由 cpp 处理且支持 #/## 操作符 ✓ 正确答案 B 汇编宏在运行时展开 C C 宏在语法分析之后展开 D 条件汇编属于运行期分支
# 39. 优化 pass 的执行顺序为何会影响最终效果?举例说明一个 pass 可能使另一个 pass 的机会消失。 A 每个 pass 相互独立可任意排序 B pass 顺序对结果无影响 C pass 间存在机会创造与破坏关系,如内联为常量传播创造机会、过早 CSE 会破坏后续折叠机会,故流水线顺序需精心编排 ✓ 正确答案 D 优化档位与 pass 顺序无关
# 40. 位置无关代码(PIC)为什么必须通过 GOT 间接寻址访问全局数据,直接寻址会破坏共享库的什么特性? A PIC 直接编码绝对地址 B PIC 经 GOT 间接寻址全局数据,加载时由 ld.so 按实际基址填充,保持任意基址加载、符号覆盖与指令页共享 ✓ 正确答案 C GOT 寻址比直接寻址更快 D PIC 只影响数据不影响函数
# 41. C++ 的 vague linkage 为何会让虚表与内联函数在多个目标文件中重复定义,COMDAT/section group 如何完成去重? A vtable 与内联函数按 vague linkage 每 TU 生成一份,编译器放入 COMDAT 组,链接器按签名符号只保留一份完成去重 ✓ 正确答案 B 多份 vtable 会触发重复定义错误 C COMDAT 组不保证完整性 D 内联函数不允许重复生成
# 42. 命名空间与 cgroup 在隔离范围(视图 vs 资源)上的根本区别是什么,PID 命名空间为何让进程可拥有独立 1 号? A 命名空间与 cgroup 功能重复 B cgroup 也能隔离进程视图 C PID namespace 内的 1 号进程是宿主机的 PID 1 D namespace 隔离视图(进程树/网络/挂载),cgroup 限制资源用量;PID namespace 内从 1 重新编号,首进程承担容器 init 语义 ✓ 正确答案
# 43. 分支预测的工程手段,__builtin_expect(likely/unlikely)如何影响代码布局,BTB 溢出对分支预测性能有何影响? A BTB 容量无限 B __builtin_expect 直接控制 CPU 预测器 C __builtin_expect 让编译器把热路径排成直落布局、冷路径跳转,改善取指;BTB 容量有限,分支目标过多会溢出导致预测失败惩罚 ✓ 正确答案 D likely 只影响语法不影响布局