分享

DOS 下多任务系统的设计与实现

 fan_tao 2005-11-09
 DOS 下多任务系统的设计与实现

 

 (Designing And Implement Of Multi_Task System Based On DOS)

 

                    作者:  王晓智

 

 

 

 

                       ABSTRACT

 

     This  paper  tell  how  to  design  and  implement  a

 multi_task system based on DOS under the condition of the

 real_mode. And how to  expand  DOS  system  in  order  to

 environment of multi_task. And the program is given.

 

KEYWORD:  Multi_Task System,DOS,Task Scheldure

 

 

摘要:   本文阐述了在实方式下基于DOS的多任务系统的设计及实现

方法,主要讨论了如何扩展DOS系统以适应多任务环境,并给出了一个

任务切换程序实例.

 

关键词:  多任务,DOS,任务切换

 

 

 

 

.引言:

 

     DOS扩展为多任务系统 ,在理论和实践中都有很重要的意义.

但由于DOS系统单用户,单任务的设计局限,在实现中有不少难点.

文提出一种实现方法及其具体设计.

     现有的多任务DOS系统,可以分为两类. 一类是在其它多任务系

(OS/2,UNIX,WINDOWS NT)的支持下的DOS多任务. 在这类系统

,有着严格的,良好的任务保护和隔离机制,DOS 等于是一个虚拟机

器环境或一个客户服务器,不包括在机器内核之中,因此,在内核之上

运行多个DOS任务比较容易实现,但是,大量的DOS 上的运行软件要求

在运行时完全控制计算机资源,这样的做法势必与操作系统的资源管

理机制相冲突.为此,许多系统又设计为在运行DOS程序时可以选择独

占系统资源,即便如此,DOS程序也有诸多限制, 这类任务切换系统

的优点是功能强大,可靠性高.

     第二类系统是在DOS系统本身的支持下,通过对DOS内核的改造,

使的DOS具有一定的多任务能力,典型的如DOSSHELL.由于一般用户

DOS的多任务能力要求并不是很高,如后台并发进程,设备驱动排队

等并不需要,而仅是希望能够在程序间方便的进行任务切换, 同时不

影响被切换的程序,而这类系统对用户的最大方便是用户程序在执行

时可以独占机器,同时,这类系统建立在DOS系统之上, 对用户程序兼

容性好,因此仍有一定的实用价值.

     相比较之下,第一类占有很大的技术和性能优势,但它对机器硬

件要求高,系统设计复杂,并且不能作到对现有程序的全兼容,第二类

在近几年内还有一定的市场,特别会流行于低档PC机上, 它的设计较

之第一类是很简单的,本文提出的设计方案就基于第二类.

 

.设计思想:

     许多的DOS应用程序在运行时要求全部的内存空间,一般要求达

400K以上.所以在640K内存中运行多个任务是不现实的, 只能将不

使用的任务对换出常规内存,暂时存入硬盘或扩展内存(本文将1088K

以上空间泛指扩展内存),在任务再次执行时再换入常规内存 .当然,

这种方法是以增加任务切换时间为代价的.

     作为任务切换程序必须很好解决DOS重入问题,由于DOS 内核设

计上的单任务串行执行性,DOS是不支持多任务的.一些程序通过一些

其它方法(如检测IN_DOS信号量)来进行重入,这类方法不但局限性大

(许多情况下不能使用),而且只能重入一次, 在多任务环境下无法使

.目前,使DOS内核可以执行多任务代码的最佳方法是SDA 对换重入

,DOS3.16.2均提供稳定支持,但由于许多细节没有澄清, 一些

使用此办法的DOS重入程序可靠性不高,程序运行结果似是而非.只有

SDA对换技术作深入研究之后,才能正确使用 .关于这方面的资料,

详见资料[1].

     需要指出的是,使用SDA对换技术之后,DOS可以支持多个任务.

     由于应用程序可能接管中断向量(TSR或某些字处理软件),

果系统在这方面缺乏控制,当多个任务在内存中相互作用, 形成复杂

的中断链时,几乎肯定要死机,因此从系统的安全性,健壮性的角度出

