操作系统开发系列—13.b.进程之丰富中断处理程序
首先打开时钟中断:
out_byte(INT_M_CTLMASK, 0xFE); // Master 8259, OCW1.
out_byte(INT_S_CTLMASK, 0xFF); // Slave 8259, OCW1.
为了让时钟中断可以不停地发生而不是只发生一次,还需要设置EOI:
hwint00: ; Interrupt routine for irq 0 (the clock).
mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259
iretd
运行后发现结果和原来没有任何区别,因为我们只是可以继续接受中断而已,其余并没有做什么。
中断现在已经被打开,于是就存在ring0和ring1之间频繁的切换。两个层级之间的切换包含两方面,一是代码的跳转,还有一个不容忽视的方面,就是堆栈也在切换。

代码如下:
hwint00: ; Interrupt routine for irq 0 (the clock).
sub esp,4
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx inc byte [gs:0] ; 改变屏幕第 0 行, 第 0 列的字符 mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259 lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax pop gs ; `.
pop fs ; |
pop es ; | 恢复原寄存器值
pop ds ; |
popad ; /
add esp,4 iretd
你可以看到,sub/add esp这两句代码实际上是跳过了4字节,结合进程表的定义知道,被跳过的这4字节实际上就是那个retaddr,我们还是先不管这个值。

我们曾经提到过内核栈的问题,如今这个问题真的出现了。现在esp指向的是进程表,如果此时我们要执行复杂的进程调度程序呢?最简单的例子是如果我们想调用一个函数,这时一定会用到堆栈操作,那么我们的进程表立刻会被破坏掉。所以我们需要切换堆栈,将esp指向另外的位置。
hwint00: ; Interrupt routine for irq 0 (the clock).
sub esp, 4
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx mov esp, StackTop ; 切到内核栈 inc byte [gs:0] ; 改变屏幕第 0 行, 第 0 列的字符 mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259 mov esp, [p_proc_ready] ; 离开内核栈 lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax pop gs ; `.
pop fs ; |
pop es ; | 恢复原寄存器值
pop ds ; |
popad ; /
add esp, 4 iretd
切到内核栈和重新将esp切到进程表都很简单,一个mov语句就够了,但是它却非常关键。如果没有这个简单的mov,随着中断例程越来越大,出错的时候,你可能都不知道错在哪里。
在这里我们尽可能地把代码放在使用内核栈的过程中来执行,只留下跳回进程所必需的代码。我们在这里试一下,把这段打印字符的代码替换成使用DispStr这个函数:
extern disp_str [SECTION .data]
clock_int_msg db "^", 0 hwint00: ; Interrupt routine for irq 0 (the clock).
sub esp, 4
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx mov esp, StackTop ; 切到内核栈 inc byte [gs:0] ; 改变屏幕第 0 行, 第 0 列的字符 mov al, EOI ; `. reenable
out INT_M_CTL, al ; / master 8259 push clock_int_msg
call disp_str
add esp, 4 mov esp, [p_proc_ready] ; 离开内核栈 lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax pop gs ; `.
pop fs ; |
pop es ; | 恢复原寄存器值
pop ds ; |
popad ; /
add esp, 4 iretd
运行结果如下,我们看到不断出现的字符“^”,说明函数disp_str运行正常,而且没有影响到中断处理的其他部分以及进程A,之所以在两次字符A的打印中间有多个“^”,是因为我们的进程执行体中加入了delay()函数,在此函数的执行过程中发生了多次中断:


