《ucore lab1 exercise1》实验报告
资源
题目:理解通过make生成执行文件的过程
列出本实验各练习中对应的OS原理的知识点,并说明本实验中的实现部分如何对应和体现了原理中的基本概念和关键知识点。
操作系统镜像文件ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)
一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
解答
题目1的解答
等完成lab1后再回头总结。
题目2的解答
首先具体分析Makefile的执行流程,然后再回答题目所问的ucore.img的生成过程。
设置环境变量
第1~139行大部分是设置环境变量、编译选项等,其中关键是第117行和第136行,分别设置了libs和kern目录下的obj文件名,两者合并即为$(KOBJS)。
第117行:生成libs目录下的obj文件名
第117行语句是
$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,)
,可见是调用了add_files_cc函数,输入参数有2个,第2个是libs(目录名),第1个是调用另一个函数listf_cc的返回值listf_cc函数的定义为
listf_cc = $(call listf,$(1),$(CTYPE))
,可见listf_cc又调用了listf函数,调用时传入的第1个参数为$(1) = $(LIBDIR) = libs
,第2个参数为$(CTYPE) = c S
listf函数的定义为
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%), $(wildcard $(addsuffix $(SLASH)*,$(1))))
,将输入参数代入得:listf = $(filter %.c %.S, libs/*)
,可见此处调用listf的返回结果为libs目录下的所有.c和.S文件。由于lab1的libs目录下只有.h和.c文件,因此最终返回.c文件。这时,第117行语句可化简为
add_files_cc(libs/*.c, libs)
add_files_cc的定义为
add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4))
,结合4可化简为add_files(libs/*.c, gcc, $(CFLAGS), libs)
add_files的定义为
add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5)))
,而do_add_files_to_packet的定义为:
define do_add_files_to_packet
__temp_packet__ := $(call packetname,$(4))
ifeq ($$(origin $$(__temp_packet__)),undefined)
$$(__temp_packet__) :=
endif
__temp_objs__ := $(call toobj,$(1),$(5))
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5))))
$$(__temp_packet__) += $$(__temp_objs__)
endef
packetname的定义为
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
,其中$(OBJPREFIX)=__objs_
,而$(1)=libs
,因此__temp_packet_ = __objs_libs
toobj的定义为
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)), $(addsuffix .o,$(basename $(1))))
,其中$(OBJDIR)=obj, $(SLASH)=/
,而输入参数为$(1)=libs/*.c, $(5)=''
,因此__temp_objs_ = obj/libs/*.o
综上,第117行的最终效果是
__objs_libs = obj/libs/**/*.o
第136行:生成kern目录下的obj文件名
生成过程与第117行类似,不再赘述。第136行的实际效果是__objs_kernel = obj/kern/**/*.o
生成kernel文件
第140~151行是生成kernel文件。由于脚本中的语句往往会引用到前面定义的变量,而前面定义的变量又可能引用到其他文件的变量,为便于分析,下面会将所有相关的语句集中放在一起。
- 第141行设置生成的kernel目标名为
bin/kernel
kernel = $(call totarget,kernel)
totarget = $(addprefix $(BINDIR)$(SLASH),$(1))
BINDIR := bin
SLASH := /
- 第143行指出kernel目标文件需要依赖tools/kernel.ld文件,而kernel.ld文件是一个链接脚本,其中设置了输出的目标文件的入口地址及各个段的一些属性,包括各个段是由输入文件的哪些段组成、各个段的起始地址等。
$(kernel): tools/kernel.ld
- 第145行指出kernel目标文件依赖的obj文件。最终效果为
KOBJS=obj/libs/*.o obj/kern/**/*.o
。
$(kernel): $(KOBJS)
KOBJS = $(call read_packet,kernel libs)
read_packet = $(foreach p,$(call packetname,$(1)),$($(p)))
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
OBJPREFIX := __objs_
- 第146行打印kernel目标文件名
@echo + ld $@
// output: `+ ld bin/kernel`
- 第147行是链接所有生成的obj文件得到kernel文件
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS)
V := @
LD := $(GCCPREFIX)ld
// GCCPREFIX = 'i386-elf-' or ''
// output: ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o obj/kern/libs/stdio.o obj/kern/libs/readline.o obj/kern/debug/panic.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/picirq.o obj/kern/driver/intr.o obj/kern/trap/trap.o obj/kern/trap/vectors.o obj/kern/trap/trapentry.o obj/kern/mm/pmm.o obj/libs/string.o obj/libs/printfmt.o
- 第148行是使用objdump工具对kernel目标文件反汇编,以便后续调试。首先toobj返回obj/kernel.o,然后cgtype返回obj/kernel.asm,所以第148行相当于执行
objdump -S bin/kernel > obj/kernel.asm
,objdump的-S选项是交替显示将C源码和汇编代码。
@$(OBJDUMP) -S $@ > $(call asmfile,kernel)
OBJDUMP := $(GCCPREFIX)objdump
// GCCPREFIX = 'i386-elf-' or ''
asmfile = $(call cgtype,$(call toobj,$(1)),o,asm)
cgtype = $(patsubst %.$(2),%.$(3),$(1))
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\
$(addsuffix .o,$(basename $(1))))
OBJDIR := obj
SLASH := /
- 第149行是使用objdump工具来解析kernel目标文件得到符号表。如果不关注格式处理,实际执行语句等效于
objdump -t bin/kernel > obj/kernel.sym
。
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call sy mfile,kernel)
OBJDUMP := $(GCCPREFIX)objdump
SED := sed
symfile = $(call cgtype,$(call toobj,$(1)),o,sym)
第151行是调用create_target函数:
$(call create_target,kernel)
,而create_target的定义为create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5)))
,可见create_target只是进一步调用了do_create_target的函数:do_create_target(kernel)
do_create_target的定义如下。由于只有一个输入参数,temp_objs为空字符串,并且走的是else分支,因此感觉这里的函数调用是直接返回,啥也没干?
// add packets and objs to target (target, #packes, #objs[, cc, flags])
define do_create_target
__temp_target__ = $(call totarget,$(1))
__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3)
TARGETS += $$(__temp_target__)
ifneq ($(4),)
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
$(V)$(4) $(5) $$^ -o $$@
else
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
endif
endef
生成bootblock
第156行:
bootfiles = $(call listf_cc,boot)
,前面已经知道listf_cc函数是过滤出对应目录下的.c和.S文件,因此bootfiles=boot/\*.c boot/\*.S
第157行:从字面含义也可以看出是编译bootfiles生成.o文件。
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))
cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4)))
define do_cc_compile
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4))))
endef
- cc_template的定义为
// cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
define cc_template
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@echo + cc $$<
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@
ALLOBJS += $$(call toobj,$(1),$(4))
endef
第159行:
bootblock = $(call totarget,bootblock)
,前面已经知道totarget函数是给输入参数增加前缀"bin/",因此bootblock="bin/bootblock"
第161行声明bin/bootblock依赖于obj/boot/*.o 和bin/sign文件:
$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign)
。注意toobj函数的作用是给输入参数增加前缀obj/,并将文件后缀名改为.o第163行链接所有.o文件以生成obj/bootblock.o:
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock)
。这里要注意链接选项中的-e start -Ttext 0x7C00
,大致意思是设置bootblock的入口地址为start标签,而且start标签的地址为0x7C00.(未理解-Ttext的含义)第164行反汇编obj/bootblock.o文件得到obj/bootblock.asm文件:
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)
第165行使用objcopy将obj/bootblock.o转换生成obj/bootblock.out文件,其中-S表示转换时去掉重定位和符号信息:
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
第166行使用bin/sign工具将obj/bootblock.out转换生成bin/bootblock目标文件:
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock)
,从tools/sign.c代码中可知sign工具其实只做了一件事情:将输入文件拷贝到输出文件,控制输出文件的大小为512字节,并将最后两个字节设置为0x55AA(也就是ELF文件的magic number)第168行调用了create_target函数
$(call create_target,bootblock)
,根据上文的分析,由于只有一个输入参数,此处函数调用应该也是直接返回,啥也没干。
生成sign工具
第173行调用了add_files_host函数:
$(call add_files_host,tools/sign.c,sign,sign)
add_files_host的定义为
add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3))
,可见是调用了add_files函数:add_files(tools/sign.c, gcc, $(HOSTCFLAGS), sign, sign)
add_files的定义为
add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5)))
,根据前面的分析,do_add_files_to_packet的作用是生成obj文件,因此这里调用add_files的作用是设置\_\_objs\_sign = obj/sign/tools/sign.o
第174行调用了create_target_host函数:
$(call create_target_host,sign,sign)
create_target_host的定义为
create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS))
,可见是调用了create_target函数:create_target(sign, sign, gcc, $(HOSTCFLAGS))
create_target的定义为
create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5)))
。根据前面的分析,do_create_target的作用是生成目标文件,因此这里调用create_target的作用是生成obj/sign/tools/sign.o
生成ucore.img
第179行设置了ucore.img的目标名:
UCOREIMG := $(call totarget,ucore.img)
,前面已经知道totarget的作用是添加bin/前缀,因此UCOREIMG = bin/ucore.img
第181行指出bin/ucore.img依赖于bin/kernel和bin/bootblock:
$(UCOREIMG): $(kernel) $(bootblock)
第182行:
$(V)dd if=/dev/zero of=$@ count=10000
。这里为bin/ucore.img分配10000个block的内存空间,并全部初始化为0。由于没指定block的大小,因此为默认值512字节,则总大小为5000M,约5G。
备注:在类UNIX 操作系统中, /dev/zero 是一个特殊的文件,当你读它的时候,它会提供无限的空字符(NULL, ASCII NUL, 0x00)。其中的一个典型用法是用它提供的字符流来覆盖信息,另一个常见用法是产生一个特定大小的空白文件。BSD就是通过mmap把/dev/zero映射到虚地址空间实现共享内存的。可以使用mmap将/dev/zero映射到一个虚拟的内存空间,这个操作的效果等同于使用一段匿名的内存(没有和任何文件相关)。
第183行:
$(V)dd if=$(bootblock) of=$@ conv=notrunc
。这里将bin/bootblock复制到bin/ucore.img第184行:
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc
。继续将bin/kernel复制到bin/ucore.img,这里使用了选项seek=1
,意思是:复制时跳过bin/ucore.img的第一个block,从第2个block也就是第512个字节后面开始拷贝bin/kernel的内容。原因是显然的:ucore.img的第1个block已经用来保存bootblock的内容了。第186行:
$(call create_target,ucore.img)
,由于只有一个输入参数,因此这里会直接返回。
总结ucore.img的生成过程
编译libs和kern目录下所有的.c和.S文件,生成.o文件,并链接得到bin/kernel文件
编译boot目录下所有的.c和.S文件,生成.o文件,并链接得到bin/bootblock.out文件
编译tools/sign.c文件,得到bin/sign文件
利用bin/sign工具将bin/bootblock.out文件转化为512字节的bin/bootblock文件,并将bin/bootblock的最后两个字节设置为0x55AA
为bin/ucore.img分配5000MB的内存空间,并将bin/bootblock复制到bin/ucore.img的第一个block,紧接着将bin/kernel复制到bin/ucore.img第二个block开始的位置
题目3的解答
问题: 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
答:
- 大小为512字节
- 最后两个字节为0x55AA
《ucore lab1 exercise1》实验报告的更多相关文章
- [操作系统实验lab3]实验报告
[感受] 这次操作系统实验感觉还是比较难的,除了因为助教老师笔误引发的2个错误外,还有一些关键性的理解的地方感觉还没有很到位,这些天一直在不断地消化.理解Lab3里的内容,到现在感觉比Lab2里面所蕴 ...
- Ucore lab1实验报告
练习一 Makefile 1.1 OS镜像文件ucore.img 是如何一步步生成的? + cc kern/init/init.c + cc kern/libs/readline.c + cc ker ...
- ucore操作系统学习(三) ucore lab3虚拟内存管理分析
1. ucore lab3介绍 虚拟内存介绍 在目前的硬件体系结构中,程序要想在计算机中运行,必须先加载至物理主存中.在支持多道程序运行的系统上,我们想要让包括操作系统内核在内的各种程序能并发的执行, ...
- 《ucore lab3》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1:给未被映射的地址映射上物理页 题目 完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页.设置访问权限的时候需 ...
- 《ucore lab1 exercise5》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 题目:实现函数调用堆栈跟踪函数 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_s ...
- 《ucore lab8》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1: 完成读文件操作的实现(需要编码) 题目 首先了解打开文件的处理流程,然后参考本实验后续的文件读写操作的过程分析,编写在sfs_inod ...
- 《ucore lab7》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1: 理解内核级信号量的实现和基于内核级信号量的哲学家就餐问题(不需要编码) 题目 完成练习0后,建议大家比较一下(可用meld等文件dif ...
- 《ucore lab6》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1: 使用 Round Robin 调度算法(不需要编码) 题目 完成练习0后,建议大家比较一下(可用kdiff3等文件比较软件) 个人完成 ...
- 《ucore lab5》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1: 加载应用程序并执行(需要编码) 题目 do_execv函数调用load_icode(位于kern/process/proc.c中) 来 ...
- 《ucore lab4》实验报告
资源 ucore在线实验指导书 我的ucore实验代码 练习1:分配并初始化一个进程控制块 题目 alloc_proc函数(位于kern/process/proc.c中) 负责分配并返回一个新的str ...
随机推荐
- 第三章 基本的bash shell命令
1.硬链接:等同于引用了原文件,并未产生新的文件,不同的硬链接共用一个inode 2.符号链接:创建的是一个新文件,新文件指向原文件,因为是不同的文件,所以有不同的inode
- P3313 [SDOI2014]旅行——树链剖分+线段树(动态开点?)
P3313 [SDOI2014]旅行 一棵树,其中的点分类,点有权值,在一条链上找到一类点中的最大值或总和: 树链剖分把树变成链: 把每个宗教单开一个线段树,维护区间总和和最大值: 宗教很多,需要动态 ...
- sonca排除不扫描文件
在pom.xml文件中的<properties>标签下加上<sonar.exclusions>XXX</sonar.exclusions>标签,如下 <pro ...
- OF1.7中的p_rgh【翻译】
翻译自:CFD-online 帖子地址:http://www.cfd-online.com/Forums/openfoam-solving/80454-p_rgh-1-7-a.html stawrog ...
- faster-rcnn CUDA8.0编译错误
之前编译Faster-RCNN的时候用的都是CUDA7.5,最近换了机器,变成了CUDA8.0,果然编译出现错误了…… 参考下面这篇博客解决了问题: http://blog.csdn.net/kexi ...
- mysql和mssql数据库快速创建表格 五
* into testAAA FROM tbl_User --sqlserver方法一复制表结构 select * into testAAA FROM tbl_User --sqlserver复制表结 ...
- 最简单的SpringAop 小案例
网盘下载地址: 链接:https://pan.baidu.com/s/1Z-em-1ouWyXeMP3JW0IbCg 提取码:0o4o 1.目录结构: 2.配置文件 applicationCo ...
- Co-Clustering_Reproducing
调包一时爽,复现马上躺. Co-Clustering 注意右上角的:"Edit on GitHub",一开始疯狂吐槽没有源码,复现得非常难受,今天刚做完GM05中Algotirhm ...
- Java 关于日期加一天(日期往后多一天)
1.原来Java的日期添加不像.NET的.Add: import java.util.Date ; Date date=new Date();//取时间System.out.println(dat ...
- python 设计模式之访问者模式
写在前面 设计模式是经过总结.优化的,对我们经常会碰到的一些编程问题的可重用解决方案.一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码.反之,设计模式更为高级,它是一种必须在特定情形下实现 ...