,应当控制全部的中断向量.

     从单任务转化到多任务,要求对一些系统资源也要进行处理,

屏幕,当前目录等,使它能与系统当前任务相对应.

     作为任务切换软件,必须解决以上的问题,本文试给出一种解决

方案,具体如下:

 

   1.内存对换技术:

     由于DOS的许多应用程序要求大量内存,因此将活动任务装入内

,而将其它任务暂存于磁盘或扩展内存中, 就能将整个内存分配给

单个任务以满足内存需要,在用户请求任务切换时, 将现有任务暂存

于磁盘或扩展内存中,并将新的任务装入内存运行,在切换过程中,

有的任务都具有相同的优先级,而由用户决定运行哪一个任务, 这个

过程可以表示如下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

     将所有的任务看作一个大的轮盘,在任务切换时,将现有任务存

于轮盘上,并将新的任务从轮盘装入内存运行,在这种情况下,每个任

务都认为自己拥有全部的计算机资源,这种多任务方式可以称作轮盘

式多任务.考虑多方面的因素,没有必要支持任务在后台执行.

 

   2.避免中断向量的复杂链接:

     考虑到应用程序不可避免的要挂接中断向量, 在任务对换出去

,若中断向量还指向原处,在新的任务装入后,中断向量便会指向不

可知区域,所以,要保存各个任务的中断向量表,使中断向量表局部于

任务.在任务切换时,保存任务的中断向量表,在任务交换到磁盘之前

将内存中所有指向此任务的中断向量改为此任务运行之前的值,即强

行摘除任务挂接的中断向量,当任务再次运行时, 将保存的中断向量

恢复,"安装"它的中断处理程序,这样,任务挂接的中断仅在任务在

内存中执行时起作用,当它被对换出内存后,中断向量被复原.这使的

在多个任务均挂接中断向量时,不会形成复杂的, 无法解开的中断链

机制.

 

   3.与其它程序的相容性:

     作为任务切换程序,必须和用户程序共驻内存,这就需要任务切

换程序对用户应用程序的影响要尽可能的小,但是对有些实在无法避

免冲突的情况,则不予考虑, 这虽然在软件工程中被看作是无法容忍

,但却是理论和实践相结合的很好例子. 只要能够相容于绝大多数

普通应用软件,及一部份的TSR,就可以了.而与其它任务切换软件(

DOSSHELL)的相容性则不予考虑,理由是:没有必要在内存中存在两个

任务切换程序,同时,不考虑与DOS 5.0的任务切换API的接口问题,

将努力使两个任务切换程序能在内存中相容,不发生冲突.

     相容性是一个软件能够存在和发展的重要因素, 从软件设计思

想到具体技术实现,都因当首先考虑,把它贯穿于设计过程之中.首先,

应在设计思想中重视相容性,不使用相容性差的思想及技术, 并且要

考虑在今后的发展中会遇到的不相容的因素.其次, 在设计过程中要

与现有软件作对比和测试,尽最大可能避免冲突.

     需要指出的是,相容性是在系统设计时考虑的重点,但在设计完

成之后,不再考虑.

 

   4.软件设计中的其它考虑:

     出于单任务改造为多任务的需要,我们必须使屏幕,驱动器,

前目录也局部于具体任务,在任务切换时做相应的保存恢复工作.

     虽然汇编语言是任务切换软件设计的直接选择,但在权衡速度,

效率等方面之后,决定采用C语言来编写,毕竟,采用C语言编写系统软

件已有许多成功的先例,值得一试.

     由于任务切换软件不可避免的会遇到直接使用DOS 数据结构及

未公开的DOS功能调用,因此一定要保证在各个 DOS版本下的兼容性,

程序何时与DOS交互,何时深入DOS编程,都应作仔细考虑,特别是直接

存取DOS数据结构及使用未公开的DOS功能调用对兼容性影响最大,

使用DOS所稳定支持的,并且将来也不大可能改变的功能,在软件中直

接使用的DOS数据结构有:

 

        1. MCB: 内存控制块(Memory Ctrl Block).

        2. SDA: DOS 可对换数据区(DOS Swap Date Area).

