《返璞归真--UNIX技术内幕》--第6章 中断处理过程

6.4  PDP 11/40的中断类

系统中的中断主要有下面几种。

6.4.1  电传终端接口输入中断

电传终端接口用于链接PDP 11/40的总线和主要的输入、输出终端,当时该终端是电传打字机,这也是UNIX中用ttyteletypewriter)表示进程所使用的终端类型的原因,事实上电传接口还可以连接彩色显示器(CRT)等。输入中断是用户在终端输入字符时所触发的中断,它的优先级为4级。在中断中这是最低优先级,它的入口函数是klin,其服务函数的C语言部分是klrint函数。

6.4.2  电传终端接口输出中断

该中断在字符显示在电传接口所连接的终端时产生,它的优先级也是4级。它的入口函数是klou,其服务函数的C语言部分是klxint函数。

6.4.3  纸带打孔机输入中断

纸带打孔机是老式计算机经常使用的一种输入输出设备。纸带上有没有小孔代笔二进制数字10。这样,打上一系列小孔的纸带就代表了一连串的二进制数。打孔机在读取纸带时,以固定的速率、每隔固定的长度读取二进制数,如果该位置被打上小孔,则认为该值是1(或0,视设计而定),否则认为是相反的数字值。纸带输入中断的优先级是4级,入口函数是pcin,其服务函数的C语言部分是pcrint

6.4.4  纸带打孔机输出中断

和输入相反,输出操作在光滑的纸带上按照需要输出的数据在特定的位置打上小孔。输出中断的优先级也是4级,入口函数是pcou,其服务函数的C语言部分是pcpint

6.4.5  时钟中断

它是系统中优先级最高的中断,它的优先级为6。它又可分为两种:线频时钟中断(Line Clock)和可编程时钟中断(RTC—Real Time Clock)。就功能上而言,它们并无大的不同,所以具有相同的中断入口函数kwlp,中断服务函数的C语言部分是clock

6.4.6  行打印机中断

它只有一个(输出)中断入口:lpou。它的优先级是4,中断服务函数的C语言部分是lpint

6.4.7  磁盘读写中断

每一次磁盘读写操作完成后,都会产生磁盘读写中断。它的入口函数是rkio,其优先级为5,中断服务函数的C语言部分是rkintr


 

6.5  一些常用函

这里介绍一下m40.s中定义的一些常用函数。

6.5.1  特殊指令

mfpi, mtpi指令主要用于当前模式和之前模式之间的数据传输。mfpi从之前模式的指定地址处读取一个字存放到当前模式下的栈上;mtpi则相反,读取当前模式栈顶的字,写入到之前模式下的指定地址处。

6.5.2  fubyte(fuibyte)

函数原型:char fubyte(int addr);

功能描述:从之前模式下addr地址空间处读取1个字节,并返回其值。如果读取过程出错,函数返回-1

参数说明:addr是所要读取的数据所在地址值。

-----------------------选自光盘文件/usr/sys/conf/m40.s-----------------------
1. .globl _fubyte, _subyte
      /* -------------------------
2. .globl _fuibyte, _suibyte
      /* -------------------------
3. .globl _fuword, _suword
      /* -------------------------*/
4. .globl _fuiword, _suiword    
5. _fuibyte:            
6. _fubyte:
7.     mov 2(sp),r1    
8.     bic $1,r1    
9.     jsr pc,gword    
10.     cmp r1,2(sp)
11.     beq 1f
12.     swab r0        
14. 1:
15.     bic $!377,r0    
16.      rts pc


6-7  程序进入fubyte后的栈分布

 

14行声明函数为全局,从而使C语言代码可以调用它们。

在进入fubyte时,栈空间分布如图6-7所示。

《返璞归真--UNIX技术内幕》--第6章  中断处理过程

6-7  程序进入fubyte后的栈分布


7行就是取出参数addr值给r1

由于读字节时的地址addr可能是奇地址,所以第8行清除最低位(位0)取addr所属的偶地址,如果addr是奇地址的话。比如,地址1属于地址0所在的字,地址3属于地址2所在的字,一般地,有地址2n+1属于地址2n所在的字(n>=0)。当然如果addr本身就是偶地址,那么该操作不会对addr值有影响。

9行跳转到gword中运行,gword和其他一些函数定义如下。


1.    gword:                        //get word,从之前模式的地址r1处读取一个字
2.        mov PS,-(sp)
3.        bis $340,PS         //屏蔽所有中断
4.        mov nofault,-(sp)
5.        mov $err,nofault     //设置出错处理函数为err
6.        mfpi (r1)                //从之前模式的地址r1处读取一个字到当前栈顶
7.        mov (sp)+,r0            //把读取的字赋给r0
8.        br 1f            

