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 对换重入 法,DOS自3.1至6.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 这些数据结构和功能调用都是DOS自3.1至6.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在切换中严重死机,但DEBUG和Turbo 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 在设计过程中,作者始终得到了计算机系老师们的大力帮助,王 让定副教授给予了热情指导,提出了许多宝贵意见和建议, 建立了本 系统的思想框架,并对本文作了详细的审阅; 王小牛老师作为作者的 指导老师,贯穿于论文设计之中提出了许多思想,观点,尤其是他介绍 给作者的多任务下的多堆栈切换机制使作者深受启发,并使作者最后 下决心将简单的前后台任务改为真正的多任务系统,在设计的整个过 程中他都给予了具体指导;索国瑞老师则给作者讲述了显示卡上的许 多知识,解决了屏幕保存/恢复的难题; 还有许多老师和同学们则在 不同方面提出了见解和建议,它们都是论文思想的来源, 在此一并表 示感谢. 附 |
|