使用未公开功能调用有:

 

        1. INT 21H,5D06H DOS 可对换数据区(SDA)信息.

            入口:

                AX=5D06H

                (DPL 参数有争议,这里按大多数书上所写,将它略

)

            出口:

                DS:SI=SDA 地址

                CX= INDOS 对换区大小

                DX= 一直对换区大小

        2. INT 21H,50H 设置当前PSP.

            入口:

                AH=50H

 

这些数据结构和功能调用都是DOS3.16.2所稳定支持的( 未考虑

DOS4.X下的特殊情况),估计在未来的版本中也不大可能发生变化,

,不但任务切换软件无法运行,众多的DOS应用软件也将不能运行.

     应当指出,DOS在这种既提供多任务支持, 又不建立多任务内核

的矛盾情况下 ,它的内核代码也是有许多反映这种矛盾情况的地方.

碰到这种情况意谓着编程者要小心"地雷",不要从上面踩过去.

 

.设计技术:

   1.内存对换:

     一般的内存映象如下图:

 

    ┌──────┐

      D O S    

    ├──────┤

      COMMAND  

    ├──────┤

      T A S K   First swap seg

    ├──────┤ ── ┌─────┐ ┌─────┐

                                         

                                         

       USER 1           USER 2    USER 3 

                                         

                                         

                                         

                                         

    └──────┘      └─────┘ └─────┘

                                                         

  

 

 

     如果用单板机上的存储体切换技术来看这张图 , 就比较清楚

,TASK及它以前的内存属于不切换块, 而用户空间则随着具体任务的

变化而选中相应的存储体, 流行的 VROOM (覆盖) 技术也与此类似

.

     用户空间随着系统配置而稍有变化,一般在450-550K左右 ,

DOS被装入HMA,用户内存可达600K左右, 这些内容在切换时均被写

入磁盘,因写入量特别大,用户最好配备扩展内存,以加快切换速度.

   2.任务信息:

     为了尽量压缩任务切换程序所占内存空间, 几乎所有的任务信

息均被写入磁盘,任务信息主要包括现场,SDA,中断向量表,内存,

幕等等.这样,大大压缩了任务切换程序所占内存空间,但也要求更多

的磁盘空间.

   3.关于信号量:

     在多任务环境下,代码间的同步,互锁显得非常重要, 有许多的

信号量标志(包括与DOS进行通信和交互), 应当正确处理好这些信号

.如当热键激活后,在容许再次按热键之前,用户按下热键不应当被

再次激活,以避免切换程序本身的重入,伪代码为:

 

INVECT IntrHook proc far

 

        if not pop_up then

            pop_up=true

 

            // do anything in here

 

            pop_up=false

        else

 

        iret

 

   4.C语言编程:

     采用C语言编程给程序设计带来了一些新的问题,这些问题必须

在编程中很好的解决 , 举一个例子 , 在中断服务代码中无法使用

fprintf函数,为什么呢?因为fprintf 在函数内部使用了内存分配函

,而在中断服务程序执行时无法预料能否调用内存分配函数( 这里

牵涉到是否遇到DOS重入,当前有无堆可供分配等等 ) . 在不能使用

fprintf的前提下,用什么函数来代替,fprintf 相类似的函数在库

函数中还有哪些等等.同时,高级语言如何保护现场,如何进行任务切

,如何切换堆栈等问题,这在资料[2],资料[3]中已被论及. 这里给

出一个在屏幕上打出时间的中断服务程序,在此可以看出库函数如何

使用:

 

 

void interrupt NewInt1C(void)

