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

  1. [FORMAT "WCOFF"]
  2. [INSTRSET "i486p"]
  3. [BITS 32]
  4. [FILE "api001.nas"]
  5. GLOBAL _api_putchar
  6. [SECTION .text]
  7. _api_putchar: ; void api_putchar(int c);
  8. MOV EDX,1
  9. MOV AL,[ESP+4] ; c
  10. INT 0x40
  11. RET

本次的api002.nas

  1. [FORMAT "WCOFF"]
  2. [INSTRSET "i486p"]
  3. [BITS 32]
  4. [FILE "api002.nas"]
  5. GLOBAL _api_putstr0
  6. [SECTION .text]
  7. _api_putstr0: ; void api_putstr0(char *s);
  8. PUSH EBX
  9. MOV EDX,2
  10. MOV EBX,[ESP+8] ; s
  11. INT 0x40
  12. POP EBX
  13. RET

本次的api003.nas

  1. [FORMAT "WCOFF"]
  2. [INSTRSET "i486p"]
  3. [BITS 32]
  4. [FILE "api003.nas"]
  5. GLOBAL _api_putstr1
  6. [SECTION .text]
  7. _api_putstr1: ; void api_putstr1(char *s, int l);
  8. PUSH EBX
  9. MOV EDX,3
  10. MOV EBX,[ESP+ 8] ; s
  11. MOV ECX,[ESP+12] ; l
  12. INT 0x40
  13. POP EBX
  14. RET

照这样全都写出来的话太浪费纸张了,后面的就省略了哦(全部是从a_nask.nas中原封不动拆出来的)。依此类推,我们将这些函数拆分成api001.nas~api020.nas。

■■■■■

由于hello3.hrb所需要的.obj文件只有api001.obj和api004.obj,因此我们来修改一下Makefile。

修改后的Makefile节选

  1. hello3.bim : hello3.obj api001.obj api004.obj Makefile
  2. $(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节选

  1. a.bim : a.obj api001.obj api004.obj Makefile
  2. $(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj api001.obj api004.obj

然后是beepdown.hrb,它用到了api004、api015、api016、api017、api018和api020。

修改后的Makefile节选

  1. beepdown.bim : beepdown.obj api004.obj api015.obj api016.obj api017.obj\
  2. api018.obj api020.obj Makefile
  3. $(OBJ2BIM) @$(RULEFILE) out:beepdown.bim stack:1k map:beepdown.map \
  4. beepdown.obj api004.obj api015.obj api016.obj api017.obj api018.obj \
  5. api020.obj

■■■■■

剩下也像这样一个一个修改好就可以了,不过笔者还是觉得好麻烦啊。在此之前我们什么都不用想,只要将a_nask.obj连接上去就好了,而现在还要根据程序来确认所使用的API。于是,笔者想了一个偷懒的办法。

其实obj2bim这个连接器有一个功能,如果所指定的.obj文件中的函数并没有被程序所使用,那么这个.obj文件是不会被连接的,所以我们把用不到的.obj文件写进去也没有问题。其实市面上大多数连接器都没有这个功能,只要指定好的.obj文件就都会连接进去,而笔者编写的这个obj2bim则会先判断一下。

利用这一特性我们就可以偷懒了,也就是说,我们可以不管三七二十一,把api001到api020全都写上去。比如说,a.hrb的话可以这样写:

修改后的Makefile节选

  1. a.bim : a.obj api001.obj api002.obj (中略) api019.obj api020.obj Makefile
  2. $(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj api001.obj api002.obj \
  3. (中略) api019.obj api020.obj

虽然这样写很长,不过连接起来并没有什么问题(而且a.hrb也不会因此变大)。长是长了点,不过用文本编辑器的复制粘贴功能还是很快就能搞定的。

在将这一修改应用到所有的程序之前,笔者还是觉得Makefile太长的话不易于理解,因此我们还是想个办法改短一点吧。

本次的Makefile节选

  1. OBJS_API = api001.obj api002.obj (中略) api019.obj api020.obj
  2. a.bim : a.obj $(OBJS_API) Makefile
  3. $(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中删除。