编程技术、软件应用与系统模拟

(Programming, Applicaiton and Simulation)



本站目录

 

首页
ASP/Access/IIS
DELPHI/PASCAL
PASCAL高级编程
C语言编程实例
WORD
Excel
MATLAB
MINITAB讲座
Windows
DOS
SAS
生物系统模拟
土壤水分剖析器
其他



镜像站点

 

主站
北美镜象站
欧洲镜象站(1)
欧洲镜象站(2)

本站 Google

[搜索]  [站内导航]
座右铭:
只做有益人类的事
不做有害人类的事


安全释放基本内存和扩展内存的程序

由于DOS操作系统640KB常规内存的限制,如果不能有效地控制和释放内存中的各种TSR程序,在运行一些大
型软件时就会出现内存不够的现象,往往不得不重新启动系统,不仅浪费了时间,而且也容易损坏机器。
最近,报刊杂志上刊登了大量有关清除内存的文章,这些文章提供的程序多是TSR程序。当这些程序驻留内存
后,通过热键或参数操作来将其后驻留内存的TSR程序清掉。在实际应用过程中,不难发现这些程序都不同程
度地存在着一些缺点:①程序自身必须驻留内存,从而占掉了宝贵的常规内存,增加了系统的开销;②大多程
序自身无法撤离内存,或者没有检测自身是否安装的功能,造成重复安装;③它要占用或借用了DOS的中断号
,如有的程序借用了中断5H,使原来的屏幕打印功能失效,这有点治聋子又治成了哑巴的味道;④均不能将其
它程序开辟的但没有释放掉的扩展内存(XMS)释放出来,当运行其它使用扩展内存(XMS)的程序时,造成XMS内
存不够。
本文从DOS对基本内存和扩展内存的分配方法入手,提出了安全释放常规内存和扩展内存(XMS)资源的方法,
并给出了TURBO C程序。

一、内存分配的方法

1.常规内存
当DOS装入一个程序执行时,一般要建立一个环境内存块和一个程序内存块。每个内存块有两部分组成:一个
16字节长的内存控制块(MCB)和以节为单位的内存块。如果多个程序建立了多个内存分配块,这些内存分配块
在内存中就形成一条内存控制链,调用DOS功能52H号,在中断返回时,ES:[BX-2]中即是第一个内存控制块
的地址。
内存控制块(MCB)的结构见表1。用标志字节可以判断内存控制链是否结束;用第2个域可以判断此内存块归谁
所有;用第3个域可以求得下一个内存控制块的地址,方法是用当前内存控制块的地址与此域值相加,再加
1就得到下一个内存控制块的地址;用第4个域可以知道拥有者的名字。这4个域在释放常规内存时都要用到。

               表1  内存控制块(MCB)结构
┌──┬───────────────────────────┐
│偏移│               内                      容            │
├──┼───────────────────────────┤
│ 0  │标志字节,4DH表示内存控制链未完,5AH表示内存控制链结束│
│ 1-2│拥有者段地址,即PSP地址                              │
│ 3-4│内存块长度,以节表示(1节=16个字节)                   │
│ 8-F│程序名,为ASCIIZ字符串                               │
└──┴───────────────────────────┘

2.扩展内存
MS DOS 4.0及以后的版本提供了HIMEM.SYS的扩展内存(XMS)管理程序,连接在多路中断2FH上。用如下程序段
可以获得扩展内存控制程序入口:

     MOV AX,4300H   ; 判断是否安装XMS驱动程序
     INT 2FH
     CMP AL,80H     ; AL=80H,则安装XMS驱动程序,否则,没有安装
     JNE NO_XMS
     MOV AX,4310H   ; 获取XMS控制程序入口地址
     INT 2FH
     LEA DI,XMS_CONTROL
     MOV DS:[DI],BX      ; 存XMS控制程序入口的偏移地址
     MOV DS:[DI+2],ES    ; 存XMS控制程序入口的段地址