{

static int nIsEnter=0;

static char sStr[12],*pStr;

static char far *p;

static unsigned char h,m,s;

 

        (*OldInt1C)();

 

        if(nIsEnter++<4)

                return;         /* 每隔200ms打印一次 */

        else

        {

                nIsEnter=0;     /* 互锁信号量 */

                _AH=2;

                geninterrupt(0x1a);     /* get  time  from

 real clock */

                h=_CH;

                m=_CL;

                s=_DH;

 

#ifndef NDEBUG          /* 调试信息: ERROR_PTR,INDOS_PTR */

 

                sprintf(sStr,"%d %d ",*(pDosSwapArea),

                        *(pDosSwapArea+1));

 

#else                   /* 任务号,,, */

 

                sprintf(sStr,"%02d %02x:%02x:%02x",

                        uTask,h,m,s);

 

#endif

 

        /* Can not use standard I/O library function */

        /* 虽然可以用cprintf函数,但不推荐这样做 */

 

                pStr=sStr;

                p=MK_FP(0xb800,160-22); /* Video Buffer at

 right_top */

                while(*pStr)

                {

                        *(p++)=*(pStr++);         /*   OME

 char */

                        *(p++)=RED;             /* Attrib*/

                }

        }

}

 

     这个程序虽然很简单,但还有一些需要解释的地方,nIsEnter

明为static是因为多次进入NewInt1C时需要知道此信号量的一贯值,

而其它的值声明为static是为了防止过多的局部变量引起堆栈溢出,

在调用sprintf,要在编译时保证DS!=SS,否则参数压栈和引用参数

无法得到正确结果(请注意,NewInt1C执行时,堆栈可能是属于当前

内存中的任意进程所有,而不光是你的程序),程序不使用cprintf

因为它有可能与前台任务的文本函数相互干扰,并有可能造成BIOS

,最重要的是,它的堆栈占用太大(尽管它可以在程序中工作, 但这

并不是使用它的理由).

 

.设计实现:

 

   1.程序设计:

     为了实现强制任务切换,程序必须挂接INT 9H,以达到必要的快

速响应.同时,应实现以下流程:

 

 

  ┌─────┐

    任务 1     

  ├─────┤

    任务 2           INT 9H  服务程序

  ├─────┤    ┌─────────┐

    任务 3                       

  ├─────┤        调度程序     

    任务 4                       

  ├─────┤    └─────────┘

    任务 5 

  └─────┘

     就绪队列

 

 

     INT 9H程序伪代码为:

 

     if not our hotkey

     {

          old_int9 proc

          return;

     }

     if also POPUP or DOS_busy or crit_error

          return;

     POPUP=1;

    

          enter scheldure

 

     POPUP=0;

     return

 

     scheldure程序伪代码为:

 

     save oldSDA    保存老的SDA

    

     save oldVECT      保存老的中断向量

     save oldmemory    保存老的内存

 

     get newTASK       取下一个任务  

 

     load newmemory    装入新的内存

     load newVECT      装入新的中断向量 

     load newSDA       装入新的内存 

     return

 

   2.关键技术的解决办法:

 

     内存的保存涉及MCB(memory contrl block)

 

     struct MCB {

          char attr;      ‘M‘: normal,‘Z‘:last MCB

          unsigned size;     

          unsigned owner;     owner psp

     }

     MCB 近乎于单链表,为段对齐,下一块MCB 的段地址等于本块的

段地址加本块的长度,一直到最后一个MCB,它的attr‘Z‘.保存/

复内存时,从用户的MCB开始,一直到 A000H.以下为伪代码:

 

     char *p;

     for(p=firstaddress;p<MK_FP(0xa000,0);)

     {

          write(file,p,BLOCKSIZE);

          p+=BLOCKSIZE;

          p=(char huge *)p+0;

     }

 

 

 

     DOS在关键区时,不容许任务切换,因此建立CRIT_ERROR 信号

量来与DOS通信(详见资料[1]):

 

DOS进入或退出关键区会通知用户:

 

     INT 2AH

          AH=80:    DOS 通知用户进入关键区

          AH=81:    DOS 通知用户退出关键区

          AH=82:    DOS 通知用户退出关键区

 

