Page 470 - 《软件学报》2025年第5期
P. 470
2370 软件学报 2025 年第 36 卷第 5 期
20 reg_bank_swap(bank->start_window, bank->context, NULL);
21 }
22 list_append(&bank_in->reg_bank_node, &meta->activated_bank_queue);
23 bank_in->cpuid = current_cpu_id;
24 bank_in->active = true;
25 meta->activated_reg_bank[bank_in->start_window] = bank_in;
26 update_regs(bank); // Update PSR and WIM (system registers).
27 }
SPARC 架构下, 即使内核也无法以全局索引来访问所有通用寄存器, 只能访问当前寄存器窗口内的寄存器和
全局寄存器, 而当前的寄存器窗口由处理器状态寄存器 PSR 中的 CWP 位决定. 此外, SPARC 处理器还是用 WIM
寄存器标记了当前无效的寄存器窗口, 如果 CWP 指示的当前窗口与 WIM 标记的无效窗口一致, 那么就会触发
window_overflow 或 window_underflow 这两种系统陷阱 (trap). 图 4 中, CWP 指向了当前线程寄存器组中的某个
窗口, 当该线程运行时, WIM 中与该线程寄存器组对应的窗口被标记为有效, 其余窗口无效. 为了实现寄存器组机
制, 我们需要内核能够对所有寄存器窗口进行管理和分配, 因此在内核需要控制寄存器窗口时, 需要将 WIM 寄存
器清空, 使内核暂时获得对所有寄存器窗口的控制权限, 此时内核就能够通过修改 PSR 中的 CWP 来访问任意寄
存器窗口, 在内核需要做的操作完成后, 会将 CWP 和 WIM 的值恢复, 以跳转回内核原来所处的寄存器窗口, 继续
完成其他操作. 比如, 在需要恢复某个线程的上下文时, 内核会找到可以容纳该线程上下文的寄存器窗口, 然后跳
转到该窗口并将占据该窗口的线程上下文 (如果有的话) 保存到内存中, 之后再将需要恢复的线程上下文写入寄
存器中. 如果这一过程涉及多个窗口的保存或恢复, 则内核还需要相应地在寄存器窗口间进行滑动, 由于 WIM 已
被清零, 所以这种滑动不会导致任何陷阱发生, 能够顺利完成.
为了保证微内核下不同进程之间的隔离性和可靠性, 在实现上述寄存器组机制时还需要注意以下问题. 首先,
在交换寄存器组时必须将寄存器窗口中所有的寄存器全部覆写, 以保证换入的线程无法访问被换出线程存留在寄
存器中的数据. 此外, 在退出内核进入用户态时, 需要对 WIM 寄存器进行修改, 禁止应用程序访问其寄存器组之
外的其他寄存器窗口, 以防止某个线程读取其他同时位于寄存器中的属于其他进程的上下文. 当用户态应用程序
试图使用 SAVE 或 RESTORE 指令滑动到其寄存器组之外的寄存器窗口时, 由于这些寄存器窗口在 WIM 中被标
记为无效, 因此会触发 window_overflow 或 window_underflow 陷阱, 由内核进行进一步处理, 从而能够阻止该线
程的越界访问.
出于性能考虑, 我们为系统的内核单独分配了一个寄存器组, 包含一个寄存器窗口, 并且将该寄存器组固定在
真实寄存器中, 如图 4 所示. 这样应用程序在进入内核后, 就可以直接跳转到内核对应的寄存器组中, 从而不需
要再为内核分配额外的空闲寄存器窗口, 也不需要保存当前线程的上下文. 但这样的设计会减少可用的空闲寄存
器窗口的数量, 进而导致可以同时在寄存器中存在的线程数量减少, 因此可能会使得对线程上下文的交换更加频
繁. 但微内核系统下, 用户态应用程序经常需要进出内核, 我们认为对进出内核时上下文的切换进行专门优化是值
得的.
实现寄存器组机制时的主要挑战是, 各寄存器组使用的寄存器窗口是不能直接相邻的, 这是因为 SPARC 架
构中相邻的寄存器窗口之间有 8 个寄存器是共享的, 如果寄存器组所使用的寄存器窗口相邻的话, 那么不同线程
间就会出现共享的寄存器, 这会导致线程之间相互影响, 也难以保证隔离性. 另一个重要原因是, 当应用程序触发
陷阱进入内核时, 会自动滑动至下一个寄存器窗口用于进行陷阱处理, 如果寄存器组直接相邻, 那么在陷阱处理时
就有可能进入其他线程正在使用的寄存器窗口中. 因此, 每个寄存器组都需要包含一个额外的“冗余”寄存器窗口
用于与其他寄存器组进行分隔, 如图 5 所示. 如果一个寄存器组包含 3 个寄存器窗口, 那么其能够完全使用的寄存
器只有前两个寄存器窗口中的所有寄存器, 第 3 个寄存器窗口将作为“冗余”窗口. 这样看似会导致一整个寄存器
窗口的浪费, 但实际上仅会浪费 8 个寄存器. 这是因为冗余窗口中的 8 个 in 寄存器与上一个寄存器窗口共享, 被