linux1.0内核代码学习(二)

这节主要是对Makefile进行详细注解:

VERSION = 1    ##版本号

PATCHLEVEL = 0         #补丁号

ALPHA =

 

all: Version zImage 

#all标示创建Makefile所知的最顶层的目标。这里就是Version和zImage。zImage就是引导启动盘映像文件,

#若将其写入软盘就可以使用该软盘引导linux系统了。

#Version用于生成.config和.depend文件,zImage就是要

#生成的核心,Version依赖于dummy,作用是删除tools/version.h和如果系统中没有.config

#和.depend文件的话,帮助生成这两个文件,zImage要生成的映像文件名,1.0版本的核心

#不支持非压缩的核心

 

.EXPORT_ALL_VARIABLES: #导出所有的变量给子目录中的Makefile使用

 

 

CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \

  else if [ -x /bin/bash ]; then echo /bin/bash; \

  else echo sh; fi ; fi)

  

#使用make的shell函数来执行shell程序,使用if语句的-x选

#项来判断程序是否可以执行,BASH为shell的内部变量,在make中使用双$$来引用变量

#

 

# Make "config" the default target if there is no configuration file or

# "depend" the target if there is no top-level dependency information.

#

ifeq (.config,$(wildcard .config))  #如果当前目录下存在.config的文件

include .config              #那么包含.config文件

ifeq (.depend,$(wildcard .depend)) #如果当前目录下存在.depend文件

include .depend                     #则包含.depend文件

else

CONFIGURATION = depend              #否则变量CONFIGURATION赋值为depend

endif

else

CONFIGURATION = config              #否则变量CONFIGURATION赋值为config

endif

 

#$(wildcard PATTERN)是make的函数,

#功能是列出当前目录下所有符合模式“ PATTERN”格式的文件名,

#返回值是空格分割的、存在当前目录下的所有符合模式“ PATTERN”的文件名

 

ifdef CONFIGURATION    #如果定义了CONFIGURATION,则让CONFIGURE=dummy,也就是说让.config 和.depend依赖于dummy

CONFIGURE = dummy      #否则CONFIGURE为空,就表示存在文件.config  .depend

endif

 

#

# ROOT_DEV specifies the default root-device when making the image.

# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case

# the default of FLOPPY is used by 'build'.

#

#ROOT_DEV指定在创建内核映像(zImage)文件时所使用的默认根文件系统所在的设备,

#这可以是软盘(FLOPPY),CURRENT,/dev/xxxx #或者干脆为空,空着时build程序(在tools/目录中)

#就使用默认值FLOPPY

 

#ROOT_DEV = CURRENT

ROOT_DEV = /dev/fd0

 

 

#

# If you want to preset the SVGA mode, uncomment the next line and

# set SVGA_MODE to whatever number you want.

# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.

# The number is the same as you would ordinarily press at bootup.

#

 

SVGA_MODE= -DSVGA_MODE=NORMAL_VGA

 

#

# standard CFLAGS

#

#-Wall打印#所有警告信息,-w关闭警告信息,

#-Wstrict-prototypes#如果函数的声明或定义没有指出参数类型,编译器就发出警告

#-O2包含-O1的优化并增加了不#需要在目标文件大小和执行速度上进行折衷的优化。编译器不执行循环展开以及函数#内联.

#此选项将增加编译时间和目标文件的执行性能

#-fomit-frame-pointer指明对于无需帧指针(Frame-pointer)的函数不要把帧指针

#保留在寄存器中。这样在函数中可以避免对帧指针的操作和维护。-pipe使用管道#代替临时文件。

 

CFLAGS = -Wall -w -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe

 

#-x language filename#设定文档所使用的语言,使后缀名无效,对以后的多个有效.

ifdef CONFIG_CPP

CFLAGS := $(CFLAGS) -x c++ 

endif

 

#高版本的编译器用-mtune=i486

ifdef CONFIG_M486

