欢迎光临我的博客
分类:
2010-07-06 21:53:15
自陷和中断非常相似,它也是通过在总线上发起一个信号来打断当前运行的程序而获得CPU的,只不过该信号是由CPU自身产生的,比如非法指令。另外自陷的优先级都为7,这和中断的最高优先级是一样的,但自陷不可被屏蔽,也就是说,即便设置了PSW的中断屏蔽优先级为7,也不能阻止自陷的产生。
总体上来看,PDP 11/40的自陷可分为程序错自陷、系统调用自陷和调试自陷三大类。程序错自陷是因为指令的某种非法操作,比如访问了一个未被映射的虚拟地址而产生,它一般不是程序所期望的。系统调用自陷是程序执行trap指令产生的,调试自陷通过设置PSW的T位或执行bpt指令而产生,它们一般都用于实现操作系统调用,使得程序进入到内核模式,从而执行内核代码,访问内核资源,它们是程序有意而为的。
从low.s可以看出,自陷向量如图7-1所示:
对于PDP11/40芯片而言,自陷有下面三大类:系统出错自陷、系统调用自陷和调试自陷。而每大类又可能有多种具体的自陷。
系统出错自陷可分为下面几种。
该自陷表示电源供应有异常:对于115V的直流电源,如果电压低于95V,或者对于230V的直流电源、电压低于190V;或交流电的频率不在47Hz~63Hz之间。其自陷向量位于内核地址16处。在电源出错自陷产生后,程序有2ms时间处理,可用来保存数据如寄存器值和外围设备状态等。当电源供应正常后,该自陷又会被触发,同时系统恢复外设到电源异常前的状态。
产生该自陷的原因是程序在奇地址处访问了一个字变量(占用2个字节的变量,如int型),它在程序中并不少见,但随着现代GNU扩展C语言中align关键字的使用,它出现的概率越来越低了。其向量位于内核地址4处。
PDP 11/40提供了SLR(Stack Limit Register)来检测内核栈是否溢出。该寄存器地址位于0o177774,紧邻PSW,格式如下:
15 8 7 0
|
|
|
|
|
|
|
|
保留 |
低8位(bit7~0)未使用,所以可知SLR的值是n * 256(0<=n <=255)。
当系统不管是硬件重启或者被reset指令重启时,该寄存器被清除为0。
在指令执行时,CPU比较SLR的值和栈地址来决定栈是否违例。决定栈违例的规则如下:
(1)黄色区域(yellow zone)= SLR的值+0o340~377;
(2)红色区域(red zone)<= SLR的值+0o337。
如果栈在黄色区域内,则执行完当前指令后再产生自陷,称作黄色栈违例;如果栈在红色区域内,则不执行当前指令,直接产生自陷,称作红色栈违例。栈违例自陷的向量也在内核地址4处。
产生黄色栈违例时,自陷服务函数使用的栈空间如果位于黄色区域内的,则不会再产生黄色栈违例自陷,除非使用的栈空间位于红色区域内,这时会产生红色栈违例自陷。
红色栈违例是致命的栈错误,产生该自陷时,老的PC和PS保存到地址0和2处,再从地址4处取出中断向量执行。
UNIX中并没有设定该寄存器,可见该寄存器值为0,则黄色区域位于0o340~0o377=224~256内,红色区域位于0~223内。而调度进程的栈空间在所有进程的内核栈中位置最“靠前”,所谓“靠前”就是接近低地址,也就是离“警戒区域”(黄色区域和红色区域)最近。因为调度进程的栈空间最早分配,而且其他进程的内核栈所在的分配空间是从调度进程的栈空间后面(高地址)处开始的,所以它们的内核栈地址必然大于调度进程的栈地址。而调度进程的栈空间位于[_end_nb+sizeof(u),_end_nb+1024]处,_end_nb是_end对应的下一个物理块,它大致位于29K处,显然不管怎样,该栈不可能溢出到“警戒区域”。
当主设备同步脉冲信号在总线上持续15微妙(usec)而没有收到从设备脉冲信号时,产生该错误。通常情况下产生该错误的原因是访问了一个不存在的内存地址或外围设备。比如,系统内存只有128K时,在内核模式下,CPU尝试访问位于129K处的物理地址,就会产生该自陷。该自陷产生时,当前指令被中断,程序跳转到中断向量指示的服务函数中执行。其向量也位于内核地址4处。总线超时几乎是程序出错引起的自陷中最常见的类型。
内存是一种电子器件,在其工作过程中难免会出现错误,而这种错误可能会引起致命性的问题。内存错误根据其原因还可分为硬错误和软错误。硬件错误是由于硬件的损害或缺陷造成的,因此数据总是不正确,此类错误是无法纠正的;软错误是随机出现的,例如在内存附近突然出现电子干扰等因素都可能造成内存软错误的发生。为了能检测内存错误,出现了内存“奇偶校验”。内存中数据存储最小的单位是“位”,位只有两种状态,分别以1和0来标示,连续8位构成一个字节。不带奇偶校验的内存每个字节只有8位,而奇偶校验就是在每一字节(8位)之外又增加了一位作为错误检测位,该检测位标识该字节中值为1的位的个数,如果该个数是奇数,那么检测位值为1;如果该个数是偶数,那么检测位值为0。当CPU读取存储的数据时,它会再次把前8位中存储的数据相加,计算结果是否与校验位相一致,从而判断数据是否出错。如果该字节数据中有奇数位的值发生了变化,那么检验位就和计算结果不一致;如果该字节数据中有偶数位的值发生了变化,那么检验位仍然和计算结果一致,这种情况CPU并不能检测出错误。该自陷向量位于内核地址76处。
只在内存管理单元打开,也就是使用虚拟内存时,才可能产生该违例(具体情况请见第2章)。产生该违例的原因有:
(1)所访问的(虚拟)内存页面没有权限。比如试图向一个具有“只读”权限的页面写入数据。
(2)访问的地址超出页面范围
其向量位于内核地址168处。它也是程序中经常容易出现的错误,又称为“段违例”,UNIX下常见的“segmentation fault”就是指它。
在浮点运算时,产生的异常。其向量位于内核地址164处。
在PDP 11/40的指令集中,有些指令是保留的,以供将来使用。如果使用这些指令,就会引起该异常。同时,对某些现有指令的不正确使用也会引起该自陷,比如:jmp r0。所以它又称为非法指令(illegal instructions)错,其向量位于内核地址8处。
系统调用自陷是PDP 11/40为操作系统提供的供程序员访问内核资源的指令。它并不是程序异常。其汇编指令为trap,但是它对应的机器码并不唯一,是35072~35327,从而最多可直接表示256种系统调用。UNIX提供的所有系统调用(或称应用程序接口,API)都通过trap指令实现,其向量位于内核地址28处。
调试自陷有3种:仿真器自陷(emulator trap)、断点自陷(breakpoint trap)和单步自陷(trace trap)。
仿真器自陷(emulation trap)是由EMT指令引起的。它一般用于仿真器的调试,不推荐做其他使用,其向量位于内核地址24处。
断点自陷(breakpoint trap)由BPT指令引起,其向量位于内核地址12处。它通常用于调试器对程序进行调试。当程序员用调试器在代码的某一行设置断点时,调试器把该行指令替换为BPT,从而使程序运行到该处时产生断点自陷(暂停),再在断点自陷服务函数中统计程序的相关信息(比如变量值等),供程序员查看,最后把BPT在替换回原指令。