3.2 操作系统如何进行权限限制
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
提示:以下是本篇文章正文内容,下面案例可供参考
执行完当前指令后,检测到从CPU内部或者外部发送过来的中断信息,并且可以立即对所接受到的信息进行处理;
中断信息根据来自CPU内部和外部分为:内中断和外中断
(1)终止:断电等
(2)故障:缺页、除法错误、溢出等
(3)自陷:调用int指令引发中断
例:BIOS中以中断处理程序的形式提供了微机中基本I/O设备的控制程序,称为'BIOS中断',应用程序可以用'INT n'指令调用这些程序。
外部中断是实时地处理外部事件的一种内部机制。当某种外部事件发生时,中断系统将迫使CPU暂停正在执行的程序,转而去进行中断事件的处理;中断处理完毕后.又返回被中断的程序处,继续执行下去。
例:鼠标、键盘引起的中断;
通过执行事先编好的某个特定程序,用来处理中断信息的程序。
BIOS中,以中断处理程序的形式提供了计算机中基本I/O设备的控制程序,称为'BIOS中断',应用程序可以用'INT n'指令调用这些程序。
例:'int 0x13h' BIOS提供给程序控制磁盘IO资源;
程序运行的主要软件环境是操作系统。
操作系统为程序运行提供基本的服务,如,程序的装入、结束,内存的分配,文件管理等。以DOS操作系统为例,它也以中断处理程序的形式提供这些服务,称为'DOS中断', 应用程序也用'INT n'指令调用这些服务。
也可称为:操作系统提供系统调用(接口)来供上层应用来和操作系统交互,进而控制系统资源。
例:如计算机应用程序用 “ int 0x80h” 或者 “ int 0x21h”,通过操作系统提供的系统调用来操作计算机资源;
类型号 | 中断处理程序功能 | 类型号 | 中断处理程序功能 |
0 | 除数为0中断 | 1 | 单步中断 |
2 | NMI中断 | 3 | 断点中断 |
4 | 溢出中断 | 5 | 打印屏幕 |
6 | 保留 | 7 | 保留 |
8 | 定时中断( 中断) | 9 | 键盘中断( 中断) |
A | 保留( 中断) | B | 串行口COM2( 中断) |
C | 串行口COM1( 中断) | D | 硬盘中断( 中断) |
E | 软盘中断( 中断) | F | 打印机中断( 中断) |
10 | CRT显示驱动程序 | 11 | 设备检测 |
12 | 存储器容量检测 | 13 | 磁盘I/O驱动程序 |
14 | RS-232 I/O驱动程序 | 15 | 磁带机I/O处理 |
16 | 键盘I/O驱动程序 | 17 | 打印机驱动程序 |
18 | ROM BASIC | 19 | 系统引导 |
1A | BIOS日期、时钟中断 | 1B | BIOS提供的Ctrl_Break处理 |
1C | 定时器时标(54.9ms) | 1D | CRT初始化参数表 |
1E | 磁盘参数 | 1F | 图形字符集 |
20 | 程序结束 | 21 | DOS系统功能调用 |
22 | 结束地址 | 23 | DOS的Ctrl_Break处理 |
24 | 严重错误处理 | 25 | 磁盘顺序读 |
26 | 磁盘顺序写 | 27 | 程序结束且驻留(TSR) |
28~2E | 为DOS保留 | 2F | DOS内部使用 |
30~3F | DOS保留 | 40~7F | 未用 |
80~85 | BASIC保留 | 86~F0 | BASIC使用 |
F0~FF | 未用 | / | / |
通过执行事先编好的某个特定程序,用来处理中断信息的程序。
为防止用户程序随意对操作系统内核区域的访问与操作,操作系统进行权限控制;
操作系统将内存区域分为了用户态和内核态,由于CS:IP是当前的指令,所以使用CS的最低两位来表示: 0是内核态,3是用户态。
每次访问时,目的指令的DPL >= 当前指令的CPL时才能访问,内核段的DPL都是为0(在初始化阶段DPL就都置成了0) 这样内核态可以访问任何数据,用户态不能访问内核数据。通过DPL和CPL就实现了用户态无法直接访问内核态的内容
DPL是目标内存段的特权级; CPL是当前内存段的特权级
操作系统提供了中断指令int 0x80(DOS系统调用)来主动进入内核,这是用户程序发起的调用访问内核代码的唯一方式;
调用系统函数时会通过内联汇编代码插入int 0x80的中断指令,(不仅会插入中断指令,还会将系统调用编号设置给 %eax 寄存器)
核心:内核接收到int 0x80中断后,需要查询IDT表来取出中断处理函数地址,此处不同与其他操作系统区域的代码,int 0x80的idt表中的DPL被设置成了3,所以能从用户态能直接访问int 0x80的中断指令的(唯一入口),然后将CPL修改为0(访管指令),后续就可以正常访问内核代码。
图为 IDT表结构
void sched_init(void) {set_system_gate(0x80, &system_call)} 在linux/include/asm/system.h中 #define set_system_gate(n, addr) \ _set_gate(&idt[n],15,3,addr); //idt是中断向量表基址 #define _set_gate(gate_addr, type, dpl, addr)\ __asm__(“movw %%dx,%%ax\n\t” “movw %0,%%dx\n\t”\ “movl %%eax,%1\n\t” “movl %%edx,%2”:\ :”i”((short)(0x8000+(dpl<<13)+type<<8))),“o”(*(( \ char*)(gate_addr))),”o”(*(4+(char*)(gate_addr))),\ “d”((char*)(addr),”a”(0x00080000))
注:这是一段在系统初始化阶段执行的代码,主要工作是用来设置0x80的中断处理IDT表项,设置相应的&system_call入口函数的地址。并且注意到这里面会将0x80的IDT表项的DPL设置为3,这样CPL=3用户态的程序就能进入,跳入IDT表之后,段选择符是8,将段和偏移设置为新的pc,cs=8,ip=&system_call,这个cs=8 还要再找gdt表,就是从内核0位开始,此时CPl已经变成了0。也就完成了特权级的转化。再下一步,在system_call中会根据%eax中跳转到相应的处理函数执行,这样就完成了一个系统函数的调用过程
Linux的系统调用通过int 80h实现,用系统调用号来区分入口函数。 操作系统实现系统调用的基本过程是:
(1)应用程序如何执行?
通过执行代码序列,配合用户栈完成函数调用和返回(调用函数时将PC地址压栈,当调用结束时,通过弹栈实现返回),实现顺序执行和跳转;
注:代码数据段、用户栈
(2)系统函数在哪里?如何执行?
由于保护系统函数,通过权限集完成;导致用户程序无法执行系统函数;
但是为了用户程序能够执行操作系统提供的功能,OS将部分功能程序通过系统调用的形式交由用户程序执行;(syscall)
通过调用int指令,来执行操作系统或者BIOS提供的函数处理;(中断、异常、函数调用)
但是为了用户程序方便使用,C语言等通过提供库函数,将系统调用函数所需要的的参数等封装,提供给用户程序更简单的函数;
(2) API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
(1)通过封装思想,提供给用户程序更简单的函数调用
(2)内部将使用函数调用,将系统调用需要的参数传入;
比如:系统调用号;
首先,需要明白操作系统也是一个软件,只是操作系统直接操作硬件资源,是一个系统软件;
操作系统通过int指令,主动进行中断处理;但是用户态无法进入操作系统,所以操作系统提供
(3) 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
(4) 系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
(5) 中断处理函数返回到API中;
(6) API将EAX返回给应用程序。
1. 关中断(关中断在内核态)
2. 保存断点(将PC指针压栈)
3. 中断服务程序寻址(根据中断类型号)
4. 保存现场环境(PSW和通用寄存器等)
置屏蔽字
5. 开中断
6. 中断服务
7. 关中断
8. 恢复现场环境(PSW和通用寄存器等)
恢复屏蔽字
9. 开中断
10. 中断返回(将PC指针弹栈)
(1-10 都在内核态执行)
例1:系统调用
1. 调用库函数(将PC指针压栈(函数调用,不同于保存断点),间接调用系统函数,传递系统调用号)
(1 为用户态)
---------------------------------------------------------------------------------------
2. 通过Trap指令,用户态进入内核
3. 保存断点(将用户栈指针、段基址寄存器和偏移地址 压入内核栈)
4. 系统调用程序寻址(根据系统调用号)
5. 保存现场环境 (将PSW、通用寄存器等依次压栈 ---- 用什么压什么)
6. 系统服务
7. 恢复现场环境(通用寄存器、PSW等依次退栈)
8. 中断返回(将段基址寄存器和偏移地址、用户栈指针依次退栈)
(2-8 为核心态)
中断是被动运行,而异常是主动运行;
中断:需要在执行周期结束后检测,可能被屏蔽或者被关中断(这里被关中断其实是在中断处理过程中的关中断,正常指令执行是在用户态,无法关中断);
异常:用户程序运行在用户态,此时应当处于开中断模式,当CPU检测到后立即处理,不存在中断无法响应的情况;
异常和中断运行步骤应当是完全一直,但由于异常是主动执行,所以有些差别;
课本上(尤其计算机组成原理)关于中断处理过程的描述并不全;因为详细过程会牵扯到软件层面(操作系统);所以自从中断响应(中断隐指令)开始,都是从内核态的角度出发,未涉及用户态,以及用户态到核心态的转换;
目前推测出:不可以被中断,但是可以阻塞,主动让出CPU;
例:当执行读文件操作时,当系统函数执行完部分操作后,设置相应操作参数后,主动阻塞让出CPU,系统调用和中断处理程序一样,都会关中断,但是系统调用很多会设计IO指令,自动阻塞,让出CPU;
完成系统调用或中断处理,返回到用户态前为什么会产生一次调度时机?
首先,完成系统调用和中断处理时,此时还未恢复用户程序现场环境;
其次,此时处于指令周期的执行周期末期,如果发生切换中断,是允许发生中断;
最后,而且这样的设计也利于节约资源,此时节省了用户态进入内核态的花销;
联系客服