用户挂接INT 2A 后就可知道DOS是否在关键区.

    

     INT2A proc far

          switch(ax&0xff00)

          {

               case 8000:         

                    CRIT_ERROR++;

                    break;

               case 8001:

               case 8002:

                    if(CRIT_ERROR)

                         CRIT_ERROR--;

                    break;

               default:

                    old_INT2A

          }

          iret

     endp

 

     屏幕的保存伪代码如下:

    

     save display mode

    

     if mode<8 save textbuffer

    

     else save  graphicsbuffer

       

     save txetbuffer

     {

     char far *p=MK_FP(0xb800,0);

          write(fp,p,4000);

     }

     save graphicsbuffer /* 图形下必需保存4个页面 */

     {

     char far *p=MK_FP(0xa000,0);

          select page0

               write(fp,p,38400);

          select page1

               write(fp,p,38400);

          select page2

               write(fp,p,38400);

          select page3

               write(fp,p,38400);

     }    

 

     由于使用C语言编程,C语言中断程序实际上是:

 

     void interrupt a(void)

     {

     do any code in here

     }

    

     相应的汇编语言为:

 

     a proc far

     push all

     mov ds,DATA

    

     do any code in here

    

     pop all

     iret

     endp

 

     而手工汇编为:

     a proc far

     iret

     endp

 

     了解这些有助于我们的程序设计,C语言相对于汇编语言还存在

一定的差距,体积和效率都太差.

 

      对话框是程序设计中用到的一个对象, 全部风格仿照afxwin

.h的式样,代码如下:

 

 

/* 画一个比窗口略大的框 */

 

#define SINGLELINE       1

#define DOUBLELINE    2

 

void Box(RECTSTRUCT *p,int type,char *title) {

int x1,y1,x2,y2;

int i;

int a,b,c,d,h,l;

int titlelen;

 

       if(type==SINGLELINE) {

              a=218,b=191,c=217,d=192,h=179,l=196;

       }

       if(type==DOUBLELINE) {

              a=201,b=187,c=188,d=200,h=186,l=205;

       }

 

       x1=p->nXBegin;

       y1=p->nYBegin;

       x2=x1+p->nLength;

       y2=y1+p->nHigh;

 

       x1--,y1--,x2++,y2++;

 

       window(1,1,80,25);

       SetColor(ColorStruct.BoxColor,ColorStruct.BoxBackColor);

 

       gotoxy(x1,y1);

       titlelen=strlen(title)+2;

 

       cprintf("%c",a);

        for(i=x1+1;i<x2;i++)

              cprintf("%c",l);

 

       cprintf("%c",b);

 

       gotoxy(x1+(x2-x1+1-titlelen)/2+1,y1);

       cprintf("%s",title);

 

       gotoxy(x1,y2);

       cprintf("%c",d);

       for(i=x1+1;i<x2;i++)

              cprintf("%c",l);

 

       cprintf("%c",c);

       for(i=y1+1;i<y2;i++) {

              gotoxy(x1,i);

              cprintf("%c",h);

              gotoxy(x2,i);

              cprintf("%c",h);

       }

}

 

 

/*

       提示框类型

*/

 

 

#define ID_OK                     1

#define ID_YESNO        2

#define ID_YESNOCANCEL  3

#define ID_CANCEL       4

 

/*

       提示框返回值

*/

 

 

#define MB_OK       1

#define MB_YES  2

#define MB_NO       3

#define MB_CANCEL 4

 

typedef struct {

       int nXBegin;

       int nYBegin;

       int nLength;

       int nHigh;

} RECTSTRUCT;

 

 

RECTSTRUCT MBRECT={

       30,10,24,5

};

 

#define BOXCOLOR  WHITE

#define BOXBACKCOLOR LIGHTBLUE

 

 

 

int MessageBox(char *title,char *prompt,int type)

{

char *pBuf;

int size;

int nReturnValue;

       size=GetRectSize(&MBRECT);            /* 要求保存/恢复屏幕 */

       pBuf=malloc(size);

       assert(pBuf);

       SaveScreen(&MBRECT,pBuf);

       nReturnValue=PopMessageBox(title,prompt,type);

       RestoreScreen(&MBRECT,pBuf);

       free(pBuf);

       return(nReturnValue);

}

 

int PopMessageBox(char *title,char *prompt,int type)