9.    pword:                        //put word,向之前模式的地址r1处写入一个字
10.        mov PS,-(sp)
11.        bis $340,PS            
12.        mov nofault,-(sp)
13.        mov $err,nofault
14.        mov r0,-(sp)
15.        mtpi (r1)                //把当前栈顶字写到之前模式的地址r1处
16.    1:
17.        mov (sp)+,nofault
18.        mov (sp)+,PS
19.        rts pc

20.    err:
21.        mov (sp)+,nofault    //恢复全局变量nofault的值
22.        mov (sp)+,PS            //恢复PSW的值
23.        tst (sp)+            
24.        mov $-1,r0            //设置返回值为-1
25.        rts pc        


2行保存PS,为下一行关中断做准备。第3行关闭7级中断,事实上目前系统中优先级最高的中断是6级,这样做为安全起见,而且为将来系统的变化而提供扩展性。

4行保存nofault值到栈。

5行赋nofault值为错误处理函数err的地址。

6行从r1指向的之前模式下的地址空间读取一个字,并存放到当前栈上。这个读取过程有可能成功,也有可能会失败。

—     如果读取成功,那么第7行把读出的字节值存到寄存器r0中,跳转到第23行。第24行恢复nofault的值,第25行恢复PS值为调用gword之前的值,第26行返回到调用者,这里是fubyte,则继续fubyte中第10行执行。第10行比较r1和实际参数addr,这是因为r1addr所属字的偶地址,如果addr是偶地址,那么r1=addr,否则r1addrr0是从地址r1处读出的一个字的值,所以如果r1=addr,那么所需要读取的字节值就是r0的低字节,因此直接跳转到第14行清除r0的高字节;如果r1addr,那么所需要读取的字节值就是r0的高字节,因此第12行交换r0的高低字节。最后在第15行返回到调用者,返回值是r0

—     如果读取失败,那么大多数情况是由于r1指向的地址不存在而产生总线超时自陷,或者由于该地址不在当前页内而产生内存管理违例自陷,不管产生何种自陷,程序都将被打断而跳转到trap函数中运行。根据第7章的讲述,我们知道trap函数将返回到nofault中保存的错误处理函数在这里也就是在err函数中运行。这时栈分布如图6-8所示:

《返璞归真--UNIX技术内幕》--第6章  中断处理过程
6-8  mfpi执行出错后的栈分布


再来看看err函数。

2122行恢复nofaultPS的值,第23行增加栈指针,使其指向“返回到fubyte的调用者PC”。第24行设置r0(返回值)为-1,最后在第25行直接返回到fubyte的调用者。这一段处理过程有些复杂,其流程图如图6-9所示:

《返璞归真--UNIX技术内幕》--第6章  中断处理过程


6-9  fubyte调用出错的指令执行流程

至此我们可以明白在main函数中,为何当fuibyte()返回-1时,表示该地址不存在。

6.5.3  fuword(fuiword)

函数原型:int fuword(int addr);

功能描述:从之前模式下addr地址处读取1个字,并返回其值。如果读取出错,则返回-1

参数说明:addr是所要读取的数据所在地址值。


-----------------------选自光盘文件/usr/sys/conf/m40.s-----------------------
1. _fuiword:
2. _fuword:
3.     mov 2(sp),r1
4. fuword:            
5.      jsr pc,gword
6.      rts pc

该函数的实现过程和fubyte基本一致,但由于是读取整个字的值,因此流程更简单。这里有点疑惑的地方是第4行的声明,似乎没有什么意义。

6.5.4  subyte(suibyte)

函数原型:int subyte(int addr,  char ch);

功能描述:向之前模式下addr地址处写入一个字节的值ch。函数返回0表示写入成功,-1表示写入失败。

参数说明:addr是所要写入的地址值。

ch是所要写入的值。

------------------------选自光盘文件/usr/sys/conf/m40.s----------------------
1.    _suibyte:
2.    _subyte:
3.        mov 2(sp),r1
4.        bic $1,r1
5.        jsr pc,gword
6.        mov r0,-(sp)
7.        cmp r1,4(sp)
8.        beq 1f
9.        movb 6(sp),1(sp)
10.        br 2f
11.    1:
12.        movb 6(sp),(sp)
13.    2:
14.        mov (sp)+,r0
15.        jsr pc,pword
16.        clr r0
17.        rts pc

进入subyte后的栈分布如图6-10所示:

3行赋addrr1。第4行同样清除r1最低位,使其等于addr所属字的偶地址,然后调用gword读出该地址处的字值。之所以这样做,是因为用mtpi指令向之前模式的地址空间写入数据时,只能单个字的写入,所以需要先读出整个字的值,再把需要写入的字节部分改成ch,然后再写入修改后的字。

《返璞归真--UNIX技术内幕》--第6章  中断处理过程

6-10  进入subyte后的栈分布图

上一章   进程管理和调度                                      目录                                   下一章   自陷

本书在全国各大书店均有销售!


上一篇:在Solaris下如何在程序中获得当前调用栈信息(函数名等)


下一篇:在Linux下如何在程序中获得当前调用栈信息