Kernel Exploring
  • 前言
  • 支持
  • 老司机带你探索内核编译系统
    • 编译出你的第一个内核
    • 内核编译中的小目标
    • 可能是kbuild中最直接的小目标 – help
    • 使用了一个kbuild函数的目标 – cscope
    • 内核中单个.o文件的编译过程
    • 根目录vmlinux的编译过程
    • 启动镜像bzImage的前世今生
    • setup.bin的诞生记
    • 真假vmlinux–由vmlinux.bin揭开的秘密
    • bzImage的全貌
    • kbuild系统浅析
  • 启动时的小秘密
    • INIT_CALLS的秘密
    • 内核参数
  • 内核加载全流程
    • bootloader如何加载bzImage
    • 内核压缩与解压
    • 内核加载的几个阶段
    • 保护模式内核代码赏析
  • 内存管理
    • 内核页表成长记
      • 未解压时的内核页表
      • 内核早期的页表
      • cleanup_highmap之后的页表
      • 映射完整物理地址
      • 启用init_level4_pgt
    • 自底而上话内存
      • e820从硬件获取内存分布
      • 原始内存分配器--memblock
      • 页分配器
        • 寻找页结构体的位置
        • 眼花的页结构体
        • Node-Zone-Page
        • 传说的伙伴系统
        • Compound Page
        • GFP的功效
        • 页分配器的用户们
      • slub分配器
        • slub的理念
        • 图解slub
      • 内存管理的不同粒度
      • 挑战和进化
        • 扩展性的设计和实现
        • 减少竞争 per_cpu_pageset
        • 海量内存
        • 延迟初始化
        • 内存热插拔
        • 连续内存分配器
    • 虚拟内存空间
      • 页表和缺页中断
      • 虚拟地址空间的管家--vma
      • 匿名反向映射的前世今生
      • 图解匿名反向映射
      • THP和mapcount之间的恩恩怨怨
      • 透明大页的玄机
      • NUMA策略
      • numa balance
      • 老版vma
    • 内存的回收再利用
      • 水线
      • Big Picture
      • 手动触发回收
      • Page Fram Reclaim Algorithm
      • swapfile原理使用和演进
    • 内存隔离
      • memcg初始化
      • 限制memcg大小
      • 对memcg记账
    • 通用
      • 常用全局变量
      • 常用转换
    • 测试
      • 功能测试
      • 性能测试
  • 中断和异常
    • 从IDT开始
    • 中断?异常?有什么区别
    • 系统调用的实现
    • 异常向量表的设置
    • 中断向量和中断函数
    • APIC
    • 时钟中断
    • 软中断
    • 中断、软中断、抢占和多处理器
  • 设备模型
    • 总线
    • 驱动
    • 设备
    • 绑定
  • nvdimm初探
    • 使用手册
    • 上帝视角
    • nvdimm_bus
    • nvdimm
    • nd_region
    • nd_namespace_X
    • nd_dax
      • dev_dax
  • KVM
    • 内存虚拟化
      • Qemu内存模型
      • KVM内存管理
  • cgroup
    • 使用cgroup控制进程cpu和内存
    • cgroup文件系统
    • cgroup层次结构
    • cgroup和进程的关联
    • cgroup数据统计
  • 同步机制
    • 内存屏障
    • RCU
  • Trace/Profie/Debug
    • ftrace的使用
    • 探秘ftrace
    • 内核热补丁的黑科技
    • eBPF初探
    • TraceEvent
    • Drgn
  • 内核中的数据结构
    • 双链表
    • 优先级队列
    • 哈希表
    • xarray
    • B树
    • Maple Tree
    • Interval Tree
  • Tools
  • Good To Read
    • 内核自带文档
    • 内存相关
    • 下载社区邮件
Powered by GitBook
On this page

Was this helpful?

Last updated 3 years ago

Was this helpful?

原来安装的是它

编译完内核,接下来要做的就是安装新的内核来使用了。那安装的是哪个文件呢?

关于这个问题,昨天我还真好好看了一下,发现原来安装的不是根目录下的vmlinux,而是bzImage。玩了内核这么多年,一直都以为是根目录下的那个vmlinux是安装时拷贝到/boot/目录下的文件,结果原来不是。真是惭愧惭愧。

我是从make install这个规则开始找下去的。

这个规则在arch/x86/Makefile中,好奇为什么没有在根Makefile里找到。

那实际上真正执行的是在arch/x86/boot/Makefile中这个规则

对应x86架构,在这个install.sh就是arch/x86/boot/install.sh。虽然脚本中有几种安装内核的方式,不过我们只看其中一种也就能确认安装在/boot/目录下的是bzImage而不是vmlinux了。

所以说不看不知道,一看吓一跳。以后不敢说自己懂内核了。

#目标在哪里?

我们在一文中也提到过,bzImage是x86平台下默认的目标之一。但是并没有在根目录的Makefile中发现bzImage目标。 而在根目录的Makefile中的前面部分有