获得扩展内存控制程序的入口地址后,可以采用长调用的方式来使用它提供的功能。一般可用9号功能来分配
一块扩展内存,调用返回时,如果分配成功,在DX寄存器中返回一个句柄。扩展内存控制程序就是通过这个
唯一的句柄来存取和释放这块扩展内存的。一个扩展内存句柄加10或减10,即可得到下一个或上一个扩展内存
句柄,这为释放扩展内存提供了便利。扩展内存控制功能的具体使用方法参见有关文献。

二、保存当前的内存状态

根据内存分配的方法可知,欲释放TSR软件占用的内存资源,在运行TSR程序之前,只要将系统的中断向量表、
常规内存的最后一个MCB的地址和最后一个可用的扩展内存句柄保存起来,在需要撤除TSR程序时,将原来的
中断向量表恢复,把内存控制链中保存的MCB值之后的内存控制块全部释放,将保存的扩展内存句柄及其后的
有效句柄所标识的扩展内存全部释放,就完全将内存恢复到TSR程序运行前的状态了。这个过程没有必要用常
驻内存的TSR程序来实现,只需要事先保存欲恢复的内存状态,需要时用程序恢复一下即可。
保存当前的内存状态的方法如下:
1.保存中断向量表:将绝对地址00000-00400H共1024个字节的内容保存到磁盘上即可。
2.保存当前最后一个内存控制块(MCB)的段地址(即自由内存控制块的段地址):首先用DOS功能调用52H获得
第一个MCB的地址,然后从此地址开始遍寻整个内存控制链,最后判断倒数第二个内存控制块是否是程序块,
如果不是,则此内存控制块就是要保存的MCB地址,否则,倒数第一个内存控制块是要保存的MCB地址。
3.保存最后一个可用扩展内存句柄:方法很简单,用XMS控制程序的9号功能申请一块1KB的XMS内存,获得DX寄
存器中返回的句柄,它就是要保存的扩展内存句柄。

三、释放内存的方法

释放内存时,首先要查找内存中是否有第2份COMMAND.COM,当有第2份COMMAND.COM存在时,释放内存是不安
全的,应当先执行EXIT退出COMMAND.COM,然后再释放内存,往往执行COMMAND.COM的程序是暂驻在内存中的
,当COMMAND.COM返回时,可以正常地从内存中退出,就没有再必要释放内存了。如果内存中没有第2份
COMMAND.COM时,就恢复中断向量表,释放常规和扩展内存。恢复中断向量表很简单,下面介绍释放常规内存
和扩展内存的具体方法。
1.释放常规内存:根据原来保存的最后一个MCB的段地址,找到属于TSR的内存控制块后,将内存控制块所在段
地址加1装入ES寄存器,调用DOS功能49H号就可以释放这块内存。然后寻找下一个内存控制块,释放之,这样
从一个内存控制块移向下一个内存控制块并释放它,直到最后一块,即完成了常规内存的释放。
2.释放扩展内存(XMS):根据原来保存的最后一个扩展内存块句柄,调用扩展内存控制功能0DH,解锁它标识
的扩展内存块,如果成功,就调用扩展内存控制功能0AH,释放这块扩展内存,然后将扩展内存句柄加10,即
下一个扩展内存块,重复上述步骤,直到找不到已分配的扩展内存块为止。

四、程序设计与使用

根据上面介绍的方法,作者用TURBO C编写了一个释放常规内存和扩展内存的程序RM.C(源程序见后),该程序
用TCC编译,用TLINK命令连接生成RM.COM即可使用。该程序在286、386、486机器和MS DOS 5.0~6.X下通过
。
该程序不驻留内存,在系统启动之后运行一次,将当前的内存状态保存起来,作为将来释放内存的依据。在需
要释放内存资源时,再执行一次,系统就返回到以前保存的状态。
该程序的使用方法是:
    1.保存系统的当前内存状态,以便将来释放内存时使用,执行:
          RM /B
    2.释放TSR程序所占用的内存,恢复原来保存的系统内存状态,执行:
          RM /R
    3.获得程序的使用方法,执行:
          RM /?
