编写实模式多任务操作系统模型之(5)

来源:互联网 发布:ftp空间域名 编辑:程序博客网 时间:2024/05/10 05:28
5.切换到下一将被调度运行task2的进程堆栈空间。由于在进程堆栈保存有进程的断点信息,所以找到进程堆栈的顶位置便可转入到该进程运行。scheduler对于各进程的调度采用轮转式的方式,即按照task1 --> task2 --> task3的顺序,而后再从task1 开始依次循环,使各个进程得以运行。所以将current_task的值增1,再判断current_task是否超过最大进程数MAXTASKS,若超过,则使其复位,从0开始重新计数即可。取得合适的current_task值后,按上面步骤的公式便可找到将被调度进程的堆栈指针的存放地址,从中取出值送到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,切换到进程堆栈空间


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 结束


原创粉丝点击