#CFLAGS := $(CFLAGS) -m486

CFLAGS := $(CFLAGS) -mtune=i486

else

CFLAGS := $(CFLAGS) -m386

endif

 

#

# if you want the ram-disk device, define this to be the

# size in blocks.

#

#如果你想使用RAM盘(RAMDISK)#设备的话就定义块的大小。这里默认RAMDISK盘没有定义

#否则gcc编译时会带有选项-DRAMDISK=512

#RAMDISK = -DRAMDISK=512

#RAMDISK = -DRAMDISK=2048

 

#可以看到既有8086的汇编编译器、连接器也有gnu的汇编器、连接器,说明linux1.0源码中存在两种汇编语言

AS86 =as86 -0 -a #8086汇编编译器和连接器

LD86 =ld86 -0    #-o #生成8086的16位目标程序,-a#生成与gas和gld部分兼容的代码。

 

AS =as   #AS LD HOSTCC#三个变量分别为gnu的汇编器、连接器、C语言编译器

LD =ld

HOSTCC =gcc

CC =gcc -D__KERNEL__

MAKE =make

CPP =$(CC) -E #-E 预处理,预处理之后的代码将送往标准输出

AR =ar #函数库打包程序,可创建静态库.a文档

STRIP =strip   #去掉可执行文件的调试信息

 

#kernel目录、mm目录、fs目录、net目录、ipc目录所产生的目标代码文件。为了方便引用在这里将它们

#用ARCHIVES(#归档文件)#标识符表示。

ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o ipc/ipc.o

 

FILESYSTEMS =fs/filesystems.a   #文件系统库文件

 

#块和字符设备库文件。.a表示该文件是个归档文件,也即包含有许多可执行二进制代码子程序集合的库文件,

#通常是用GNU的ar程序生成。ar是gnu的二进制文件处理程序,用于创建、修改以及从归档文件中抽取文件。

DRIVERS =drivers/block/block.a \

 drivers/char/char.a \

 drivers/net/net.a \

 ibcs/ibcs.o

LIBS =lib/lib.a  #由lib/目录中的文件所编译生成的通用库文件。

SUBDIRS =kernel drivers mm fs net ipc ibcs lib

 

#定义内核头文件路径

#KERNELHDRS =/usr/src/linux/include

KERNELHDRS =/mnt/mywork/linux-1.0/include/

 

#gcc的环境变量C_INCLUDE_PATH(for C header files)和CPLUS_INCLUDE_PATH(for C++ header files)是指明头文件的搜索路径,

#此两个环境变量指明的头文件会在-I指定路径之后,系统默认路径之前进行搜索。

#LIBRARY_PATH指明库搜索路径,此环境变量指明路径会在-L指定路径之后,系统默认路径之前被搜索。

C_INCLUDE_PATH =/mnt/mywork/linux-1.0/include/

 

#如果用户选择了SCSI设备,则这里就会增加这部分定义

ifdef CONFIG_SCSI

DRIVERS := $(DRIVERS) drivers/scsi/scsi.a

endif

 

#是否配置了声卡驱动

ifdef CONFIG_SOUND

DRIVERS := $(DRIVERS) drivers/sound/sound.a

endif

 

#是否配置了数学协处理器

ifdef CONFIG_MATH_EMULATION

DRIVERS := $(DRIVERS) drivers/FPU-emu/math.a

endif

 

#到此为止所定义的变量都会被导出

 

#下面是make老式的隐式后缀规则,该行指示make利用下面的命令将所有的'.c'文件编译生成'.s'

#汇编程序。':'#表示下面是规则的命令。整句表示让gcc采用前面CFLAGS所指定的选项,在适当的编译后不进行汇编就停止(-S),

#从而产生与输入的各个C文件对应的汇编语言形式的代码文件。默认情况下所产生的汇编程序文件是原C文件名去掉'.c'后再