注意:该程序释放扩展内存部分符合扩展内存规范3.0(即XMS 3.0)的规定,所以在MSDOS 4.0或5.0的
HIMEM.SYS(XMS版本2.0)下不能正常工作,必须在WINDOWS 3.1或MSDOS 6.X系统所带的HIMEM.SYS(XMS版本
3.0)下才能正常工作。如你的DOS系统是5.0,只需要用高版本的HIMEM.SYS替代MS DOS 5.0的对应程序即可
。

五、源程序清单

/********************************************************/
/*  程序名称: RM.C 1.0                                  */
/*  作    者: 董占山                                    */
/*  完成日期: 1995-10-31                                */
/*  用    途: 安全释放常规内存和扩展内存,本身不驻留内存 */
/*  编译方法: 用下列命令编译连接可以得到RM.COM:         */
/*  tcc -mt rm                                          */
/*  tlink c:\tc\lib\c0t+rm,rm,,c:\tc\lib\cs\lib /t      */
/********************************************************/

#include <stdio.h>
#include <dos.h>
#include <string.h>

#define FSize 10786l  /* 可执行文件的实际长度 */

char sign[3] = "RM";           /* 程序的标志信息 */
char commandname[8]="COMMAND"; /* COMMAND.COM命令名 */

/* 显示版权信息函数 */
void Copyright()
{
  printf("(RM)Release Memory version 1.00 Copyright (c) 1995 Dong Zhanshan\n");
}

/* 显示程序使用方法的函数 */
void help()
{
  printf("Syntex:\n");
  printf("      RM [/Switch]\n");
  printf("Switches:\n");
  printf("  /B --- Build the interrupt vector table\n");
  printf("  /R --- Release memory\n");
}

/* 寻找最后一个扩展内存分配句柄 */
unsigned int FindLastXMSHdl()
{
 long xms_control;
 unsigned int XMSHdl;
 asm   mov ax,0x4300 /* 检查是否安装XMS驱动模块 */
 asm   int 0x2f      /* 调用多路中断 */
 asm   cmp al,0x80   /* 是否安装XMS驱动程序 */
 asm   jnz l1
 asm   mov ax,0x4310 /* 取XMS驱动程序入口地址 */
 asm   int 0x2f
 asm   lea di,xms_control /* 取XMS驱动程序向量地址 */
 asm   mov [di],bx        /* 保存XMS驱动程序入口偏移地址 */
 asm   mov [di+2],es      /* 段地址 */
 asm   mov ah,0x09        /* 分配一块XMS内存 */
 asm   mov dx,0x01        /* XMS块为1KB */
 asm   call dword ptr [di]  /* 调用XMS功能 */
 asm   mov ah,0x0a        /* 释放已分配的XMS内存 */
 asm   call dword ptr [di]
 asm   mov XMSHdl,dx      /* 保存第一个XMS自由句柄 */
l1:
  return XMSHdl; /* 返回这个句柄 */
}

/* 查找最后一个内存分配块段地址 */
unsigned int FindLastMCB()
{
  unsigned int i,j,MCBArray[100];
  asm   mov ah,0x52
  asm   int 0x21
  asm   sub bx,2
  asm   mov ax,es:[bx] /* 第一个内存分配块段地址 */
  asm   push ax
  asm   pop bx
  asm   lea di,MCBArray /* 装入MCBArray变量的地址 */
l1:
  asm	mov [di],bx     /* 保存MCB段地址 */
  asm	inc di
  asm	inc di
  asm	mov es,bx
  asm	mov al,byte ptr es:[0] /* 取MCB的标志 */
  asm	cmp al,0x4d    /* 是否为最后MCB块 */
  asm	jnz l2         /* 是,转 */
  asm	add bx,es:[3]  /* 计算下一个MCB段地址 */
  asm	inc bx
  asm	jmp l1
l2:
  asm   sub di,6       /* 计算最后一个MCB段地址 */
  asm   mov bx,word ptr [di]
  asm   mov es,bx
  asm   inc bx
  asm   cmp bx,es:[1]
  asm   jne l3
  asm   add di,2
l3:
  asm   mov ax,word ptr [di]
  asm   mov i,ax        /* 返回最后一个MCB段地址 */
  return i;
}