【源码】
操作系统开发系列—13.b.进程之丰富中断处理程序的更多相关文章
- 操作系统开发系列—13.c.进程之中断重入
现在又出现了另外一个的问题,在中断处理过程中是否应该允许下一个中断发生? 让我们修改一下代码,以便让系统可以在时钟中断的处理过程中接受下一个时钟中断.这听起来不是个很好的主意,但是可以借此来做个试验. ...
- 操作系统开发系列—13.a.进程 ●
进程的切换及调度等内容是和保护模式的相关技术紧密相连的,这些代码量可能并不多,但却至关重要. 我们需要一个数据结构记录一个进程的状态,在进程要被挂起的时候,进程信息就被写入这个数据结构,等到进程重新启 ...
- 操作系统开发系列—13.d.多进程 ●
进程此时不仅是在运行而已,它可以随时被中断,可以在中断处理程序完成之后被恢复.进程此时已经有了两种状态:运行和睡眠.我们已经具备了处理多个进程的能力,只需要让其中一个进程处在运行态,其余进程处在睡眠态 ...
- 操作系统开发系列—13.e.三进程
我们再来添加一个任务,首先添加一个进程体: void TestC() { int i = 0x2000; while(1){ disp_str("C"); disp_int(i++ ...
- 操作系统开发系列—13.g.操作系统的系统调用 ●
在我们的操作系统中,已经存在的3个进程是运行在ring1上的,它们已经不能任意地使用某些指令,不能访问某些权限更高的内存区域,但如果一项任务需要这些使用指令或者内存区域时,只能通过系统调用来实现,它是 ...
- 操作系统开发系列—13.i.进程调度 ●
上面的三个进程都是延迟相同的时间,让我们修改一下,尝试让它们延迟不同的时间. void TestA() { int i = 0; while (1) { disp_str("A." ...
- 操作系统开发系列—13.h.延时操作
计数器的工作原理是这样的:它有一个输入频率,在PC上是1193180HZ.在每一个时钟周期(CLK cycle),计数器值会减1,当减到0时,就会触发一个输出.由于计数器是16位的,所以最大值是655 ...
- 微信公众号开发系列-13、基于RDIFramework.NET框架整合微信开发应用效果展示
1.前言 通过前面一系列文章的学习,我们对微信公众号开发已经有了一个比较深入和全面的了解. 微信公众号开发为企业解决那些问题呢? 我们经常看到微信公众号定制开发.微信公众平台定制开发,都不知道这些能给 ...
- 操作系统开发系列—12.f.在内核中添加中断处理 ●
因为CPU只有一个,同一时刻要么是客户进程在运行,要么是操作系统在运行,如果实现进程,需要一种控制权转换机制,这种机制便是中断. 要做的工作有两项:设置8259A和建立IDT. /*========= ...
随机推荐
- Java线程面试题 Top 50 (转载)
转载自:http://www.cnblogs.com/dolphin0520/p/3958019.html 原文链接:http://www.importnew.com/12773.html 本文由 ...
- Django之路由、模板和模型系统
一.路由系统 浏览器会自动给url后加一个"/" django会自动给路由的正则表达式前面加一个"/" django会给任何不带"/"结尾的 ...
- [Tool] 配置文件之Web.config
开发人员工具: 安装完vs后,(如2013:C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts\VS ...
- [译]学习IPython进行交互式计算和数据可视化(五)
第四章:交互式绘图接口 本章我们将展示Python的绘图功能以及如何在IPython中交互式地使用它们. NumPy为处理大量的多维数组结构的数据提供了高效的方法.但是看行行列列的数字总不如直接看曲线 ...
- MVC知识进阶01
下面是在项目中有关于MVC的知识总结,有些地方若不对,请指出. 一:MVC的基本知识 1 名词解释 *惯例优先原则<约定大于配置>:是一种开发设计模式,说是在项目里面使用事先规定的命 ...
- Mysql日期统计函数简介
NOW() 返回当前的日期和时间 CURDATE() 返回当前的日期 CURTIME() 返回当前的时间 DATE() 提取日期或日期/时间表达式的日期部分 EXTRACT() 返回日期/时间按的单独 ...
- 分享一个递归无限级拼接Json的方法---ExtJs的TreePanel和TreeGrid均适用(Ef,Lambda,Linq,IQueryable,List)
话不多说,先上实体类,如果你不是codefirst,就把它当成数据表结构. 下面是底层BaseDal获取数据的方法 (如果你没有Base类,直接写在你的DAL层和BLL层) 下面是BaseServi ...
- iOS阶段学习第六天笔记(数组)
iOS学习( ...
- Debian 7 下安装CodeBlocks12.11
1. 安装编译器 sudo apt-get install build-essential 2. 安装debug工具gdb sudo apt-get install gdb 3. 更新源 sudo n ...
- eclipse新建maven项目(2)
本篇博文是继续之前的博文eclipse新建maven项目(1),那篇博文不在随笔在文章中.首先按照之前那篇博文进行创建maven项目操作,一系列操作下来之后发现刷新项目后会报错: 别急哈,可以解决. ...