在独立环境下做C语言和汇编语言的试验的一些感想

最近一直在为参加 SUN 公司的编程大赛做准备,自己又在准备软件设计师认证,真是忙得一塌糊涂。关于比赛,我主要去负责软件内核的设计,而我本人也想把软件的内核放到单纯的独立环境(所谓独立环境就是指没有任何操作系统环境,就是BIOS经过 POST(Power On Self Test) 之后直接引导进我的环境,类似操作系统kernel)中去,但是,问题出现了,学计算机的人也许都知道,汇编语言是没有可移植性可言的,而 C 有,但是这只局限于代码,同样的程序,在 UNIX 和 Windows 上编译出的结果是大不相同的,也是互不通用的,而汇编语言,在拥有同一指令集的 CPU 的机子上,不同操作系统下则要好得多,换句话说,就是C语言本身有强大的可移植性,但程序没有(这一点在你看过 Windows/DOS EXE 和 UNIX OUT 文件结构后就非常明白了),这就是说在不同环境下都要去编译源代码来获取相应的程序,但是,我所面临的是没有操作系统的独立环境,不要谈编译,连编译器都不能运行,所以,要面临的一个问题是在某一平台开发程序并移植到独立环境中去!这就需要独立代码,所谓独立代码就是指语言无关,只用到语言,而不依赖语言,事实上,没有任何函数可让你依赖,也就是说,有强大库函数支持的 C 语言现在成为了 C 结构的汇编语言(C++/Java/C#/VB 是想都不要想的,这些语言永远不可能实现脱离 Runtime Libary,尤其是 Java 和 VB/C# 这种虚拟机方式的程序)。
也许,与我的平台有关,我做的试验也许并不适用于其它平台。我选择在 Windows 环境下去开发我的程序,因为相比之下我更了解这位老朋友,而其他的很多朋友则选择在 UNIX 或 Linux 下去开发,那么下面我要说的,也许就是和你们无关了。
我千辛万苦找来了号称是 DOS 下最棒的编程工具的 Visual C++ 1.52c(有人用过么?)和 Turbo C++ 3.0(因为 Turbo C/C++ 系列中只有它支持鼠标),以及MASM 6.15 for DOS,这里需要说明的是,开发独立环境程序必须使用 for DOS 的工具,而任何版本的 WIN 程序都将让你痛苦,因为你将永远摆脱不了 Win32 kernel 的折磨,你将永远看不到你的程序像你想象的那样运行在POST之后。之后,我在 VMware Workstation 5.5.3 上搭建起一个平台,不预装任何 OS,然后用 UltraISO 建立了一个 ISO 文件和虚拟机光驱关联,试图通过光盘直接引导进我的程序,如果成功,则说明我的环境搭建成功。然后,我做了一个标准试验:
首先我简单地利用汇编语言操作 BIOS int16 和 int10 标准输入输出中断实现最简单的 getchar() 和 putchar() 函数,并在主函数中调用,这里需要说明的是,我并没有调用通常的 int86() 函数和 stdio.h 也并没有以 main() 函数作为入口函数,因为这样会产生冲突(在这里做个解释,C语言在编译的时候会选择适当模式的 loader 用 Linker 加以连接,这些 loader 是开发工具提供的,它们是用汇编语言编译好并在适当的时候调用 C 语言中的 main() 函数从而把控制权交给 main() 函数,这就是为什么我们的 C 程序全部要从 main() 函数开始执行的原因),然后我自己用汇编语言编写了一个 loader 引导 C 程序,这里要注意的是,.MODEL 一定要设置成 TINY 并且 langtype 要是 C,而且要禁止 MASM6.15 的 EXPR32 特性并且为了防止冲突,我把程序的入口偏移放在 0100h (因为在0000h:0070h是 BIOS 数据区) 然后我使用 VC 的 Linker 将两个目标文件加以连接得到了一个 16 位实模式的 DOS 程序,将它设置成 ISO 文件的引导程序,开机运行,成功进入我的环境!接下来我们开始有变数的去做对照试验,目的是为了说明我之前的观点,库函数和标准C程序在独立环境无效。我使用 stdio.h 中的标准函数 getchar() 和 putchar() 进行我的实验,首先我编写了最简单的程序:

#include <stdio.h>

int  main()
{
    
char i=97
;
    
for(;i<='z';i++
)
    {
        putchar(i);
    }
    putchar(
'/n'
);
    
for
(;;)
        putchar(getchar());
    
return 0
;
}

需要说明的是,我的 VC1.52C 可能存在文件缺失,这段程序无法连接成程序,所以我选择在 TC 下以 TINY 模式编译并连接我的程序(在同样的 Windows 下,这样的变数不会影响结果)得到 TC 所谓 Standard EXE 确有 6KB 之巨,要知道,使用 exe2bin 程序转换的 TINY 模式的程序只有一个程序段,体积应该是非常小的,我刚才试验成功的程序只有 80 字节,而拥有 x86 分区的硬盘 MBR 只不过也只有 446 字节,事实上这应该已经说明了它是不可能成功运行在独立环境的,不过,我还是继续做了,我将它设为引导程序,启动虚拟机,运行失败,从而证明了标准 C 程序在独立环境无效。之后,我把上面程序中 main() 函数改名为 t_main() 函数在 VC 1.52C 中只编译(没有 main() 函数,是不能 Build 的),然后同样与我自己编写的目标文件连接,Linker 发出未定义的外界函数错误:
error L2029: '_putchar' : unresolved external
error L2029: '_getchar' : unresolved external
也就是说,Linker 找不到 putchar() 和 getchar() 的原型。我打开 stdio.h 也并没有发现函数的原型,只是找到了 int __cdecl getchar(void); 和 int __cdecl putchar(int); 这样的定义,这足以说明问题,我们知道,__cdecl 协议是 Windows API 惯用的协议,理论上讲,我们可以猜想库函数在 Windows 中的原型就是 Windows API 函数,至于 getchar() 和 putchar() 关联到哪个系统函数,我们这里不做讨论,但支持我观点的一个论证就是 fopen() 对应 CreateFile()。在没有 OS 的情况下,我们无关联可谈,自然库函数也就形同虚设。
很多时候我总是在听前辈们说起独立环境和独立代码,也见到过不少人们问起能否让 C#/Java/VB 脱离虚拟机,但是现在我才真真正正在体会这些,通过自己的双手去试验,完全可以证明很多东西,也许我上面的试验有纰漏,但是,它已经足以让我们能够去了解计算机上运行的程序需要哪些环境,让我们了解,我们的语言又是基于怎样的一种管理。一个基于 C 的操作系统当然为我们提供了足够的 C 环境,让我们去在 OS 层面之上发挥自己的聪明才智,然而当我们想去深入进 OS 层甚至是底层时,我们才会发现我们能够依靠的越来越少。相反的,当我们走进高层,当我们面对 C/C++/C#/Java/VB 等高级语言的时候我们能使用的已经封装好的函数太多太多,也致使我们很多时候并不能给我们的老朋友更多,相反的,我们却索取了更多,但是我并不希望这样,所以我真真正正想去生活在计算机底层,与我的老朋友面对面的交流。
写在最后:我曾经想去实现 malloc() 和 free() 函数,却发现一个问题,malloc() 函数在寻找可用空间时借助 OS 帮助,所以其原型 API 函数应该调用了 OS 内部内存管理函数,这也是操作系统需要解决的问题之一,而对于我来讲还不知道如何去实现这样的技术,但是有一个想法,那就是去开辟一个很大的结构体数组作为内存空间,并通过我的程序设置结构体里的某些变量将某一段数组设为有用或无用。但是,这样做存在的问题是,如果我的程序不是系统 kernel 就不能开辟太大的空间,否则内存会崩溃,于是,我只能想象中的去使用 DOS 21h 中断的 48h,49h,4ah 功能服务去分配内存(48h:给程序分配一块所需内存块,然后返回该块指针;49h:释放已分配内存块;4ah修改已分配内存块的大小),从而如果我想把我准备参加比赛的程序移植到独立环境,还需要做很多工作,毕竟不只是 malloc() 还有 printf() 等可变参和涉及到转义字符的函数,在没有 stdarg.h 和类型判断函数的情况下一切都要依赖于汇编语言这种我还并不熟悉的语言,但愿 MASM 6.15 提供的 TYPE 伪指令可以帮助我实现类型判断从而我自己可以实现转义字符,但是,可变参数仍然是令我最痛苦的事情。在我继续开发系统曾 kernel 的道路上有多少阴影我还无从知晓,是不是有一天,能在世界某一台计算机上跑起我的操作系统呢?努力中...... 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值