/* 存储最后一个MCB段地址、第一个XMS自由句柄和中断向量表 */
void SaveIntVec(filename)
char *filename;
{
  FILE *fh1;
  int i,LastMCB,LastXMSHdl,IntVec[512];
  LastMCB = FindLastMCB();
  LastXMSHdl = FindLastXMSHdl();
  fh1 = fopen(filename,"rb+"); /* 打开一个二进制流文件 */
  fseek(fh1,FSize,SEEK_SET);   /* 到文件的指定位置 */
  fwrite(sign,3,1,fh1);        /* 写标志字节 */
  for (i=0;i<512;i++) {        /* 写中断向量表 */
     IntVec[i] = peek(0,i*2);
     fwrite(&IntVec[i],sizeof(IntVec[i]),1,fh1);
  }
  fwrite(&LastMCB,sizeof(LastMCB),1,fh1); /* 写最后一个MCB段地址 */
  fwrite(&LastXMSHdl,sizeof(LastXMSHdl),1,fh1); /* 写第一个XMS自由句柄 */
  fclose(fh1); /* 关闭文件 */
  printf("Ok! Current status of memory has been saved.\n");
}

/* 释放基本内存 */
int ReleaseBaseMemory(LastMCB)
int LastMCB;
{
  int i;
  asm   xor cx,cx
  asm	mov ah,0x52
  asm	int 0x21
  asm	sub bx,2
  asm	mov ax,es:[bx]
  asm	mov bx,ax
l1:
  asm	mov es,bx
  asm	mov al,byte ptr es:[0]
  asm	cmp al,0x4d
  asm	jne l2
  asm	mov ax,word ptr es:[1] /* 判断环境块是否是后来安装的 */
  asm	cmp ax,LastMCB
  asm	jb l3
  asm	inc bx
  asm	mov es,bx
  asm	mov ah,0x49 /* 释放常规内存 */
  asm	int 0x21
  asm	dec bx
  asm	inc cx /* 累加释放内存块个数 */
l3:
  asm 	mov es,bx
  asm	add bx,es:[3]
  asm	inc bx
  asm	jmp l1
l2:
  asm	mov i,cx
  return i; /* 返回释放内存块个数 */
}

/* 释放扩展内存(XMS) */
void ReleaseXMS(LastXMSHdl)
int LastXMSHdl;
{
  long xms_control;
  asm   mov ax,0x4300
  asm   int 0x2f
  asm	cmp al,0x80
  asm	jnz l3
  asm	mov ax,0x4310
  asm	int 0x2f
  asm	lea di,xms_control
  asm	mov [di],bx
  asm	mov [di+2],es
  asm	mov dx,LastXMSHdl
l4:
  asm 	mov ah,0x0d  /* 解锁XMS块 */
  asm	call dword ptr [di]
  asm	cmp bl,0xa2
  asm	jz l3
  asm	mov ah,0x0a /* 释放内存块 */
  asm	call dword ptr [di]
  asm	add dx,10 /* 下一个XMS块 */
  asm	jmp l4
l3: ;
}

/* 恢复文本显示格式 */
void RestoreVideo()
{
  asm   mov ax,0x40
  asm   push ax
  asm   pop es
  asm   mov al,byte ptr es:[0x49]
  asm   cmp   al,3
  asm   je    l1
  asm   mov ah,0
  asm	mov al,3
  asm	int 0x10
l1:;
}

