简介这份课后习题参考解答配套《操作系统原理及应用》主编陈敏、许雪林、汤龙梅面向计算机专业学生、自考人员及备考者主要用于课后练习的自我检测与考点巩固。内容覆盖操作系统功能、分类、设计理念、安全性、硬件支持、易用性等章节习题尤其针对Windows与Linux的系统构成、安全性、硬件驱动、易用性等多维差异以及分时系统与实时系统的比较、多道程序设计需要解决的问题等均给出条理清晰的参考答案便于对照教材梳理答题逻辑、掌握论述要点。资源包内为1个docx文档整体大小仅65KB轻量易用可直接搜索、编辑或打印。目前已有1109人学习下载适合期末复习、考研基础巩固及教师备课参考也可帮助初学者快速抓住操作系统核心概念。1. 操作系统原理到底在解决什么多数线上故障不是算法错了而是操作系统资源边界没守住。陈敏、许雪林、汤龙梅主编的《操作原理及应用》这类教材核心只讲一件事在有限的内存、CPU、磁盘面前让多个程序安全地共享一台机器。理解它的最快方式不是背页面置换而是打开一个 Linux shell把每个机制用命令照出来。下面沿着进程、内存、文件系统的主线推进每个原理对应一条可复现的命令最后落到《linux操作系统基础知识》里常考的优先级、缺页、上下文切换这些指标上无论是准备操作系统期末复习还是排查生产环境抖动这套路径都适用。2. 进程、内存与文件系统教材里的操作系统机制操作系统课程讲原理最终要回答的问题永远是资源不够了怎么办。单核 CPU 只能同时执行一个指令流物理内存有限磁盘 IO 又比内存慢几个数量级但用户希望程序之间既隔离又能协作。于是内核演化出三套机制进程承载 CPU 调度虚拟内存承载地址隔离文件系统承担数据持久化。这三套机制不是并列的进程一创建就要分配地址空间地址空间又依赖文件系统换入换出所以最好先把每一段的关系理清。2.1 进程状态机用 ps 能看到调度器在做什么进程的本质是一个状态机。创建、就绪、运行、阻塞、退出每一步都由内核里的调度器推进。教材中的 PCB 进程控制块在 Linux 里就是task_struct它被挂在运行队列或等待队列上由 CFS 等调度算法挑选。用户态看不到task_struct但能通过进程状态字段看到它的投影。ps -eo pid,ppid,stat,pri,ni,wchan:32,comm --sortstat | head -15stat第一列是状态字符R 运行、S 可中断睡眠、D 不可中断睡眠、T 停止、Z 僵尸pri是内核动态优先级ni是 nice 值wchan表示进程正在内核的哪个函数上等待比如等在pipe_wait上就可以判断它在等管道数据。Linux 默认调度器 CFS 不设固定时间片而是用虚拟运行时间选择最欠缺 CPU 的进程所以普通进程之间没有“谁优先级绝对高”的说法只有权重差别。线上最需要警惕的是 D 状态进程越积越多说明进程正卡在不可中断的磁盘或内核操作上此时 load average 很高但 CPU 使用率不高继续查存储层比查应用层有价值。2.2 虚拟内存缺页中断是性能问题的重灾区每个用户态进程都以为拥有从 0 到内核地址边界以下的完整地址空间实际上物理内存被页表切成 4KB 或更大的页面。CPU 访问未建立映射的地址时触发缺页中断内核在中断路径里填充页表如果页已经被换出到 swap还要先读回磁盘。这个过程对应用透明但会产生明显的延迟毛刺。ps -o pid,minflt,majflt,vsz,rss,cmd -C nginxminflt是次要缺页数页面已经在内存中只是页表没映射代价较小majflt是主缺页数需要访问磁盘一次可能就是毫秒级。vsz是虚拟内存大小rss是实际驻留内存。判断内存是否过度回收不能只看free要看majflt的增长率如果持续上涨说明应用在不断换入换出内存或 swap 配置一定有问题。虚拟内存存在的理由有两个隔离和重定位。每个进程独立页表进程 A 无法直接读写 B 的物理页页表又让程序链接时不用关心物理内存的碎片虚拟地址连续即可。这也是为什么共享内存能作为进程间通信手段本质上只是两个进程把同一个物理页映射进各自的虚拟地址空间。2.3 进程、线程与协程的边界很多复习资料把进程、线程、协程放在一起比较它们不是并列概念。进程是资源分配单位线程是内核调度单位协程是用户态自行管理的执行流。它们各自解决一个问题也各有代价。调度单位是否内核调度切换成本共享资源典型场景进程是高需要切换页表并刷新 TLB不共享依赖 IPC隔离要求高的服务线程是中共享地址空间但切换内核栈和寄存器堆、全局变量、文件描述符多核并行计算协程否用户态调度低只换寄存器和栈指针线程内全部资源IO 密集异步框架热门检索词“计算机操作系统管程和协程”里管程是另一套东西一种同步构造用互斥和条件变量把共享资源访问包装起来解决的是并发正确性不是调度成本。协程解决的是大量 IO 等待时线程浪费栈空间的问题比如 C 里一个线程默认栈可达数 MB而 Go 的 goroutine 初始栈只有 2KB 左右。选型时最常犯的错是在 CPU 密集场景用协程结果单个事件循环被计算卡死或者在协程里调用阻塞 IO把用户态切换的优势全部抵消。3. 在 Linux 上把原理变成可复现的命令教材给机制命令给证据。下面以 Ubuntu 20.04 LTS 这类常见教学环境为例命令在 RHEL、Rocky 等发行版上同样适用。整节不需要图形界面也不会引入额外监控平台只依赖系统自带工具。3.1 先看清你手上的内核和发行版信息uname -a cat /etc/os-release lscpu | grep -E ^CPU\(s\)|Model name|NUMAuname -a同时给出内核版本、主机名、架构和编译时间cat /etc/os-release告诉你这是哪个发行版、基于什么体系。排查问题时发行版信息比内核版本更重要因为 Ubuntu 和 RHEL 的 glibc、systemd 版本差异会直接影响行为。lscpu里的 NUMA 节点数决定了绑核策略进程在一个 NUMA 节点上运行但内存在另一个节点远端分配延迟会明显偏高。3.2 用 ps 和 top 观察进程状态与优先级ps -eo pid,stat,sched,rtprio,comm --sort-pid | head -20 top -b -n 1 -o %CPU | head -25ps的sched列显示调度策略TS 对应SCHED_OTHERFF 是SCHED_FIFORR 是SCHED_RR。如果看到一个普通进程是 FF 或 RR说明有人用chrt调整过实时优先级一旦这个进程写死循环整个系统的普通进程都可能得不到 CPU。top -b -n 1适合脚本采集-o %CPU让结果按 CPU 使用率排序top 交互界面里按H可以切换到线程视图排查多线程服务时要先做这一步否则看到的是整个进程全部线程的 CPU 合计。3.3 用 free、vmstat、pidstat 拆开内存与切换free -h vmstat 1 5 pidstat -w -I 1 3free -h里不要只看 free 列available 才是“新程序启动时实际还能用多少”的估算值它包含可以回收的缓存。vmstat 1 5每隔 1 秒输出 5 行r 列是运行队列长度b 列是 D 状态进程数cs 列是每秒上下文切换次数si/so 是 swap 换入换出。r 长期大于 CPU 核数要么 CPU 不够要么锁竞争导致线程频繁让出。cs 超过单核每秒数万次时系统可能先出现抖动而不是 CPU 跑满。pidstat -w -I输出每个进程的自愿切换和非自愿切换。非自愿切换高说明线程被抢占或锁等待自愿切换高说明进程本身在大量让出 CPU常见于 IO 等待场景。pidstat属于 sysstat 包没装时用apt install sysstat或yum install sysstat。3.4 用 /proc 直接读内核参数而不是猜grep -E SwapTotal|SwapFree /proc/meminfo sysctl vm.swappiness kernel.pid_max kernel.threads-max/proc下的文件不是普通文件而是内核数据结构的导出接口。比如vm.swappiness控制内存回收时对 swap 的偏好默认通常是 60在内存充足的服务器上我一般调到 10 左右避免页缓存被过早换出。但这不是绝对值如果应用本身就是缓存友好型调低反而浪费内存。kernel.pid_max在很多系统上默认是 32768容器集群遇到 fork 失败或“cannot allocate memory”但内存充足时先检查它。参数常见默认值调节方向vm.swappiness60内存型服务调到 10 左右vm.overcommit_memory0频繁触发 OOM 时调到 2 并配置 overcommit_ratiofs.file-max按内存自动计算高并发连接数不足时上调net.ipv4.ip_local_port_range32768 60999短连接量大时扩大端口范围在 UOS、麒麟这类桌面发行版里控制面板的系统调节工具最终也是改这一层 sysctl。明白参数含义后就不怕调节工具把值改坏判断标准始终是实际指标而不是工具上的推荐档位。4. 从原理到应用系统调用、并发模型和发行版适配的坑操作系统原理不只是考试题它直接决定应用在真实机器上怎么跑。下面从三个最常见的技术触点讲系统调用边界、并发模型的落地、以及换一个发行版后为什么会起不来。4.1 用 strace 观察进程与内核的每一次握手应用无法直接访问磁盘和网卡所有操作都要通过系统调用进入内核。strace可以把这些握手记录下来是排查“应用卡在哪”的最直接手段。strace -tt -f -e traceprocess,file,desc -o syscall.log ./your_app strace -p 12345 -e tracenetwork -f -o net.log-t输出时间戳-tt精确到微秒-f跟踪 fork 出的子进程-e traceprocess,file,desc选择进程、文件、描述符相关的系统调用-o指定输出文件避免污染标准错误。附加到已运行进程用-p PID但需要足够权限。生产环境不要长时间全量跟踪strace 本身会让每次系统调用多一次额外写入性能损耗明显。我一般先抓前几百条看调用顺序或者在阻塞点用-c参数统计各类调用耗时。文件打不开是 ENOENT 还是 EACCES网络超时是 connect 慢还是 write 慢这类问题在 strace 输出里一目了然。4.2 管程、协程和锁并发模型如何落到内核高级语言里的 synchronized 或条件变量底层大多实现为互斥量加条件变量最终通过futex系统调用进入内核。futex的精妙之处在于锁无竞争时完全不需要系统调用只有线程需要睡眠或唤醒时才切换内核态所以锁的代价不是“加锁”本身而是唤醒延迟和缓存一致性开销。pidstat -w -I 1 5 -p $(pgrep -n mysqld)观察 MySQL 这类多线程服务的自愿/非自愿切换如果非自愿切换持续偏高说明线程频繁被抢占或锁冲突严重如果自愿切换高可能是在等 IO。用这个命令比看 CPU 使用率更能反映并发瓶颈。协程则是把调度从内核挪到用户态没有内核栈、不参与 CFS 调度所以在ps和vmstat里都看不出协程数量。这也是协程和线程的一大区别——协程多了不会推高cs列但共享状态竞争依然存在而且更难用系统指标发现。管程解决的是生产者消费者这类同步问题协程解决的是并发规模问题二者不是替代关系。4.3 发行版差异从 Ubuntu 切到麒麟、openEuler、UOS 时真正要改的很多应用在开发机 Ubuntu 上编译正常部署到麒麟或 openEuler 就启动报错。最常见原因是 glibc 版本不一致在较新的 Ubuntu 上编译的动态链接二进制放到旧版 glibc 系统上会提示GLIBC_2.34 not found。判断方法很简单ldd --version | head -1 objdump -T ./your_app | grep GLIBC_ | sort -u | tail -10ldd --version看目标机器上的 glibc 版本objdump -T列出可执行文件依赖的 GLIBC 符号如果某个符号版本高于目标系统就必须换更旧的构建环境、静态编译或用容器打包。Java、Python、Go 这类语言没有 glibc 符号问题但动态库路径和 OpenSSL 版本仍可能不匹配。Qt 项目在麒麟等环境最容易踩 ffmpeg 版本差异开发机上的 so 比系统自带的新直接覆盖库文件会破坏依赖闭包。临时调试用LD_LIBRARY_PATH指定本地目录可以上生产不建议。鸿蒙 PC 的开发预览陆续放出来后同样要先做 ABI 边界评估图形栈和系统服务接口不同直接把 Linux 二进制拿过去并不成立。差异点现象常见处置glibc 版本启动报 version not found换旧构建环境、静态编译、容器打包systemd 单元服务安装成功但启动失败检查单元路径、依赖和权限内核模块签名insmod 失败确认模块与内核版本及签名策略Qt/ffmpeg 依赖界面不正常、解码失败使用发行版自带工具链重新构建5. 验证调度行为的一个命令行小技巧提示下面的脚本只依赖 /proc/stat所有主流发行版都有不需要额外安装性能工具。不想装 perf 时可以用/proc/stat里的计数器验证调度器活动。ctxt行记录系统自启动以来的上下文切换总次数processes行记录累计创建的进程数。两次采样相减就能得到一段时间内的变化量。sample_ctx() { awk /^ctxt / { c$2 } /^processes / { p$2 } END { printf %d %d, c, p } /proc/stat } read c0 p0 $(sample_ctx) sleep 10 read c1 p1 $(sample_ctx) echo 10秒内上下文切换: $((c1-c0))进程创建: $((p1-p0))把sample_ctx定义成函数是为了复用read一次读入两个整数sleep 10 秒后第二次采样。用(c1-c0)/10/nproc换算成每核每秒上下文切换数。如果这个数稳定在每秒 5 万以上且 CPU 占用不高优先怀疑锁竞争或定时器风暴而不是进程数太多。想定位到具体进程时再用内核提供的统计接口grep -E voluntary|nonvoluntary /proc/PID/status先记下本次值触发一次请求后再读两次差就是该进程在这段时间内的切换次数。voluntary_ctxt_switches高说明进程主动让出nonvoluntary_ctxt_switches高说明被抢占或时间片用完。如果安装了 perf可以进一步执行perf stat -e task-clock,context-switches,cpu-migrations -p PID sleep 5cpu-migrations表示进程在 CPU 之间搬家的次数太高时用taskset -pc 0 PID固定核心再对比切换次数是否下降就能量化 CPU 迁移带来的调度代价。本文还有配套的精品资源点击获取