5 优化应用程序的大小(harib24e)
话说,我们的“纸娃娃系统”开发计划已经差不多接近尾声了,第29天和第30天我们主要是来编写一些应用程序的,对于操作系统本身的开发也就只剩下今明两天的时间了。正琢磨着这1天半里还能做点什么的时候,笔者忽然想起操作系统的大小来了。截止到harib24d时的haribote.sys大小为33 331字节,也就是32.5KB。麻雀虽小,五脏俱全,这么小的文件也已经具备了操作系统的基本功能了。
而另一方面,应用程序的大小又如何呢?不看不知道,一看吓一跳,hello3.hrb居然有520字节那么大了,在21.2节的时候明明才只有100字节来着!后来我们也只是将结束应用程序的方式改为了API方式而已,居然会增大到520字节,这也太不可思议了。
究其原因,主要是因为创建hello3.hrb时所引用的a_nask.nas变大了。也就是说,在hello3.hrb中,除了包含像_api_putchar和_api_end这样真正需要用到的函数之外,还包含了像_api_openwin和_api_linewin这些在这个应用程序中根本用不到的函数。
这实在是对空间的浪费,我们得想个办法才行。如果能够只将需要用到的部分包含在可执行文件中,就可以解决这个问题了。
■■■■■
那么我们该怎么办呢?我们可以将这些函数做成不同的.obj文件,将_api_putchar等需要用到的函数和_api_openwin等不需要用到的函数分离开。连接器(Linker,即obj2bim)的功能只是决定是否将.obj文件连接上去,而不会在一个包含多个函数的.obj文件中挑出需要使用的部分,并舍弃不需要使用的部分(这并不是因为obj2bim的功能不够强大,一般的连接器都是这样设计的)。
因此,我们将函数都拆开吧。
本次的api001.nas
[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "api001.nas"]
GLOBAL _api_putchar
[SECTION .text]
_api_putchar: ; void api_putchar(int c);
MOV EDX,1
MOV AL,[ESP+4] ; c
INT 0x40
RET
本次的api002.nas
[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "api002.nas"]
GLOBAL _api_putstr0
[SECTION .text]
_api_putstr0: ; void api_putstr0(char *s);
PUSH EBX
MOV EDX,2
MOV EBX,[ESP+8] ; s
INT 0x40
POP EBX
RET
本次的api003.nas
[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "api003.nas"]
GLOBAL _api_putstr1
[SECTION .text]
_api_putstr1: ; void api_putstr1(char *s, int l);
PUSH EBX
MOV EDX,3
MOV EBX,[ESP+ 8] ; s
MOV ECX,[ESP+12] ; l
INT 0x40
POP EBX
RET
照这样全都写出来的话太浪费纸张了,后面的就省略了哦(全部是从a_nask.nas中原封不动拆出来的)。依此类推,我们将这些函数拆分成api001.nas~api020.nas。
■■■■■
由于hello3.hrb所需要的.obj文件只有api001.obj和api004.obj,因此我们来修改一下Makefile。
修改后的Makefile节选
hello3.bim : hello3.obj api001.obj api004.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:hello3.bim map:hello3.map hello3.obj api001.obj api004.obj
这样一make,hello3.hrb就只有112字节了,减少了408字节,撒花!
我们索性将其他的应用程序也全部重新修改一下吧。首先是a.hrb,它所需的.obj文件也是api001和api004,所以像上面一样修改就可以了。
修改后的Makefile节选
a.bim : a.obj api001.obj api004.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj api001.obj api004.obj
然后是beepdown.hrb,它用到了api004、api015、api016、api017、api018和api020。
修改后的Makefile节选
beepdown.bim : beepdown.obj api004.obj api015.obj api016.obj api017.obj\
api018.obj api020.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:beepdown.bim stack:1k map:beepdown.map \
beepdown.obj api004.obj api015.obj api016.obj api017.obj api018.obj \
api020.obj
■■■■■
剩下也像这样一个一个修改好就可以了,不过笔者还是觉得好麻烦啊。在此之前我们什么都不用想,只要将a_nask.obj连接上去就好了,而现在还要根据程序来确认所使用的API。于是,笔者想了一个偷懒的办法。
其实obj2bim这个连接器有一个功能,如果所指定的.obj文件中的函数并没有被程序所使用,那么这个.obj文件是不会被连接的,所以我们把用不到的.obj文件写进去也没有问题。其实市面上大多数连接器都没有这个功能,只要指定好的.obj文件就都会连接进去,而笔者编写的这个obj2bim则会先判断一下。
利用这一特性我们就可以偷懒了,也就是说,我们可以不管三七二十一,把api001到api020全都写上去。比如说,a.hrb的话可以这样写:
修改后的Makefile节选
a.bim : a.obj api001.obj api002.obj (中略) api019.obj api020.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj api001.obj api002.obj \
(中略) api019.obj api020.obj
虽然这样写很长,不过连接起来并没有什么问题(而且a.hrb也不会因此变大)。长是长了点,不过用文本编辑器的复制粘贴功能还是很快就能搞定的。
在将这一修改应用到所有的程序之前,笔者还是觉得Makefile太长的话不易于理解,因此我们还是想个办法改短一点吧。
本次的Makefile节选
OBJS_API = api001.obj api002.obj (中略) api019.obj api020.obj
a.bim : a.obj $(OBJS_API) Makefile
$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj $(OBJS_API)
这样一来,今后如果追加了api021.obj,我们也只要在Makefile里修改一行代码就可以了。
我们将Makefile中的a_nask.obj全部替换为$(OBJS_API)之后,应用程序果然变小了很多,而且运行起来毫无问题,撒花!
应用程序名 | 大 小 | 备 注 |
---|---|---|
hello.hrb | 37→37 | 没有引用a_nask.obj,因此文件大小不变 |
hello2.hrb | 25→25 | 没有引用a_nask.obj,因此文件大小不变 |
a.hrb | 488→84 | |
hello3.hrb | 520→112 | |
hello4.hrb | 506→102 | |
hello5.hrb | 78→78 | 没有引用a_nask.obj,因此文件大小不变 |
winhelo.hrb | 8024→7640 | |
winhelo2.hrb | 8088→7784 | |
winhelo3.hrb | 587→335 | |
star1.hrb | 566→306 | |
stars.hrb | 652→392 | |
stars2.hrb | 676→452 | |
lines.hrb | 638→406 | |
walk.hrb | 715→487 | |
noodle.hrb | 1969→1773 | |
beepdowm.hrb | 576→224 | |
color.hrb | 670→386 | |
color2.hrb | 796→512 | |
crack7.hrb | 96→96 | 没有引用a_nask.obj,因此文件大小不变 |
在做上表的时候发现,有几个应用程序是无法正常工作的。比如hello.hrb和hello2.hrb并不是用bim2hrb生成的,因此运行时会报hrb文件格式错误。此外,我们现在已经支持了LDT,crack7.hrb也就没什么用了。因此,上面3个文件我们会在harib24f中删除。