/* 显示错误信息 */
void DisplayErrorMsg()
{
   printf("First, please execute:\n");
   printf("     RM /B\n");
   help();
   exit();
}

/* 查找内存中是否有第2份COMMAND.COM,有返回1,无返回0 */
unsigned int Has2Command(lastmcb)
unsigned int lastmcb;
{
  unsigned int i;
  asm  xor     ax,ax
  asm  mov     bx,lastmcb
l1:
  asm  mov     es,bx
  asm  mov     dl,byte ptr es:[0]
  asm  cmp     dl,0x4d
  asm  jnz     l2
  asm  mov     dx,word ptr es:[1] /* 是自由内存块? */
  asm  cmp     dx,0
  asm  je      l4                 /* 是转 */
  asm  mov     cx,7
  asm  lea     si,commandname
  asm  mov     di,8
  asm  cld
  asm  repz cmpsb    /* 内存块为COMMAND.COM的程序块 */
  asm  je      l3    /* 是转 */
l4:
  asm  add     bx,es:[3]  /* 下一个内存块的段地址 */
  asm  inc     bx
  asm  jmp     l1
l3:
  asm  mov     ax,1 /* 存在第二份COMMAND.COM */
l2:
  asm  mov     i,ax
  return i;
}

/* 释放内存 */
void ReleaseMemory(filename)
char *filename;
{
  FILE *fh1;
  int i,ErrorNo,LastMCB,LastXMSHdl,IntVec[512];
  char mysign[3];
  fh1 = fopen(filename,"rb");
  fseek(fh1,FSize,SEEK_SET);
  ErrorNo = fread(mysign,3,1,fh1);
  if (!ErrorNo) DisplayErrorMsg();
  if (strcmp(mysign,sign)==0) { /* 查找标志 */
    ErrorNo = fread(IntVec,sizeof(IntVec),1,fh1);
    if (!ErrorNo) DisplayErrorMsg();
    ErrorNo = fread(&LastMCB,sizeof(LastMCB),1,fh1);
    if (!ErrorNo) DisplayErrorMsg();
    ErrorNo = fread(&LastXMSHdl,sizeof(LastXMSHdl),1,fh1);
    if (!ErrorNo) DisplayErrorMsg();
    fclose(fh1);
    if (Has2Command(LastMCB)==1) { /* 有第2份COMMAND.COM吗? */
      printf("There is the second copy of COMMAND.COM in memory.\n");
      printf("Please execute EXIT to remove the copy of COMMAND.COM.\n");
      exit();
      }
    if (LastMCB == FindLastMCB()) { /* 最后的MCB与存储的相等吗? */
       printf("No TSR program is running.\n");
       printf("It's not necessary to release memory.\n");
       exit();
       }
    if (ReleaseBaseMemory(LastMCB)) { /* 释放常规内存 */
	for (i=0;i<512;i++) poke(0,i*2,IntVec[i]);
	ReleaseXMS(LastXMSHdl);
	RestoreVideo();
	printf("OK! Memory is all released.\n");
    }
    else {
      printf("\nPlease BUILD current status of memory, use: \n");
      printf("     RM /B\n");
      printf("You can use RM /R to release memory from nom on.\n");
    }
  }
}

/* 主程序 */
main(argc,argv)
int argc;
char *argv[];
{
  int LastMCB;
  char *temp,ch;
  Copyright();
  if (argc == 2) {
        temp = argv[1];
        ch = toupper(temp[1]); /* 取开关字符 */
        switch (ch) {
	 case  'B' : /* 建立内存当前状态 */
                     SaveIntVec(argv[0]);
                     break;
         case  'R' :  /* 恢复以前存储的内存状态 */
                     ReleaseMemory(argv[0]);
                     break;
	 default: help();
         }
  }
  else help();
  return 0;
}

© 1998-, 董占山, 版权所有。
转载文章请注明出处(www.sunfinedata.com/articles)。