操作系统第二次实验

问题1:为什么用户程序不能直接调用内核函数,而要通过 int 0x80 这种特殊的方式?

用户态与内核态的权限等级不同,用户程序运行在用户态(Ring3),内核函数运行在内核态(Ring0),不能允许低权限态直接执行高权限态的敏感操作,不然任何应用都能改中断,关时钟,访问任意内存,系统将失去安全性与稳定性。

对于此框架,系统调用门被放在IDT的0x80,在这里允许用户态进入内核。

问题2:当用户程序执行 int 0x80 触发系统调用时,CPU 自动将哪些内容压入栈中?内核处理完成后通过 iret 返回,返回时栈帧是如何变化的?

先将返回现场压到内核栈(eip,cs,eflags,esp_user,ss

_user),再继续压栈段寄存器与通用寄存器,返回时按照这个进行逆序恢复。

问题3:在 syscallWrite 函数中,为什么需要通过 getSegBase(tf->ds) 获取用户段的基址,将用户态的虚拟地址转换为物理地址后才能访问?

内存具有分段机制,用户段与内核段是隔离的,对应地址计算方式有差异,如果不做转换,内核会把用户偏移当成内核线性地址去读,可能读错内容或者是访问非法/敏感区域,导致错误甚至破坏隔离。

问题4:app被kernel加载到内存后,为什么不能直接跳转执行?框架代码此处是如何处理的,请详细描述。

原因:

(1)直接跳转将使得在当前特权级继续执行,应用也将处在特权级执行,将破坏用户态隔离

(2)用户程序链接在自己的段语义下,在特权级执行将造成地址解释错误。

处理流程:

(1)把 app.bin 读到物理 APP_BASE

(2)调用 enterUserSpace

(3)enterUserSpace 调用 switch_to_user,传入用户 DS/CS/ESP

(4)switch_to_user 手工构造 iret 栈帧 SS, ESP, EFLAGS, CS, EIP,再执行 iret 完成 CPL 从 0 到 3 的切换