#加上'.s'后缀。'-o'表#示其后是输出文件的形式。'$*.s'(或'$@')是自动目标变量,'$<'#代表第一个先决条件,

#这里即是符合条件'*.c'的文件。

.c.s:

$(CC) $(CFLAGS) -S -o $*.s $<

 

#表示将所有.s汇编程序文件编译成.o目标文件。整句表示使用as编译器将汇编程序编译成.o目标文件。

.s.o:

$(AS) -o $*.o $<  

#$(AS) -c -o $*.o $<

 

#类似上面*.c->*.o目标文件。整句表示使用gcc将C语言文件编译成.o目标文件但不链接。

#-c#表示只编译或汇编,但不进行连接操作。

.c.o:

$(CC) $(CFLAGS) -c -o $*.o $<

 

Version: dummy

rm -f tools/version.h

 

#make config执行此命令,进行内核配置,第一条命令展开为bash Configure < config.in生成.tmpconfig文件

#Configure是可执行脚本文件,config.in是脚本文件的输入文件

#第二条命令使用grep在.tmpconfig文件中搜索字符串CONFIG_SOUND,如果内核中配置有声卡,就进入驱动中sound目录进行

#声卡配置,最后将.tmpconfig 复制为文件.config

config:

$(CONFIG_SHELL) Configure $(OPTS) < config.in

@if grep -s '^CONFIG_SOUND' .tmpconfig ; then \

$(MAKE) -C drivers/sound config; \

else : ; fi

mv .tmpconfig .config

 

#命令展开就是set -e; for i in kernel drivers mm fs net ipc ibcs lib; do make -C $i; done

#就是进入每个子目录中进行编译

#"set -e"#表示之后出现的代码,一旦出现了返回值非零即有错误,整个脚本就会立即退出。

#

linuxsubdirs: dummy

set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done

 

tools/./version.h: tools/version.h

 

#version.h文件的先决条件是dummy和Makefile,dummy的先决条件是存在.config和.depend文件

#第一条命令使用脚本makever.sh生成隐藏文件.version,里面是一个累计数字,然后使用echo命令生成版本文件。

tools/version.h: $(CONFIGURE) Makefile

@./makever.sh

@echo \#define UTS_RELEASE \"$(VERSION).$(PATCHLEVEL)$(ALPHA)\" > tools/version.h

@echo \#define UTS_VERSION \"\#`cat .version` `date`\" >> tools/version.h

@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h

@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h

@echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> tools/version.h

@echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h

 

tools/build: tools/build.c $(CONFIGURE)  #由tools目录下的build.c程序生成执行程序build

$(HOSTCC) $(CFLAGS) -o $@ $<

 

boot/head.o: $(CONFIGURE) boot/head.s  #利用上面的.s.o规则生成head.o目标文件

 

#-traditional尝试支持传统C编译器的某些方面。详见GNU C手册。

boot/head.s: boot/head.S $(CONFIGURE) include/linux/tasks.h

$(CPP) -traditional $< -o $@

 

tools/version.o: tools/version.c tools/version.h

 

init/main.o: $(CONFIGURE) init/main.c