这个是不同的arch会include不同的文件,比如是x86的架构就会include arch/x86/Makefile

打开一看,果不其然

好了,我们终于找到这个bzImage的target了,在x86平台它也是all的一部分。

来看看具体是什么

生成规则

在arch/x86/Makefile中,bzImage具体定义是这么个样子的。

又是一坨这么长的,整的好生心烦。幸好,看到了一个眼熟的$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)。这个不是我们编译具体目标的时候经常看到的么?咱来展开看一眼:

make -f scripts/Makefile.build obj=arch/x86/boot arch/x86/boot/bzImage

是不是觉得好像亲切了一些?

对scripts/Makefile.build文件再补充一点,在该文件的开头处出了包含了scripts/Kbuild.include文件,还包含了一个$(build-file)文件。

先来看一下这个变量的定义:

你猜到了什么不? 没猜到? 再看一眼上面的注释?

对了,这个就是单个目录下符合kbuild系统的规则文件。

你还记得编译一个内核模块时候那个Makefile中定义的obj-y, obj-m么?为什么我们在内核模块中的规则文件只需要定义这几个变量,就可以编译出目标文件和模块呢?原因就是在scripts/Makefile.build中包含了目标目录下的规则文件。

不懂也没关系,看多了日后自然知晓。

先看一下当前包含的文件,这次包含的是arch/x86/boot/Makefile。里面有这么一句。

简单明了,会心一笑。

所以最后的最后是通过tools/build这个用户态工具生成bzImage文件,而依赖的文件是setup.bin和vmlinux.bin。

当然加上绝对路径后,这几个文件分别是,

看看这个build都做了什么~

这是一个用户态的程序,文件在arch/x86/boot/tools/build.c。虽然说main函数的主体结构相对简洁清晰,但实际上包含了不少文件格式相关的检查和动态填充。不少细节我也不是很懂,而且本主题关注的还是bzImage编译过程,所以细节部分就暂且略过。

来看我们能看得明白的,按照相关线索抽取了代码

回忆一下最后build的命令:

拷贝setup.bin

  • 先打开了bzImage,叫dest。

  • 然后读取setup.bin到buf

  • 最后写入bzImage

拷贝vmlinux.bin

  • 先打开了vmlinux.bin

  • 做了一下map

  • 拷贝到了dest指向的bzImage

是不是也挺简单的呢。

一张图显示依赖关系

丑是丑了点,毕竟清晰了些。

心中的疑惑

bzImage的编译过程就告一段落,然而心中的疑惑更多了。作为保存在磁盘上的一个文件,grub又是如何加载?加载到内存的哪个位置?加载时首先执行的是哪条指令?

bzImage的两个组成部分vmlinux.bin和setup.bin又是怎么出现的呢?

路漫漫其修远兮,吾将上下而求索

include $(srctree)/arch/$(SRCARCH)/Makefile
# Default kernel to build
all: bzImage
# KBUILD_IMAGE specify target image being built
KBUILD_IMAGE := $(boot)/bzImage

bzImage: vmlinux
ifeq ($(CONFIG_X86_DECODER_SELFTEST),y)
	$(Q)$(MAKE) $(build)=arch/x86/tools posttest
endif
	$(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)
	$(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot
	$(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@
#The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
quiet_cmd_image = BUILD   $@
cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin \
			       $(obj)/zoffset.h $@

$(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE
    $(call if_changed,image)
    @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'
arch/x86/boot/tools/build
arch/x86/boot/setup.bin
arch/x86/boot/vmlinux.bin
arch/x86/boot/zoffset.h arch/x86/boot/bzImage                                                                              
$(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin \
			       $(obj)/zoffset.h $@
	dest = fopen(argv[4], "w");

	file = fopen(argv[1], "r");
	c = fread(buf, 1, sizeof(buf), file);

	if (fwrite(buf, 1, i, dest) != i)
		die("Writing setup failed");
	fd = open(argv[2], O_RDONLY);

	kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);

	/* Copy the kernel code */
	crc = partial_crc32(kernel, sz, crc);
	if (fwrite(kernel, 1, sz, dest) != sz)
		die("Writing kernel failed");
arch/x86/boot/setup.bin  arch/x86/boot/vmlinux.bin  
               \         /
                \       /
                 bzImage
  1. 老司机带你探索内核编译系统

启动镜像bzImage的前世今生

Previous根目录vmlinux的编译过程Nextsetup.bin的诞生记
  • 原来安装的是它
  • 生成规则
  • 看看这个build都做了什么~
  • 拷贝setup.bin
  • 拷贝vmlinux.bin
  • 一张图显示依赖关系
  • 心中的疑惑
boot := arch/x86/boot
install:
	$(Q)$(MAKE) $(build)=$(boot) $@
install:
	sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(obj)/bzImage \
		System.map "$(INSTALL_PATH)"
cat $2 > $4/vmlinuz
内核编译的小目标