注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

DOS编程技术

讨论在纯DOS下的编程技术

 
 
 

日志

 
 
关于我

1984年大学毕业,1985年底有机会开始接触PC机,1986年开始在PC机上做开发工作,曾接触过MS-DOS、CP/M、UNIX、VMS、LINUX、iRMX等众多的操作系统并在上面从事技术开发,擅长做底层与硬件相关的软件开发,目前主要在DOS和LINUX平台下工作,主要从事软件,在硬件开发上也有一定造诣,亦有在8051系列、6502系列(凌阳)、z80系列、ARM、X86等各类平台下开发软硬件的经历。更详细情况可以参考http://resume.whowin.net

网易考拉推荐

DJGPP和保护模式  

2009-01-22 22:38:03|  分类: 保护模式 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    下面的这部分内容,基本上翻译自DJGPP的文档,主要是说明DJGPP怎么让程序运行在保护模式下的,目前我们使用的DJGPP都在2.0版本以上,本文中还有多处提到了DPMI,有关DPMI的规范可以在下面网址下载:   

    http://blog.whowin.net/specification/dpmispec.pdf(2017年3月17日注:链接已修复)

DOS不能工作在保护模式

    让CPU工作在保护模式很容易,但CPU工作在保护模式时你无法调用DOS和BIOS服务,为什么呢?因为DOS和BIOS的代码是按照在实模式下运行的方式写的,不符合保护模式程序的规范,比如,在实模式下,DOS下的程序代码可以把任意数值放到段寄存器(CS、DS、ES、SS)中,只要不超过64K就可以,但在保护模式下,段寄存器只能放一个已经存在的selector的值,任何其它的值都将引起一个“General Protection Fault”错误异常

    所以,如果程序让CPU进入保护模式,并且调用DOS服务,比如打印一行信息,将马上使系统崩溃,如果你不明白这一点,恐怕连一个最简单的“Hello World”程序都写不出来。

    更糟糕的是,虽然应用程序不能调用DOS和BIOS服务,但DOS和BIOS必须要运行,比如时钟芯片产生的硬件中断,每秒18.2次的时钟中断等,时钟中断是BIOS的一部分,工作在实模式。

    所以,哪怕程序不调用任何实模式代码,一些异步的系统事件仍然会发生,机器仍然会很快崩溃,所以,要在DOS下进入保护模式,必须首先解决DOS/BIOS和保护模式之间的这种冲突。

DOS Extender允许DOS和保护模式共存

    如果你不想写一个保护模式下的操作系统来完全取代DOS和BIOS,解决冲突的办法是在你的应用程序和DOS/BIOS之间加入一个软件层,这个软件层可以视情况让CPU在实模式和保护模式之间切换,这个软件层叫做DOS Extender。

    在DOS Extender下,当保护模式下的程序要调用实模式下的服务时,Extender给这个调用设置一个陷阱,把CPU切换到实模式,再重新发出这个调用,等待其完成调用,然户再切换回保护模式,并返回到那个调用实模式代码的应用程序中,像时钟中断、键盘这样的硬件中断,也会被Extender设置一个陷阱,并产生保护模式到实模式的切换和返回。

    你可能会想,这种方式会使应用程序运行的很慢,然而在实际应用中,大多数程序并不会频繁地调用OS的服务,即使调用很多,由于大多数的OS服务都是存取外部设备,比如硬盘,而这些设备的速度比起CPU而言是非常慢的,所以很少有人注意到模式切换上的系统开销。

DJGPP v1.Xgo32 Extender

    在DJGPP v1.x中使用的go32程序,就是这样一个DOS Extender,每个程序启动时都会自动地装载go32,go32除了完成DOS Extender的任务外,还接管下面一些与DJGPP相关的任务。

  • 装载应用程序,并运行做好准备
    由于DJGPP的执行文件使用COFF格式,DOS看不懂这种格式,go32负责读取 COFF头并初始化代码、数据和其它文件头中的分段
  • 提供UNIX形式的命令行扩展
  • 保护模式下浮点运算仿真
  • 图形支持

    go32中有一些特殊的代码,使其能够适应各种各样的进入保护模式的方法并管理扩展存储器,所以他可以工作在任何DOS配置下,但这也使其产生一些不能忽视的缺陷,就是Extender必须被装入常规内存,并且每个程序实例大约要占用130KB的内存,大多数情况下DOS启动以后都会有500-600K剩余的常规内存,这意味着一个DJGPP程序只能有3-4级的嵌套。这是一个很严重的限制,DJGPP v2.x已经解决了这个问题。