$(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $<

 

#tools目录中的system要由冒号右边所列的元素生成。后续是生成system的命令。

#-Ttext ADDRESS 指定链接生成的文件中代码段存放的内存位置。

#nm tools/zSystem会在标准输出设备(通常是屏幕)上打印出链接印象(link map)信息,即是指由链接程序ld产生的

#目标程序zSystem内存地址映像信息,其中列出了程序段装入到内存中的位置信息,然后通过管道传给grep处理。

#grep pattern [file...] 在文件中搜索所有 pattern 出现的位置, pattern 既可以是要搜索的字符串,也可以是一个正则表达式。

#grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)'即是grep -v '(compiled)|(.o$$)|( a )',-v表示不显示所有匹配的行。

#shell中()中的命令作为一个子shell来执行,这句就表示过滤掉所有包含有内容compiled、.o$$、a(a前后有一个空格)的行。

#sort > System.map表示将grep处理后的内容排序后重定向到System.map文件

#System.map中保存的是内核符号表信息,内核符号表是所有内核符号及其对应地址的一个列表。当内核运行出错时,通过

#System.map文件中的符号表解析,就可以查到一个地址值对应的变量名,或反之。

#通过比较System.map文件和nm tools/zSystem > tmp文件可以进一步的理解grep命令的执行。

tools/system: boot/head.o init/main.o tools/version.o linuxsubdirs

$(LD) $(LDFLAGS) -Ttext 1000 boot/head.o init/main.o tools/version.o \

$(ARCHIVES) \

$(FILESYSTEMS) \

$(DRIVERS) \

$(LIBS) \

-o tools/system

nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \

sort > System.map

 

#下面两条使用8086的连接器和汇编器,也即boot/setup.S使用的是8086汇编语言编写

boot/setup: boot/setup.o

$(LD86) -s -o $@ $<

 

boot/setup.o: boot/setup.s

$(AS86) -o $@ $<

 

#-traditional尝试支持传统C编译器的某些方面。详见GNU C手册。

boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile

$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@

 

##下面两条使用8086的连接器和汇编器,也即boot/bootsect.S使用的是8086汇编语言编写

boot/bootsect: boot/bootsect.o

$(LD86) -s -o $@ $<        #-s选项表示要去除目标文件中的符号信息

 

boot/bootsect.o: boot/bootsect.s

$(AS86) -o $@ $<

 

boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile

$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@

 

zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem

$(MAKE) -C zBoot

#sync强制写入所有需要更新的 buffer 上的数据到硬盘上

zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build

tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage

sync     

zdisk: zImage

dd bs=8192 if=zImage of=/dev/fd0

 

zlilo: $(CONFIGURE) zImage

if [ -f /vmlinuz ]; then mv /vmlinuz /vmlinuz.old; fi

if [ -f /zSystem.map ]; then mv /zSystem.map /zSystem.old; fi

cat zImage > /vmlinuz

cp zSystem.map /

if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi

 

#表示tools/zSystem文件要由冒号右边所列的元素生成。紧接的是生成zSystem的命令。

#最后的> zSystem.map表示需要将zSystem经过处理后重定向到zSystem.map文件中。

tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs

$(LD) $(LDFLAGS) -e startup_32 -Ttext 100000 boot/head.o init/main.o tools/version.o \

$(ARCHIVES) \

$(FILESYSTEMS) \

$(DRIVERS) \

$(LIBS) \

-o tools/zSystem

nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \

sort > zSystem.map

 

#通过在命令行中指定变量SUBDIRS=fs的值只对莫个模块进行编译,fs的命令行展开就是:

#[root@localhost linux_1.0_study]# make fs

#make linuxsubdirs SUBDIRS=fs

#make[1]: Entering directory `/mnt/mywork/linux_1.0_study'

#set -e; for i in fs; do make -C $i; done

#make[2]: Entering directory `/mnt/mywork/linux_1.0_study/fs'

#......................

#make[2]: Leaving directory `/mnt/mywork/linux_1.0_study/fs'

#make[1]: Leaving directory `/mnt/mywork/linux_1.0_study'

#可以清楚的看到只进入fs目录进行编译,后面的lib、mm、ipc、kernel、drivers、net同理

fs: dummy

$(MAKE) linuxsubdirs SUBDIRS=fs

 

lib: dummy

$(MAKE) linuxsubdirs SUBDIRS=lib

 

mm: dummy

$(MAKE) linuxsubdirs SUBDIRS=mm

 

ipc: dummy

$(MAKE) linuxsubdirs SUBDIRS=ipc

 

kernel: dummy

$(MAKE) linuxsubdirs SUBDIRS=kernel

 

drivers: dummy

$(MAKE) linuxsubdirs SUBDIRS=drivers

 

net: dummy

$(MAKE) linuxsubdirs SUBDIRS=net

 

clean:

rm -f kernel/ksyms.lst

rm -f core `find . -name '*.[oas]' -print`

rm -f core `find . -name 'core' -print`

rm -f zImage zSystem.map tools/zSystem tools/system

rm -f Image System.map boot/bootsect boot/setup

rm -f zBoot/zSystem zBoot/xtract zBoot/piggyback

rm -f .tmp* drivers/sound/configure

rm -f init/*.o tools/build boot/*.o tools/*.o

 

mrproper: clean

rm -f include/linux/autoconf.h tools/version.h

rm -f drivers/sound/local.h

rm -f .version .config* config.old

rm -f .depend `find . -name .depend -print`

 

distclean: mrproper

 

backup: mrproper

cd .. && tar cf - linux | gzip -9 > backup.gz

sync

 

#该目标或规则用于产生各文件之间的依赖关系。创建这些依赖关系是为了让make命令用他们来确定是否需要重建一个

#目标对象。比如当某个头文件被改动过后,make就能通过生成的依赖关系,重新编译与该头文件有关的所有*.c文件。

#touch用来修改文件时间戳,或者新建一个不存在的文件

#shell遇到”>”操作符,会判断右边文件是否存在,如果存在就先删除,并且创建新文件。不存在直接创建。 无论左边命令执行是否成功,右边文件都会变为空。

#“>>”操作符,判断右边文件,如果不存在,先创建。以添加方式打开文件,会分配一个文件描述符[不特别指定,默认为1,2]然后,与左边的标准输出(1)或错误输出(2) 绑定。

#第一个for语句对指定目录下(init/)的每一个c文件执行gcc预处理操作,标志-M告诉gcc预处理器输出描述每个目标文件

#相关性的规则,并且这些规则符合make语法。对于每一个源文件,预处理程序会输出一个规则,其结果形式就是相应

#源程序文件的目标文件名加上其依赖关系,即该源文件中包含的所有头文件列表。echo用于加上路径名。将预处理的结果

#添加到临时文件.tmpdepend中。

#同理,第二个for语句也是将tools/目录下的每一个c文件执行gcc预处理操作,结果添加到 .tmpdepend文件的后面。

#第四条命令是进入每一个子目录中,对每一个源文件进行gcc预处理操作,并在相应的目录下面生成.depend依赖文件。

#mv .tmpdepend .depend就是产生顶层目录下的依赖文件。

depend dep:

touch tools/version.h

for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done > .tmpdepend

for i in tools/*.c;do echo -n "tools/";$(CPP) -M $$i;done >> .tmpdepend

set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done

rm -f tools/version.h

mv .tmpdepend .depend

 

#如果不存在.config或者.depend文件,CONFIGURATION变量就会赋值为config或者depend,在这里就会有错误提示

#

ifdef CONFIGURATION

..$(CONFIGURATION):

@echo

@echo "You have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"

@echo

$(MAKE) $(CONFIGURATION)

@echo

@echo "Successful. Try re-making (ignore the error that follows)"

@echo

exit 1

 

dummy: ..$(CONFIGURATION)  #..$(CONFIGURATION)就表示隐藏文件.config或者.depend

 

else

 

dummy:

 

endif

 

#

# Leave these dummy entries for now to tell people that they are going away..

#

lilo:

@echo

@echo Uncompressed kernel images no longer supported. Use

@echo \"make zlilo\" instead.

@echo

@exit 1

 

Image:

@echo

@echo Uncompressed kernel images no longer supported. Use

@echo \"make zImage\" instead.

@echo

@exit 1

 

disk:

@echo

@echo Uncompressed kernel images no longer supported. Use

@echo \"make zdisk\" instead.

@echo

@exit 1

你可能感兴趣的:(linux1.0内核代码学习(二))