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

来源:百度文库 编辑:神马文学网 时间:2024/04/30 03:32:16
(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{
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;icprintf("%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;icprintf("%c",l);
cprintf("%c",c);
for(i=y1+1;igotoxy(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(leni=(MBRECT.nLength-len)/2+1;
gotoxy(i,2);
cprintf("%s",title);
}
void PutPrompt(char *prompt)
{
int len;
int i;
len=strlen(prompt);
assert(leni=(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(leni=(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]: <>,赵廷哲.1995
资料[2]: <<未公开的DOS核心技术>>,ANDREW SCHULMAN等.清华大学
出版社 1992
资料[3]: <>,尹彦芝.清华大学出版社 1991
在设计过程中,作者始终得到了计算机系老师们的大力帮助,王
让定副教授给予了热情指导,提出了许多宝贵意见和建议, 建立了本
系统的思想框架,并对本文作了详细的审阅; 王小牛老师作为作者的
指导老师,贯穿于论文设计之中提出了许多思想,观点,尤其是他介绍
给作者的多任务下的多堆栈切换机制使作者深受启发,并使作者最后
下决心将简单的前后台任务改为真正的多任务系统,在设计的整个过
程中他都给予了具体指导;索国瑞老师则给作者讲述了显示卡上的许
多知识,解决了屏幕保存/恢复的难题;  还有许多老师和同学们则在
不同方面提出了见解和建议,它们都是论文思想的来源, 在此一并表
示感谢.