-
2004-05-11
Make雕虫技
在一个规模相对较大的项目中,常常许多目录中都有自己的Makefile,负责处理这个目录中的内容。
顶层Makefile中可以使用不同的方法调用子目录中的Makefile,下面是我所知道的两种:
1 进入子目录编译,比如:
cd kernel; make clean;
2 利用Makefile的特性,比如:
$(MAKE) clean -C kernel
Linux的makefile使用了后一种方法。随之而来的是一个有趣的问题。
编写Makefile的时候,我们常常定义一些变量,比如:
CC = gcc基于程序员懒惰的特性,我们当然不想在多个地方定义这个变量,我们期望的效果是顶层定义,底层使用,也就是说,上面的定义只出现在顶层的文件中,至于底层Makefile,我们只是简单$(CC)就可以使用了。
虽然这是一个合理的要求,但如果你只把这个变量定义在顶层的Makefile中,就期望使用前面提到方式的执行子目录的makefile能够如你所愿,那你只有失望的份了。底层Makefile是无法看到顶层Makefile定义的变量,make没有提供这种支持。
要解决这个问题,我知道的也是两种方法:
包含文件
同我们了解的C/C++一样,Makefile中也支持包含文件。只要把定义了变量的文件包含在Makefile中,Makefile就可以看到这个变量定义了。
听了这种说法,性子急的人直接就在底层的Makefile中把顶层的Makefile包含进来。请再给我个说话的机会,不要这么直接,好吗?
我们可以这样做,把变量定义同具体的编译内容分离开来,专门用一个文件存放变量定义,假设这个文件叫Make.rules,用这种方法,我们把原来定义在顶层Makefile中的变量分离了出来,而我们只要对这个Makefile稍做修改,加上一句
include Make.rules
对于底层需要这些变量的Makefile,只要按照目录结构包含Make.rules就可以顺利的工作了。
如果你问为什么不直接包含整个Makefile,那你一定算不上一个好程序员,好程序员怎么能容忍过分的暴露呢?
导出变量
这种方式是在变量定义之后,将它导出来,对于上面CC定义,我们还要额外的写上
export CC
这样,采用前面提到过的方式处理子目录的时候,就可以使用这些变量了。
这是Linux的makefile使用的方式。
同包含文件相比,这种方式有一个缺陷,因为这些变量都是在make执行的过程中定义的,所以如果没有在顶层执行make,那么这些变量实际上是不存在的。换句话说,直接在某个子目录中执行make,很有可能的结果是因为变量没有定义而执行失败。
其实,对于解释我们的问题来说,CC并不是一个很好的例子。
可能你会遇到这样的现象,底层的makefile中并不包含CC的定义,但它依然可能编译代码,我知道你已经准备置疑我前面的结论了。
但在置疑之前请仔细看一下,执行的是我们定义的gcc吗?
真正运行的是cc。为什么会这样?
原因很简单,因为make中有一批预定义的变量,其中就包括CC,它预定义的值就是cc。如果你在应用中定义的变量同预定义的变量具有相同的变量名,那么起作用的将是你定义的值。
用下面这个命令可以了解make有哪些预定义变量:
make -p -
2004-04-07
引导在哪里?
操作系统讲什么?快翻开你那本落满灰尘的操作系统教材看看。
我的答案来自给了Linus开创世纪动力的《Operating Systems: Design and Implementation》(中文版《操作系统:设计与实现》)。除了前面的引言和后面的代码,书中讲到了进程、输入/输出系统、存储器管理、文件系统。不知道你有没有想过这样一个问题,操作系统如何开始自己的旅程。
想当年,我兴致勃勃的准备以“好好学习,天天向上”的态度认认真真的学学操作系统。结果没有几天,我就被这个问题捆住了,久久不得解脱。当我通过各种渠道对操作系统的启动过程有了些许的认识时,另一个问题又冒了出来。既然启动是操作系统必不可少的一部分,那为什么各种操作系统教材对它却如此不屑。
我能想到的答案只有平台差异。不是吗?大家有着各自的机器结构、有着各自的汇编。胆敢涉足此处,千差万别的细节足以让所谓的权威论断碰得头破血流。如果不是听说了Multiboot Specification,我一定会坚持认为自己得到就是标准答案。
在Multiboot Specification出现之前,几乎每个OS都拥有自己的boot loader。也许你知道Boot loader要占据你可爱的硬盘上唯一的MBR(Master Boot Record,主引导扇区),如果每个OS要有自己的boot loader的话,那结果只能是只有一个操作系统可以启动。想像现在这样既装Linux又装Windows显然是不现实的。
如果能够制订一套游戏规则,大家按照统一的方式启动,上面的问题也就迎刃而解了。在众多自由操作系统开发者的共同努力之下,Multiboot Specification应运而生。
可能Multiboot Specification现在还只是在你眼前闪亮的新名词。如果我请出GRUB和LILO两个大名鼎鼎的名字,不知能否让你产生一丝亲切感。它们两个就是遵循Multiboot Specification的boot loader,Multiboot Specification的制订者之一就是GRUB的创始人Erich Boleyn。正是GRUB和LILO这样的boot loader的存在,我们才能过上Linux和Windows和平共处的生活。这样的boot loader替我们完成了本来要由我们自己的工作,对于我们的PC而言,这些工作可能加载内核、切换至保护模式、设置内核参数等等。回头看看我先前的问题。
Multiboot Specification的出现,使得大家可以共享一个boot loader,于是boot loader变不再是操作系统的一部分了。
这是由于这个原因,后来的开发者可以将更多的把精力集中在操作系统内核的开发上,至于那些繁琐的引导细节,我们就当它不存在吧!
也许有人要说,不自己开发boot loader,我如何掌控全局?
即便自己开发boot loader,加载MBR的过程依然由机器完成,我们不可能100%的掌控全局,支持不自己开发boot loader的另外一个理由就是众所周知的“不要重新发明轮子”。自己学习当然例外。这就是开放的力量,没有开放,累傻小子去吧!
说到开放,不得不再说微软几句。尝试过Linux和Windows共存的朋友一定体会过重装Windows找不到Linux的悲惨。原因很简单,Windows的boot loader不符合Multiboot Specification,一旦它抢占了MBR这块高地,其它系统就只能忍气吞声了。
微软霸道,无处不在!