{

static char *str[2]={

       "  Message Box ",

       "        ",

};

RECTSTRUCT MBRECT2;

 

       MBRECT2=MBRECT;

       MBRECT2.nXBegin+=2;

       MBRECT2.nYBegin+=1;

       MBRECT2.nLength+=2;

       MBRECT2.nHigh+=1;

 

       SetColor(BLUE,BLUE);

 

       SetWindow(&MBRECT2);

       clrscr();                  /*  画一底色 */

 

       Box(&MBRECT,DOUBLELINE,str[GetLanguage()]);

       SetWindow(&MBRECT);

       SetColor(BOXCOLOR,BOXBACKCOLOR);

       clrscr();

 

       PutTitle(title);        /*  打出提示 */

       PutPrompt(prompt);

       return(BoxKey(type));   /*  返回用户按键 */

}

 

int BoxKey(int type)      /* 仅返回 MB_OK,MB_CANCEL,MB_YES,MB_NO */

{

int key;

       PutBoxMessage(type);

       while(1)

       {

              switch(type)

              {

                     case ID_OK:

                            GetKey();

                            return(MB_OK);

                     case ID_YESNO:

                            key=GetKey();

                            if((key==‘Y‘)||(key==‘y‘))

                                   return(MB_YES);

                            if((key==‘N‘)||(key==‘n‘))

                                   return(MB_NO);

                            break;

                     case ID_YESNOCANCEL:

                            key=GetKey();

                            if((key==‘Y‘)||(key==‘y‘))

                                   return(MB_YES);

                            if((key==‘N‘)||(key==‘n‘))

                                   return(MB_NO);

                            if((key==27)||(key==‘C‘)||(key==‘c‘))

                                   return(MB_CANCEL);

                            break;

                     case ID_CANCEL:

                            key=GetKey();

                            if((key==27)||(key==‘C‘)||(key==‘c‘))

                                   return(MB_CANCEL);

                            break;

                     default:

                            assert(0);

                            break;

              }

       }

}

 

int GetKey(void)

{

int key,lo,hi;

       key=bioskey(0);

       lo=key&0x00ff;

       hi=(key&0xff00)>>8;

       return(lo==0?hi+256:lo);

}

 

void PutTitle(char *title)

{

int len;

int i;

       len=strlen(title);

       assert(len<MBRECT.nLength);

       i=(MBRECT.nLength-len)/2+1;

       gotoxy(i,2);

       cprintf("%s",title);

}

 

void PutPrompt(char *prompt)

{

int len;

int i;

       len=strlen(prompt);

       assert(len<MBRECT.nLength);

       i=(MBRECT.nLength-len)/2+1;

       gotoxy(i,3);

       cprintf("%s",prompt);

}

 

void PutBoxMessage(int type)   /*  预先定义好的提示串 */

{

char **p;

static char *str1[2]={

       "OK!",

       "确认"

};

static char *str2[2]={

       "Yes  No",

       "   "

};

static char *str3[2]={

       "Yes  No  Cancel",

       "     放弃"

};

static char *str4[2]={

       "Cancel",

       "放弃",

};

       switch(type)

       {

              case ID_OK:

                     p=str1;

                     break;

              case ID_YESNO:

                     p=str2;

                     break;

              case ID_YESNOCANCEL:

                     p=str3;

                     break;

              case ID_CANCEL:

                     p=str4;

                     break;

              default:

                     assert(0);

                     break;

       }

       PutMess(p[GetLanguage()]);

}

 

#define MESSCOLOR           LIGHTGRAY

#define MESSBACKCOLOR   BLACK

 

void PutMess(char *p)

{

int i;

int len;

char buf[64],*s;

       len=strlen(p);

       strcpy(buf,p);

       assert(len<MBRECT.nLength);

       i=(MBRECT.nLength-len)/2+2;

       gotoxy(i,5);

       SetColor(MESSCOLOR,MESSBACKCOLOR);

       s=strtok(buf," \t");

       while(s)                       /* 分隔打印 */

       {

              cprintf("%s",s);

              i=i+strlen(s)+2;

              gotoxy(i,5);

              s=strtok(NULL," \t");

       }

}

 

 

int GetRectSize(RECTSTRUCT *s)  /* 返回窗口尺寸(有余量) */

{

int x,y,xx,yy;

int size;

       x=s->nXBegin-1;

       y=s->nYBegin-1;

       xx=s->nXBegin+s->nLength+4;

       yy=s->nYBegin+s->nLength+4;

       size=(yy-y+1)*(xx-x+1);

       return(size);

}

 

 