DJGPP v2.XDPMI服务

    DJGPP v2.x放弃了Extender,取而代之的是需要一个已经运行的DPMI服务,DPMIDOS Protected-Mode Interface的缩写,是一个特殊的API,它允许保护模式的应用程序在DOS的上层运行,他定义了一些函数,使保护模式下的程序(称为DPMI客户)可以做一些诸如:进入保护模式、分配内存和段描述符、调用实模式的服务、连接中断等等,许多使用Intel CPU的操作系统都有DPMI服务,包括windows的所有版本,OS/2,以及LINUX DOS仿真器都是著名的例子,还有一些持有专利的DOS下的DPMI服务器,通常和DOS的内存管理器捆绑在一起,比如QEMM和386MAXFreeDOS也包含有一个DPMI服务器作为缺省设置的一部分,对于那些没有DPMI服务器的系统,DJGPP v2.x提供一个免费的DPMI服务器,叫做CWSDPMI,CWSDPMI很多地方使用了go32的代码,DJGPP启动代码检查DPMI服务,如果没有,将自动搜索并加载cwsdpmi.exe----CWSDPMI服务器。

    DPMI服务器(又称为DPMI HOST)可以解决运行在DOS上层的保护模式程序的大部分问题,余下的那些在1.x版本中由go32完成的函数,在v2.x中被DJGPP的启动代码接管并放在低级函数库中,下面简短地说一下DJGPP启动代码的两个特点。

DJGPP v2.x的启动代码

    DJGPP v2.x启动代码包括两部分:短小的装载程序和库启动模块,前者是由汇编语言写成,是一段有特殊作用的汇编程序,叫做djasm,它是一段16-bit的DOS可执行程序,这个短小的装载程序会被链接到每一个DJGPP程序中,是唯一一部分可以被DOS识别的部分,其余部分----COFF可执行文档----在DOS看来只是一些奇怪的数据。

    第二部分是一个库模块,它包括许多模块,有些用C写成,有些用汇编写成,当装载程序把程序调入并初始化完成后,这里就是COFF格式程序的入口点。

    装载程序完成以下工作:

  • 为传输缓冲区申请内存
    这个缓冲区用于在DOS服务和程序之间传送数据。
  • 检查是否DPMI服务已经运行
    以下两种情况说明DPMI已经启动
    (1)有一个常驻内存的DPMI服务器,比如windows中内置的DPMI服务器
    (2)当前程序是一个嵌套的DJGPP程序,他的父程序已经启动了CWSDPMI
    如果DPMI服务还没有启动,则装入CWSDPMI。首先在当前目录下搜索cwsdpmi.Exe, 然后到环境变量PATH指定的目录下去查找。
  • 把COFF可执行部分的文件头装入内存
    需要知道要为DJGPP程序申请多少内存
  • 调用DPMI host提供的入口点,把CPU切换到保护模式
    注意:装入程序的其余部分运行在保护模式下
  • 为程序代码和数据申请存储器空间
    通过DPMI的功能调用可以为代码和数据申请段描述符和内存空间,并设置基地址、 界限和权限。
  • 把COFF格式的可执行部分调入内存
    通过DPMI服务调用DOS(主要指文件操作)服务,把代码、数据和BSS节读入上 面申请的内存中,DPMI服务允许你从保护模式下调用实模式下的服务。
  • 跳转到COFF镜像的入口点执行
    这个入口点在上面提到过的库启动模块中。

    下面是库启动代码部分完成的工作

  • 生成一个不受约束的空页
    这将产生一个NULL Pointer dereference的错误,并将触发一个异常处理,程序 会收到SIGSEGV信号,但这个功能并非基本DPMI 0.9规范中的一部分,所以 windows和其它许多有专利的DPMI服务器并不支持这个功能,但CWSDPMI支持这 一功能。
  • 改变申请内存的数据段的大小
    这个听起来简单,但由于DPMI存储器调用的特殊性,实际上是非常复杂的,例如: 它需要把一段实模式下的16-bit代码调入常规内存的缓冲区并运行。
  • 设置程序的堆栈
    DJGPP程序堆栈的缺省大小是512KB,但应用程序或改变设置都可以改变堆栈大小
  • 为存取常规内存申请selector
    与DOS/BIOS函数之间传递数据,或者像视频接口上的显示缓冲区那种使用存储器 映射的设备,许多DOS程序需要存取常规内存,但由于在缺省情况下,常规内存并 没有映射在程序的数据段中,为了存取常规内存,使用了一个特殊的selector---- _dos_ds。
  • 初始化信号管理
    需要链接一些硬件中断,例如:按下CTRL-<C>时产生的SIGINT信号。还有时钟中 断产生的SIGPROF信号等。
  • 拷贝程序的环境变量到environ[]数组中
  • 读出定义了DJGPP附加环境变量的文件
  • 获得并解释命令行参数
  • 如果需要,设置x87 FPU并加载浮点运算仿真器
  • 调用静态构造函数
  • 调用用用程序的主函数

  评论这张
 
阅读(3505)| 评论(3)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018