问题1: 操作系统镜像文件 ucore-img 是如何一步一步生成的(需要比较详细的解释Makefile中的每一条相关命令和命令参数的含义,以及说明命令导致的结果)?
生成ucore-img的代码:
下述代码取自Makefile文件中
UCOREIMG := $(call totarget,ucore.img) //此时通过call函数来实现创建ucore.img的过程,UCOREIMG代表的就是即将生成的ucore.img文件
$(UCOREIMG): $(kernel) $(bootblock) //这里表示ucore-img文件的生成依赖于kernel 和 bootblock
$(V)dd if=/dev/zero of=$@ count=10000 //为UCOREIMG分配一个10000*512字节大小的空间
$(V)dd if=$(bootblock) of=$@ conv=notrunc //将bootblock拷贝到UCOREIMG中,大小为521字节
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc //将kernel拷贝到UCOREIMG中,从文件开头跳过seek个块之后开始拷贝
$(call create_target,ucore.img)
上述的UCOREEIMG文件的生成依赖于kernel和bootblock。下述给出kernel生成的代码以及bootblock生成的代码
创建kernel
# create kernel target
kernel = $(call totarget,kernel)
$(kernel): tools/kernel.ld //生成kernel目标文件需要依赖于kernel的链接配置文件以及tools
$(kernel): $(KOBJS) //kernel的生成还依赖KOBJS
@echo + ld $@ //$@代表目标文件 将以下文件和目标文件链接起来
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) //链接obj/libs/*和obj/kernel/init/*...所有的目标文件,使用kernel.ld做连接器脚本
@$(OBJDUMP) -S $@ > $(call asmfile,kernel) //最终的内核文件去除符号表等信息,并输出符号表信息,汇编文件信息,和输出信息
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)
$(call create_target,kernel)
# -------------------------------------------------------------------
创建bootblock
# create bootblock //为了生成bootblock,首先应生成bootasm.o,bootmain.c和sign
bootfiles = $(call listf_cc,boot) //用boot替换listf_cc里面的变量,将listf_cc的返回值赋给bootfiles
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))
bootblock = $(call totarget,bootblock)
$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign) //生成目标文件bootblock需要依赖于sign和bootfiles
@echo + ld $@ //将以下文件与bootblock连接起来
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock)
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) //移除bootblock中所有的符号和重定位信息
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock)
$(call create_target,bootblock)
# -------------------------------------------------------------------
下面表示在终端中运行make qemu指令编译链接生成ucore.img文件的过程
+ cc kern/init/init.c
+ cc kern/libs/readline.c
+ cc kern/libs/stdio.c
+ cc kern/debug/kdebug.c
+ cc kern/debug/kmonitor.c
+ cc kern/debug/panic.c
+ cc kern/driver/clock.c
+ cc kern/driver/console.c
+ cc kern/driver/intr.c
+ cc kern/driver/picirq.c
+ cc kern/trap/trap.c
+ cc kern/trap/trapentry.S
+ cc kern/trap/vectors.S
+ cc kern/mm/pmm.c
+ cc libs/printfmt.c
+ cc libs/string.c
+ ld bin/kernel
在ubuntu下使用make qemu运行lab1_result指令,出现了上述的代码。cc在这里代表gcc 表示编译源文件。可以看到在生成ucore.img的过程中,首先对kern文件下的一系列.c和.s文件进行了编译。随后的ld表示链接,ld bin/kernel表示将bin以及kern下的所有文件链接起来,最后通过此条命令生成kernel程序
+ cc boot/bootasm.S
+ cc boot/bootmain.c
+ cc tools/sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
+ ld bin/bootblock
通过cc对boot底下的bootasm.S和bootmain.c文件进行了编译,同时也将tools下的sign.c文件也进行了编译,随后通过ld指令,生成了bootblock程序
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0456474 s, 163MB/s
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.00281044 s, 134 kB/s
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
138+1 records in
138+1 records out
70775 bytes (71 kB) copied, 0.000473867 s, 127 MB/s
通过dd指令,将生成的kernel和bootblock的ELF文件拷贝到ucore.img当中,根据拷贝的顺序可以看到,首先是将bootblock拷贝进了ucore.img,然后才是将kernel拷贝进ucore.img,所以可以得出bootblock是引导区,kernel是操作系统内核。
1 编译所有生成bin/kernel所需的文件
2 链接生成bin/kernel
3 编译bootasm.S bootmain.c sign.c
4 根据sign规范生成obj/bootblock.o
5 生成ucore.img
问题二:一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
根据问题1可知通过sign.c文件的操作使得bootblock.o成为一个符合规范的引导扇区,因此查看sign.c的内容
sign.c的部分代码
char buf[512];
memset(buf, 0, sizeof(buf));
FILE *ifp = fopen(argv[1], "rb"); //rb表示读取二进制文件
int size = fread(buf, 1, st.st_size, ifp);
// 文件实际大小需和文件头描述一致
if (size != st.st_size) {
fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size);
return -1;
}
fclose(ifp);
buf[510] = 0x55;
buf[511] = 0xAA;
// 写入结束位
FILE *ofp = fopen(argv[2], "wb+");
size = fwrite(buf, 1, 512, ofp);
if (size != 512) {
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);
return -1;
}
主引导扇区的规则如下:
大小为512字节
多余的空间填0
第510个(倒数第二个)字节是0x55,
第511个(倒数第一个)字节是0xAA。