void SaveScreen(RECTSTRUCT *s,char *buf)

{

       gettext(s->nXBegin-1,s->nYBegin-1,

              s->nXBegin+s->nLength+4,

              s->nYBegin+s->nHigh+4,buf);

}

void RestoreScreen(RECTSTRUCT *s,char *buf)

{

       puttext(s->nXBegin-1,s->nYBegin-1,

              s->nXBegin+s->nLength+4,

              s->nYBegin+s->nHigh+4,buf);

}

 

 

.设计检验:

     程序设计及调试是在一台 Legend LX E3/40 上进行的,采用的

DOS版本为OME DOS 3.31,C语言编译器为Turbo C 2.0.源程序长约

13K,可执行文件比源文件略小,说明C语言编程是比较成功的.

 

     主要指标为:

┌────────┬──────┬───────────┐

│项            │指                      

├────────┼──────┼───────────┤

│任务数            6                            

├────────┼──────┼───────────┤

│切换速度          3       1(使用扩展内存)   

├────────┼──────┼───────────┤

│任务显示模式支持│  13H以下   320*400 256       

├────────┼──────┼───────────┤

│对换空间占用      900K以下                       

└────────┴──────┴───────────┘

     在调试完成之后,软件在 DOS 3.31,DOS 5.0,DOS 6.2下进行了

运行测试,主要目的是观察它与其它软件的相容性,同时,也与DOS 5

.0下的DOSSHELL进行了对比.

 

     DOS 3.31下的结果如下:

 

     此机上所装大部分软件均运行良好,清单如下: CCED 5.0, TC

 2.0,TURBO PASCAL 5.0, PCTOOLS 5.0, FOXBASE 2.1,  DBASE  3

 PLUS, BGIDEMO(TC下的图形演示软件).WPS SPT 运行正常,但任

务切换无法进行,原因是它们在挂接INT9H 后均未调用原来的中断处

理程序.使用CHINA(一个地图漫游软件)时在装入过程中死机.TT(

个采用320*200 256色显示模式及模拟语言发声的指法练习软件

)使用正常.

     SPDOS 5.0 下使用中效果良好,甚至将SPDOS作为普通任务进行

切换也没有什么问题.

     UCDOS 3.0 在使用中效果良好,但由于它的图形模式特别,偶而

在切换过程中屏幕保存/恢复出现问题,但程序并未死机,适当处理后

可继续运行(UC_DOS必须先装入).

     CodeView 2.0在切换中严重死机,DEBUGTurbo Debugger 3.

0运行正常.

     当任务数超过4个时,有时有死机现象,主要是因为DOS 3.31

的设置为 FILES=20,任务数增加时打开文件出错造成的.

 

     综上所述,大部分的应用软件均可良好相容,但游戏软件相容性

比较差,这与设计初的设想一致.

     CodeView 2.0的严重死机可能是由于它再加载进程运行调试的

同时,它自己也在内存中进行着覆盖对换, 与切换系统产生冲突而引

.

 

     DOS 6.2下的结果如下:

 

     由于没有DOS 6.2下的DOSSHELL,因此测试项目与DOS 3.31相同,

结果也相同,但由于设置为FILES=30,所以很少出现死机.

 

     DOS 5.0下的结果如下:

 

     DOS 5.0下的设置为FILES=60,测试项目与DOS 3.31相同, 结果

也相同,因此主要的测试转向与DOSSHELL(DOS 5.0下的任务切换软件)

的对比,对比结果如下(TASK 为作者所设计的任务切换软件):

 

   1.指标对比:

 

┌─────────┬─────────┬──────┐

程序表现            DOSSHELL          TASK    

                                          

├─────────┼─────────┼──────┤

编写语言                          TC 2.0  

├─────────┼─────────┼──────┤

内存占用            38K               22K     

├─────────┼─────────┼──────┤

界面友好程度                              

├─────────┼─────────┼──────┤

任务数                                

├─────────┼─────────┼──────┤

