在保护模式下,处理器中的“阶级”不仅体现在数据和代码的访问,还体现在指令中。
一方面将指令分级的原因是,有些指令的执行对计算机有着严重的影响,它们只有在0特权级下被执行,因此被称为特权指令(Privilege Instruction)。比如hlt指令,它可以让计算机停机,处理器只信任操作系统,所以它不得不放在0特权级下。同类的指令还有lgdt,lidt,ltr,popf等,这些对计算机的正常运行起着非同小可的影响,操作系统只有亲自执行它们才放心。
另一方面体现在I/O读写控制上。IO读写特权是由标志寄存器eflags中的IOPL位和TSS中的IO位图决定的,它们用来指定执行IO操作的最小特权级。IO相关的指令只有在当前特权级大于等于IOPL时才能执行,所以它们称为IO敏感指令(I/O Sensitive Instruction),如果当前特权级小于IOPL时执行这些指令会引发处理器异常。这类指令有in、out、cli、sti。所以你懂的,不止是操作系统可以进行IO端口访问,用户进程也是可以的,只是操作系统不允许用户进程这么做。
平时我们被灌输的思想是用户进程无法直接访问硬件,必须要向操作系统求助,只有高高在上的操作系统才有能力访问外设。操作系统的职责就是管理计算机中的资源,资源包括软件和硬件,不允许用户进程直接操作外设,这只是操作系统的一种管理策略,因为这是出于对计算机的保护,谁能保证用户程序个个都那么善良可靠呢,万一用户程序非法使用硬件,这种破坏可是难以估量呢,保护计算机安全是操作系统的责任,不应该让不受信任的程序有破坏计算机的可能。
我们在很久以前就介绍过eflags寄存器啦,现在来查看下eflags寄存器的IOPL位,如图
在eflags寄存器中第12~13位便是IOPL(I/O Privilege Level),即IO特权级,它除了限制当前任务进行IO敏感指令的最低特权级外,还用来决定任务是否允许操作所有的IO端口,对,没错,是全部IO端口,IOPL位是打开所有IO端口的开关(用来单独设置端口访问的方式是IO位图,一会介绍)。每个任务(内核进程或用户进程)都有自己的eflags寄存器,所以每个任务都有自己的IOPL,它表示当前任务的要想执行全部IO指令的最低特权级,也就是处理器最低的CPL,只有任务的当前特权级大于等于IOPL才允许执行全部IO指令,即数值上CPL <= IOPL。
CPL为0时处理器是法力无边的,所以0特权级下处理器是不受IO限制的。IOPL如何设置呢?下节再说。