编写实模式多任务操作系统模型之(5)
来源:互联网 发布:ftp空间域名 编辑:程序博客网 时间:2024/05/10 05:28
5.切换到下一将被调度运行task2的进程堆栈空间。由于在进程堆栈保存有进程的断点信息,所以找到进程堆栈的顶位置便可转入到该进程运行。scheduler对于各进程的调度采用轮转式的方式,即按照task1 --> task2 --> task3的顺序,而后再从task1 开始依次循环,使各个进程得以运行。所以将current_task的值增1,再判断current_task是否超过最大进程数MAXTASKS,若超过,则使其复位,从0开始重新计数即可。取得合适的current_task值后,按上面步骤的公式便可找到将被调度进程的堆栈指针的存放地址,从中取出值送到SP寄存器即可。如下段程序代码所示:
6. 恢复断点信息,使下一被调度进程从断点处运行。
其它辅助程序
系统中存在3个被调度的程序,分别为task1、task2、task3。由于这是一个实验程序,所以它们的功能较为单一,主要是将自身的一个16位计数器进行循环计数,而后通过直接写屏方式在显示器上显示出来。由于现行机器的运行速度较快,为使用户能看清计数器的变化过程,在各个进程的执行过程中加入了人为的延时控制。每一个进程都是一个无限的循环过程,只要调度程序在运行,各个进程就会无限运行。
此外系统中还有几个辅助程序:
◆ 16进制数显示子程序printhex
这个子程序主要是为系统内的3个演示进程服务,它可以将从堆栈传递过来的一个16位的数以16进制形式显示到屏幕的指定位置,通过直接写屏方式在显示器上显示出来。在VGA的 80X25 彩色文本显示模式下,一个字符在显示缓冲区上占2个字节的空间,其中所显示字符的ASCⅡ码存于低地址单元中,字符的显示属性(前景、背景、高亮和闪烁等)存于高地址单元中,如果将字符及其显示属性存储位置弄错,将会产生花屏的现象。Printhex根据显示位置参数计算出所需显示的准确位置,避免产生花屏的现象。调用者只需提供合法的显示位置即可,行号应在1~25之间,列号在1~80之间。
◆ 键盘中断子程序keybd
它是一个基本的内核级键盘中断处理程序,显示键盘的ASCⅡ码到屏幕上。它不受调度进程的控制,只要有键盘中断发生,该中断服务程序就会运行。在该程序被触发运行时,从60H端口读入键盘的扫描码。扫描码最高位的状态标志着其是键入码还是释放码,如为释放码则不进行处理,直接返回;若为按键的压入码,则以键入码作为索引在键盘扫描码/ASCⅡ码对照表查得对应的ASCⅡ码,通过int 10h功能调用将其输出到显示器上。在键盘中断返回前,要向键盘内的单片机发复位信号, 先将61h端口的最高位置1,而后再置0,这样键盘才不会死锁。
kernel.asm完整代码及注释如下:
inc ax ; 取得下一将被调度进行的进程号 cmp ax, MAXTASKS ; 该进程号是否超过最大进程数 jb move_on ; 若没超过,跳到 move_on 继续 xor ax, ax ; 超过最大进程数,清0,从第一个进程开始调度move_on:mov [current_task], ax;将更改后进程号送入current_task单元保存,按公式进程堆栈指针的存放地址=SPtable+2*进程号 取得进程堆栈指针 mov bx, ax shl bx, 1 lea si, [SPtable] mov SP, [ds:si + bx] ; 将进程堆栈指针送到SP,切换到进程堆栈空间
6. 恢复断点信息,使下一被调度进程从断点处运行。
pop ds pop es popa ;恢复所有被保存的CPU内寄存器值iret ;激活被调度进程
其它辅助程序
系统中存在3个被调度的程序,分别为task1、task2、task3。由于这是一个实验程序,所以它们的功能较为单一,主要是将自身的一个16位计数器进行循环计数,而后通过直接写屏方式在显示器上显示出来。由于现行机器的运行速度较快,为使用户能看清计数器的变化过程,在各个进程的执行过程中加入了人为的延时控制。每一个进程都是一个无限的循环过程,只要调度程序在运行,各个进程就会无限运行。
此外系统中还有几个辅助程序:
◆ 16进制数显示子程序printhex
这个子程序主要是为系统内的3个演示进程服务,它可以将从堆栈传递过来的一个16位的数以16进制形式显示到屏幕的指定位置,通过直接写屏方式在显示器上显示出来。在VGA的 80X25 彩色文本显示模式下,一个字符在显示缓冲区上占2个字节的空间,其中所显示字符的ASCⅡ码存于低地址单元中,字符的显示属性(前景、背景、高亮和闪烁等)存于高地址单元中,如果将字符及其显示属性存储位置弄错,将会产生花屏的现象。Printhex根据显示位置参数计算出所需显示的准确位置,避免产生花屏的现象。调用者只需提供合法的显示位置即可,行号应在1~25之间,列号在1~80之间。
◆ 键盘中断子程序keybd
它是一个基本的内核级键盘中断处理程序,显示键盘的ASCⅡ码到屏幕上。它不受调度进程的控制,只要有键盘中断发生,该中断服务程序就会运行。在该程序被触发运行时,从60H端口读入键盘的扫描码。扫描码最高位的状态标志着其是键入码还是释放码,如为释放码则不进行处理,直接返回;若为按键的压入码,则以键入码作为索引在键盘扫描码/ASCⅡ码对照表查得对应的ASCⅡ码,通过int 10h功能调用将其输出到显示器上。在键盘中断返回前,要向键盘内的单片机发复位信号, 先将61h端口的最高位置1,而后再置0,这样键盘才不会死锁。
kernel.asm完整代码及注释如下:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 内核程序kernel.asm; 在系统时钟中断的驱动下,依次使系统内的3个进程轮流占用CPU完成各; 自的工作:在屏幕上指定位置循环显示自己的计数器。具有内核级键盘中; 断,显示出键盘ASCII码;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;[BITS 16][ORG 0x00]STACKSIZE equ 0x400 ; 各进程的堆栈空间大小为1k个字节MAXTASKS equ 3 ; 最大进程数 VIDEORAM equ 0xb800 ; 显示缓存段地址 KERNELSEG equ 0x8000 ; kernel 程序段地址 ;------------------------------------------------------------------start: mov ax,KERNELSEGmov ds, ax ; 数据段,堆栈段寄存器的mov ss, ax ; 值均设为 KERNELSEGmov sp, 0xFFFF ; kernel 堆栈指针=0xFFFF mov dx,0x3f2mov al,0x0cout dx,al ;关闭软驱马达mov ax, 0x0003 int 0x10 ; 清屏 mov dx, 0x0600 ; 进程的堆栈基地址 add dx, STACKSIZE ; 进程1的堆栈指针= 0xA00mov ax, task1 ; 进程1入口地址 call taskinit ; 初始化进程1add dx, STACKSIZE ; 进程2的堆栈指针= 0xD00mov ax, task2 ; 进程2入口地址 call taskinit ; 初始化进程2add dx, STACKSIZE ; 进程3的堆栈指针= 0x1000mov ax, task3 ; 进程3入口地址 call taskinit ; 初始化进程3; 重新设置系统定时器的中断向量cli ;关中断,以免在设置中断向量发生意外 mov ax, KERNELSEG push ds ;保存ds值 mov bx,0 mov ds,bx ;ds=0mov word [ds:0x08*4], scheduler ; 定时器中断向量偏移送到0:20~0:21mov word [ds:0x08*4+2], ax ; 定时器中断向量段地址送到0:22~0:23 ; 重新设置键盘的中断向量 mov word [ds:0x09*4], keybd ; 键盘的中断向量偏移送到0:24~0:25mov word [ds:0x09*4+2], ax ; 键盘的中断向量段地址 0:26~0:27pop ds ; 恢复ds值sti ; 开中断,允许中断进入jmp $ ; kernel在此等待时钟中断的到来 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 被调度进程task1 ; 功能:对于自身的计数器进行计数,而后在屏幕的3行1显示输出;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;task1:.l1 dec word [task1ctra] ; 产生短时间延迟,以免计数显示过快 jnz .l1 inc word [task1ctr] ; 计数器增1 push word 3 ; 输出行号 push word 0 ; 输出列号 push word [task1ctr] ; 将参数通过堆栈进行传递 call printhex ; 调用16进制显示子程序jmp task1 ; 循环显示;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 被调度进程task2 ; 功能:对于自身的计数器进行计数,而后在屏幕的6行1显示输出;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;task2: .l1 dec word [task2ctra] jnz .l1 inc word [task2ctr] push word 6 push word 0 push word [task2ctr] call printhexjmp task2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 被调度进程task3; 功能:对于自身的计数器进行计数,而后在屏幕的9行1显示输出;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;task3: .l1 dec word [task3ctra] jnz .l1 inc word [task3ctr] push word 9 push word 0 push word [task3ctr] call printhexjmp task3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;任务初始化子程序taskinit;功能: 在被初始化进程的堆栈空间中压入Flag寄存器,进程的入口; 地址,及要被保护的CPU寄存器值,同时将进程的栈顶地址; 写入sptable数组中。;参数: bx= 进程的堆栈指针; ax= 进程的入口地址偏移;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;taskinit: pusha push espush ds ; 将现场保护到在kernel的的堆栈空间中。 mov bp, sp ; 保存kernel的堆栈指针。 mov sp, dx ; 切换到进程的堆栈空间中 pushf ; 在进程的堆栈空间中压入标志寄存器Flag push cs ; 在进程的堆栈空间中压入代码段值 push ax ; 在进程的堆栈空间中压入进程的入口地址偏移 pusha push es push ds ; 在进程的堆栈空间中保护CPU寄存器值 ; 按公式:进程堆栈指针的存放地址=sptable+2*进程号 ; 得到进程堆栈指针的存放地址,将进程堆栈指针的值存 ; 到该内存单元中, mov dx, sp mov bx, [taskcount] shl bx, 1 lea si, [sptable] mov [ds:si + bx], dx inc word [taskcount] ; 将进程号增1,指向下一个将被初始化的进程 mov sp, bp ; SP恢复为kernel的堆栈指针 pop ds pop es popa ; 恢复现场 ret ; 返回到kernel ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;进程调度子程序scheduler ;功能:1。保存被剥夺运行权的进程的堆栈指针 ; 2。恢复下一个将被调度运行的进程的堆栈指针 ; 3。从将被调度运行的进程的堆栈中恢复断点信息, ; 使CPU转到该进程去执行 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; scheduler: pusha push es push ds ; 在当前进程的堆栈中保存断点处的CPU寄存器值 mov al, 0x20out 0x20, al ; 给中断控制器8259送EOI指令pushfcall 0xF000:0xFEA5 ; 使原系统时钟中断处理运行mov ax, [current_task]mov bx, axshl bx, 1lea si, [sptable]mov [ds:si + bx], sp ; 保存将被剥夺执行权的进程的堆栈指针inc ax ; 取得下一将被调度的进程的进程号cmp ax, MAXTASKS ; 该进程号是否超过最大进程数jb move_on ; 若没超过,跳到 move_on 继续xor ax, ax ; 超过最大进程数,清0,从第一个进程开始调度move_on:mov [current_task], ax ; 将更改后进程号的送入current_task单元保存 ;按公式 进程堆栈指针的存放地址=sptable+2*进程号 取得进程堆栈指针 mov bx, ax shl bx, 1 lea si, [sptable] mov sp, [ds:si + bx] ; 将进程堆栈指针送到SP,切换到进程的堆栈空间 pop ds pop es popa ; 进程的堆栈空间从恢复上次进程被中断时 ; 所有被保存的CPU寄存器 iret ; 激活被调度进程运行;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;键盘中断子程序keybd;功能:读入键盘扫描码,转换为ASCII码后在显示器上输出;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;keybd: stipush axpush bxpush cxpush dipush esmov ax,0in al,60h ; 读入键盘扫描码cmp al,80h ; 是按键压入码还是释放码?jb .l1 ; 是按键压入码则转 .l1处理jmp .l2 ; 否则结束中断.l1: mov ah,0mov si,axmov al,[si+asctab] ; 查表,转换为ASCII码mov ah,0ehint 10h ; 在显示器上输出转换后的ASCII码cmp al,0dh ; 是否为回车键?jnz .l2 ; 否,则结束中断mov ax,0e0ahmov bh,07hint 10h ; 是回车键,则产生换行动作。in al,61h.l2or al,80hout 61h,aland al,0x7fout 61h,al ; 给键盘内的单片机发复位信号pop espop dipop cxpop bxpop axmov al,20hout 20h,al ; 给中断控制器8259送EOI信号iret ; 键盘中断结束;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;printhex:16 进制数显示子程序 ;; 参数: 参数1=显示行坐标;; 参数2=显示列坐标;; 参数3=显示的一个字数值;; 参数通过堆栈进行传递;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;printhex: push bp mov bp,sppush axpush bxpush cxpush dx push di push es ;保存现场 mov ax,VIDEORAM mov es,ax ;显示缓存段地址 0xb800 -> es;按公式 (行号*80+列号)*2 将行列值转换为显示缓存的绝对地址 xor ax, axmov ax, word [bp+8] ; 从堆栈中取得行号 xor cx,cx mov cl,80 mul cl ;行号*80 -> ax add ax,[bp+6] ;从堆栈中取得列号,将列号+行号*80 -> ax add ax,ax ;(行号*80+列号)*2 sub ax,2 ;显示缓存从0编址,调整为实际地址。 mov di, ax ;实际地址送di保存mov ax, word [ss:bp+4] ; 从堆栈中取得要输出的16位数据;将要输出的数据从低到高每4位一组转换为16进制数,转换规则为;若数值<=9,则加0x30,否则加0x37.;写到指定的显存中输出;mov cx, 0x08 add di,cx std.continuemov bx, axand bl, 0x0Fcmp bl, 0x9ja .hexadd bl, 0x30jmp .print.hex:add bl, 0x37.print: push ax mov al,bl mov ah,7 stosw pop ax sub cx, 2shr ax, 4cmp cx, 0jnz .continue pop es pop dipop dxpop cxpop bxpop ax pop bp ; 恢复现场ret 6 ; 返回,清除参数,平衡堆栈; End of printhex;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 数据区;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;task1ctr dw 0 ;task1计数器task1ctra dw 0 ;task1时间延迟计数器task2ctr dw 0 ;task2计数器task2ctra dw 0 ;task2时间延迟计数器task3ctr dw 0 ;task3计数器task3ctra dw 0 ;task3时间延迟计数器current_task dw MAXTASKS ;当前任务索引号taskcount dw 0 ;当前kernel内的任务总数sptable: resw MAXTASKS ;sptable 数组;键盘扫描码/ascii码对照表asctab db 0xff,0x1b,"1234567890-=",08h,09h,"qwertyuiop[]asdfghjkl;'zxcvbnm,./";;kernel 结束
- 编写实模式多任务操作系统模型之(5)
- 编写实模式多任务操作系统模型之(1)
- 编写实模式多任务操作系统模型之(2)
- 编写实模式多任务操作系统模型之(3)
- 编写实模式多任务操作系统模型之(4)
- 编写实模式多任务操作系统模型之(6)
- 操作系统实验四:保护模式之局部任务(LDT)初探
- 解说操作系统:(2)多任务模型引入的问题
- 实时多任务操作系统之我见
- 操作系统之任务管理(未完成)
- 操作系统如何管理CPU资源?细说操作系统进程与多任务模型问题
- 自编STM32轻量级操作系统(二)------任务调度
- 嵌入式操作系统之任务调度
- 操作系统多任务--网摘
- 多任务操作系统
- 多任务操作系统
- 30天自制操作系统之第15天 多任务(1)
- ARM9开发之实现多任务操作系统的基本技术
- Internet Information Services 6.0 功能
- 编写实模式多任务操作系统模型之(1)
- 编写实模式多任务操作系统模型之(2)
- 编写实模式多任务操作系统模型之(3)
- 编写实模式多任务操作系统模型之(4)
- 编写实模式多任务操作系统模型之(5)
- 编写实模式多任务操作系统模型之(6)
- 我的VHDL学习笔记(3)
- 吉林移动的冬夜
- suse93&suse10安装
- 字符串UTF-8与ASCII相互转换
- 恢复Windows2000/XP 管理员密码
- WPF体验
- JAVA 几个经典问题(转)