切换速度(一般)      2秒以内           3秒以内 

├─────────┼─────────┼──────┤

挂接中断向量        20            三个    

├─────────┼─────────┼──────┤

其它功能            文件,进程管理     很少    

├─────────┼─────────┼──────┤

应用软件相容性      见下面的分析              

└─────────┴─────────┴──────┘

                                                          

        

 

   2.应用软件相容性(切换效果)对比:

 

              DOSSHELL                TASK

 

TC 2.0                                 

 

TURBO PASCAL 5.0                       

 

PCTOOLS 5.0                            

 

CCED 5.0          西文好                 

 

DEBUG                                  

 

FOXBASE 2.1       西文好                 

 

DBASE 3 PLUS      西文好                 

 

BGIDEMO           可以运行               

                  无法切换

SPDOS 5.0         使用冲突                

 

UCDOS 3.0         部分软件使用冲突        ,

                                          但须先加载UCDOS

                                          否则不能切换

WPS 2.1           SPDOS不能运行           可以运行

                  所以无法运行            无法切换

 

SPT 1.2           SPDOS不能运行           可以运行

                  所以无法运行            无法切换

 

CodeView 2.0      不能切换                不能切换

 

TURBODEBUGGER 3.0 不能切换               

 

TT                无法运行               

 

 

:

   :                     运行切换均正常.

   西文好:                 无法装载汉字系统,西文下正常.

   可以运行,无法切换:      运行正常, 但无法切换或切换时程序

退出.

   不能切换:               运行正常,切换时引起死机.

   使用冲突:               运行时死机,无法装入内存.

 

     运行结果可以总结为:

 

     DOSSHELL 在普通任务切换效果上比TASK,但它不适合于图形

切换,并且与汉字系统和TSR程序很不相容,它的另一个优点是优秀的

文本界面.

     TASK 在设计上比 DOSSHELL 粗糙,但它的代码短小, 并且相容

性比较好,特别适合于图形及汉字下的任务切换.

     TASK 自始至终没有考虑与DOSSHELL的相容性问题,但两者之间

有着良好的相容性,两个程序甚至可以在内存中共同进行任务切换而

互不影响.

 

.其它问题:

 

     任务切换软件还有一些使用技巧, 例如可以用它来任意保存屏

幕图象,卸下TSR程序等等,下面列出几个:

 

   1.保存/恢复屏幕:

     TASK在任务切换时保存了用户屏幕,所以当需要得到屏幕图像

,进行任务切换,然后拷贝屏幕影像文件保存,以后可以利用恢复屏

幕功能来重新显示图像 .

   2.卸下TSR程序:

     由于TASK在任务切换时会"摘除"用户程序挂接的中断向量,

以在TASK以后驻留的TSR程序均可在任务切换中被"卸下".

 

.小结:

 

     TASK在近几年内还有一定的使用价值,随着技术的进步,16位程

序将向32位全面过渡,出于兼容的考虑,大量的应用程序将能够 以二

进制兼容的方式继续使用,TASK不属于这类程序.

 

 

参考文献:

 

资料[1]: <<DOS 重入问题的探讨与实现>>,赵廷哲.1995

资料[2]: <<未公开的DOS核心技术>>,ANDREW SCHULMAN.清华大学

出版社 1992

资料[3]: <<C语言高级实用教程>>,尹彦芝.清华大学出版社 1991

 

 

     在设计过程中,作者始终得到了计算机系老师们的大力帮助,

让定副教授给予了热情指导,提出了许多宝贵意见和建议, 建立了本

系统的思想框架,并对本文作了详细的审阅; 王小牛老师作为作者的

指导老师,贯穿于论文设计之中提出了许多思想,观点,尤其是他介绍

给作者的多任务下的多堆栈切换机制使作者深受启发,并使作者最后

下决心将简单的前后台任务改为真正的多任务系统,在设计的整个过

程中他都给予了具体指导;索国瑞老师则给作者讲述了显示卡上的许

多知识,解决了屏幕保存/恢复的难题;  还有许多老师和同学们则在

不同方面提出了见解和建议,它们都是论文思想的来源, 在此一并表

示感谢.

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多