操作系统课程设计指导书2015(3)

2019-04-16 22:41

void f1(void); int i; main() { i=0; f1(); i=1;返址1 }函数调用进入被调函数函数返回 f1() { i=2; return; } 图2-2 函数调用及返回图

在执行f1()函数的调用指令前的当前堆栈的情况如图2-3a所示。在执行函数调用指令时,系统首先将main()中函数调用语句的下一条指令即“i=1;”的地址(在CS:IP中,这里用“返址1”表示)压入堆栈,此时堆栈内容如图2-3b所示。然后将f1()函数的入口地址装入CS和IP 寄存器,控制就从main()转向f1()。当执行f1()的最后一条指令“return”(函数返回指令)时,系统将前面保存在堆栈中的返址1的偏移和返址1的段址弹出并送到IP和CS中,则控制就从f1()返回到main()了。

返址1的偏移sp调用f1()前的栈顶返址1的段址?...sp调用f1()后的栈顶?...sp从f1()返回后的栈顶?...图a 调用f1()前的栈顶图b 调用f1()后的栈顶图c 从f1()返回后的栈顶 图2-3 函数调用前后堆栈内容的变化

2.中断处理时的控制转移情况

除了函数调用以外,中断也能实现控制的转移。当中断发生时,系统首先将标志寄存器Flags的值压入堆栈,然后将装有被中断程序下一条指令地址的CS和IP寄存器的内容也分别压入堆栈,再从中断向量中获取中断服务程序的入口地址并将它们装入CS和IP寄存器,这样,控制就从被中断的程序转向中断服务程序。中断返回时,系统自动从栈顶弹出返址1的偏移、返址1的段址和Flags并送到IP、CS和Flags寄存器中,CPU就开始继续从断点处执行被中断程序。中断处理前后堆栈内容的变化情况如图2-4所示:

- 11 -

sp返址1的偏移进入中断处返址1的段址理时的栈顶Flags?...图b 进入中断处理时的栈顶?...?...sp中断处理前的栈顶sp中断处理返回后的栈顶图a 中断处理前的栈顶图c 中断处理返回后的栈顶 图2-4 中断处理前后堆栈内容的变化

3. Interrupt类型函数的特殊作用

在Turbo C中提供了一个特殊的函数类型说明符interrupt,我们可利用它将一个函数申明为中断处理函数。例如我们写了一个文件名为“aaa.c”的C程序,其内容如下: void interrupt fun(void); int i; main( )

{ i=0; fun(); i=1; }

void interrupt fun(void) { i=2; }

用编译命令“tcc -S aaa”得到以上C程序的汇编码: ifndef ??version ?debug macro endm endif ?debug S \

_TEXT segment byte public 'CODE' DGROUP group _DATA,_BSS assume cs:_TEXT,ds:DGROUP,ss:DGROUP _TEXT ends

_DATA segment word public 'DATA' d@ label byte d@w label word _DATA ends _BSS segment word public 'BSS' b@ label byte

- 12 -

b@w label word ?debug C E93B4F151F056161612E63 _BSS ends

_TEXT segment byte public 'CODE' ; ?debug L 3 _main proc near ; ?debug L 4 mov word ptr DGROUP:_i,0 ; ?debug L 5 pushf call far ptr _fun ; ?debug L 6 mov word ptr DGROUP:_i,1 @1: ; ?debug L 7 ret

_main endp ; ?debug L 8 _fun proc far push ax push bx push cx push dx push es push ds push si push di push bp mov bp,DGROUP mov ds,bp ; ?debug L 10 mov word ptr DGROUP:_i,2 @2: ; ?debug L 11 pop bp pop di pop si pop ds pop es pop dx pop cx pop bx pop ax iret

- 13 -

_fun endp _TEXT ends _BSS segment word public 'BSS' _i label word db 2 dup (?) _BSS ends ?debug C E9

_DATA segment word public 'DATA' s@ label byte _DATA ends

_TEXT segment byte public 'CODE' _TEXT ends public _main public _fun public _i end

从编译后的代码中可以看出,对于fun()函数,由于定义的类型是interrupt类型的中断处理函数,所以在使用tcc命令进行编译时,编译器将自动在fun()的开始加入一组push操作(代码中第二组黑体字部分),以保存被中断程序的CPU现场环境信息;相对应地在fun()的代码最后自动加入一组pop操作(代码中第三组黑体字部分),以便中断返回时恢复被中断程序的现场环境信息。

从编译后的代码段中还可以看出,main()对fun()的调用是通过: pushf call far ptr _fun /*代码中的第一组黑体字部分 */ 实现的,即先压入Flags寄存器的内容,再用call指令压入返回地址,并转去执行fun()函数。 而在进入fun()时,系统首先将执行一组push操作,将AX、BX、CX、DX、ES、DS、SI、DI、BP 的值保存到堆栈中,再用fun()的数据段装配DS寄存器,然后才执行fun()中的具体语句“i=2;”。而在返回前,先要执行一组pop操作,从堆栈中恢复BP、DI、SI、DS、ES、DX、CX、BX、AX 的值,然后再用iret指令(而不是ret指令)返回,iret指令将从堆栈中弹出返址的偏移、返址的段址、flags到CPU的IP 、CS和Flags寄存器中,从而使CPU继续从断点处执行被中断程序main()。fun()调用前后的堆栈内容如图2-5所示,图中的“返址1”是指main()函数中fun()函数调用指令的下一条指令“i=1”的地址。

- 14 -

sp返址1的偏移调用fun()返址1的段址时的栈顶sp调用fun()前的栈顶Flags?...图b 调用fun()时的栈顶BPDISIDSESDXCXBXAX返址1的偏移返址1的段址Flags?...sp fun()执行push操作后的栈顶?...图a 调用fun()前的栈顶图c fun()执行push操作后 的栈顶sp返址1的偏移 fun()执行pop操作后返址1的段址的栈顶Flags?...图d fun()执行pop操作后的栈顶?...sp从fun()返回后的栈顶图e 从fun()返回后的栈顶 图2-5 fun()调用前后的堆栈内容的变化

4. 利用堆栈切换实现CPU切换

从上面的描述可知,我们可以用函数或中断服务子程序来处理CPU的切换,但关键仍在于堆栈的切换。例如, 系统中有两个线程:线程1和线程2,它们的私有堆栈分别为 stack1 和stack2;线程1目前拥有CPU,即线程1正在执行,则系统的现行堆栈为线程1的私有堆栈stack1;在线程1中调用一函数F(),函数的返回地址即线程1的下一条指令的地址将压入到现行堆栈stack1中;若在 F() 中,将系统的现行堆栈从stack1切换到stack2:保存现行堆栈的段址SS和栈顶指针SP到变量ss1、sp1中,并将线程2的堆栈stack2的段址和栈顶指针装到CPU的SS与SP寄存器中;如果stack2的栈顶有线程2的下一条指令的地址,则从F()中返回时,将用现行堆栈stack2 的栈顶的字装配IP和CS,CPU就开始执行新线程,即线程2。若再用保存在变量ss1、sp1 中的内容来装配SS和SP寄存器,即将现行堆栈切换回线程1的私有堆栈,则CPU 将被分配给线程1,它将从原来的断点继续往下执行。考虑到CPU 切换时要进行现场保护,用中断服务子程序来实现CPU的切换就显得更方便。 下面我们用interrupt类型的函数swtch()来实现CPU在线程1和线程2间的切换,另外,必须注意的是,在堆栈切换过程中一定要做到操作的原子性,这一点我们可以通过关中断、

- 15 -


操作系统课程设计指导书2015(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:计算机网络课后习题答案(仅供参考)

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: