Appearance
题目
假定题 44 给出的计算机 M 采用二级分页虚拟存储管理方式,虚拟地址格式如下:
请针对题 43 的函数 f1 和题 44 中的机器指令代码,回答下列问题:
(1) 函数 f1 的机器指令代码占多少页?
(2) 取第 1 条指令(push ebp)时,若在进行地址变换的过程中需要访问内存中的页目录和页表,则会分别访问它们各自的第几个表项(编号从 0 开始)?
(3) M 的 I/O 采用中断控制方式。若进程 P 在调用 f1 之前通过 scanf() 获取 n 的值,则在执行 scanf() 的过程中,进程 P 的状态会如何变化?CPU 是否会进入内核态?
题 44 信息(依赖前题):函数 f1 的代码段起始虚拟地址为 0040 1000H。
push ebp是 f1 的第一条指令,位于代码段起点。
解析
(1)函数 f1 占多少页?
f1 代码段所有指令的虚拟地址高 20 位都相同——即都落在同一虚页内(页大小 = B = 4 KB,足够装下一般函数的几十条指令)。
→ f1 占 1 页。
编者注(高 20 位相同 = 同一页):
这是页式系统的几何性质——只要两条地址的高 (32 - 12) = 20 位相同,它们必然在同一虚页内。函数体一般几百字节到几 KB,都在 1 页内;除非超大函数(>4 KB)才会跨页。
(2)push ebp 指令的地址变换访问哪几个表项
Step 1:拆解虚地址 0040 1000H
把 0040 1000H 转成二进制:
按 (10, 10, 12) 切:
| 字段 | 二进制 | 十进制 |
|---|---|---|
| 页目录号(高 10) | 0000000001 | 1 |
| 页表索引(中 10) | 0000000001 | 1 |
| 页内偏移(低 12) | 0000 0000 0000 | 0 |
Step 2:查到的表项编号
- 页目录的第 1 个表项(编号从 0 开始)
- 该项指向的页表的第 1 个表项
编者注(编号方向):题面说"编号从 0 开始"——所以页目录号 = 1 对应"第 1 个"表项(不是第 2 个)。这一陷阱常见于"序号 vs 索引"的混淆。
(3)scanf() 执行过程中进程状态变化 + CPU 模式
进程 P 的状态序列
| 阶段 | P 的状态 | 触发事件 |
|---|---|---|
| ① 调用 scanf(),进入系统调用 | 运行态 → 阻塞态 | 缓冲区无数据,需等待键盘输入 |
| ② 用户敲完字符,键盘控制器发中断 | 阻塞态 → 就绪态 | 中断处理程序唤醒 P |
| ③ 调度程序选中 P 执行 | 就绪态 → 运行态 | 取得 CPU |
| ④ scanf() 返回 | 运行态(同 ③ 内) | 继续执行后续代码 |
完整轨迹:运行 → 阻塞 → 就绪 → 运行。
CPU 模式
scanf() 通过系统调用陷入内核 → CPU 从用户态变为内核态。键盘中断处理时也是内核态。整个 I/O 过程多次在用户态和内核态之间切换。
→ CPU 一定会进入内核态(执行系统调用 + 中断处理时)。
编者注(关键概念区分):
进程状态 vs CPU 模式——独立的两个维度(详见 2023-46 编者注)
- 进程状态描述"我现在在不在跑、能不能被调度"
- CPU 模式描述"当前 CPU 上的代码执行权限有多高"
scanf() 调用流程的三个关键转换:
- 调用瞬间:用户态 → 内核态(陷入)
- 等待 I/O:进程从"运行"到"阻塞"
- 中断到来:进程从"阻塞"到"就绪"
- 被调度后:从"就绪"到"运行",并最终从内核态返回到用户态
不要漏掉"就绪"中间态——很多同学把答案写成"运行 → 阻塞 → 运行",跳过了就绪态。P 被唤醒不等于立即执行——必须先排队到就绪、再被调度选中。考研答这种"状态序列"题,三态不能少。
同步阻塞 vs 异步通知——本题的 scanf() 是同步阻塞的典型代表(缓冲区空就 block)。如果改成
select()/epoll()/ 信号驱动,进程状态变化模式会不同——这是 OS I/O 模型章的进阶内容,本题不要求。