内核的编译:
- make uImage
- 1、在顶层目录下搜索uImage发现找不到,有可能uImage存在于其他的Makefile文件中
- 2、include arch/arm/Makefile
- 3、进入到arch/arm/Makefile中,寻找uImage目标
299 BOOT_TARGETS = zImage Image xipImage bootpImage uImage 304 $(BOOT_TARGETS): vmlinux(顶层目录下的) 305 $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ boot := arch/arm/boot
- 4、进入到arch/arm/boot目录下寻找uImage目标
78 $(obj)/uImage: $(obj)/zImage FORCE 79 @$(check_for_multiple_loadaddr) 80 $(call if_changed,uimage) 81 @$(kecho) ' Image $@ is ready'
- 如果需要生成uImage,必须依赖zImage
- 5、搜索目标zImage
54 $(obj)/zImage: $(obj)/compressed/vmlinux FORCE 55 $(call if_changed,objcopy)
- 6、在arch/arm/boot/compressed/Makefile中寻找vmlinux目标
185 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \ 186 $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \ 187 $(bswapsdi2) FORCE 188 @$(check_for_multiple_zreladdr) 189 $(call if_changed,ld) 190 @$(check_for_bad_syms) // 如果要生成piggy.$(suffix_y).o,需要依赖 $(obj)/piggy.$(suffix_y) // 如果要生成$(obj)/piggy.$(suffix_y),要依赖$(obj)/../Image
- 进入到arch/arm/boot/Makefile,寻找Image目标
47 $(obj)/Image: vmlinux(顶层目录下的) 48 $(call if_changed,objcopy)
- 以Image为源文件,调用了gzip命令生成了piggy.gzip
- ld链接器以5个.o文件为源文件生成了arch/arm/boot/compressed/vmlinux
- 7、返回到arch/arm/boot/Makefile,以arch/arm/boot/compressed/vmlinux调用objcopy命令生成了zImage
- 8、以zImage为源文件调用uimage命令生成uImage文件
- 总结:uImage的生成,顶层目录下生成vmlinux文件,通过vmlinux文件生成Image,Image调用gzip进行压缩最终生成了piggy.gzip,
- 调用ld链接器,以piggy.gzip.o和其它.o文件来进行链接生成了zImage,zImage调用uimage命令生成uImage文件
启动过程:
- 启动过程:涉及到汇编和c,代码都不需要记。我们需要做到的是用什么功能,了解某个部分的代码。
- arch/arm/kernel/head.S内核的最初启动文件
1、设置特权模式并且屏蔽所有中断
- 因为我们需要初始化一些硬件并且调用协处理器指令,所以必须设置特权模式
中断代码还没有设置完成
2、判断u-boot给内核传递的参数是设备树还是tag结构体
- 我们3.14内核在和u-boot2013版本配合使用时默认使用了设备树
3、创建页表
- 虚拟地址和物理地址的关系.如果要理解这个关系必须先了解什么是页目录,页表,还有页。
- 虚拟地址32位,在32位中分成3个部分:10 + 10 + 12
- 页目录 页表 页
- 1024项 1024项 1024项
也就是说我们可以把上面的页目录,页表,页都看成是数组,而虚拟地址分成的三个部分就是这三个数组的下标
- 0x12345678
- 0001001000 1101000101 011001111000
4、使能并且开启MMU
5、进入到init/main.c中执行start_kernel函数
- 执行setup_arch();
- ==>setup_machine_fdt();接收了u-boot传递给内核的设备树地址然后去解析设备树内容
- console_init()控制台初始化函数,如果在它之前调用printk则输入信息会被临时存放到缓存区中
- vfs_caches_init()
- ==>mnt_init();
- ==>sysfs_init()初始化sysfs文件系统,它的作用在驱动中讲
- rest_init()
- ==>kernel_thread(kernel_init,,);
- kernel_init_freeable();
- ==>sys_open();//在应用层中的open调用了sys_open,sys_open()函数最终帮助我们找到了驱动接口
- ==>prepare_namespace();
- ==>mount_root();
- ==> mount_nfs_root()通过nfs服务来挂载rootfs文件夹的
设备树:
- 设备树是干嘛的?设备树是描述硬件信息的。
- 在操作系统内核中驱动、设备、总线
- 总线上可以理解为既挂载了设备文件又挂载了驱动程序。总线帮助我们去匹配驱动和设备。
- 所以在使用总线的前提下我们要写一个驱动程序和一个设备程序。这两种程序都会被最终编译到uImage文件中。
- 设备树命名:.dts(设备树的源文件) .dtb(设备树的二进制文件) .dtsi(设备树的头文件)
vi arch/arm/boot/dts/exynos4412-fs4412.dts
设备树的基本语法:
节点和属性:
- 每个设备树文件都是从根节点开始的。其它的所有节点都必须包含于根节点。
/{
model = "字符串";对平台或芯片的描述语句,这个属性不重要。
compatible = "fs4412,key";在驱动中的某个结构体成员内容也必须是"fs4412,key"
reg = <寄存器首地址1 偏移量1 寄存器首地址2 偏移量2>;
节点@地址{ 为什么某些节点的后面会出现地址?这个地址为了区分同种设备中的不同子设备
};
标号:节点{为什么会有标号?如果后面的设备需要调用当前设备的所有信息的话,只需要调用标号就可以了
};
interrupt-parent = <&gpx0>; 中断父节点,其中gpx0是某个头文件中一个标号,引用标号的方法要加&
interrupts = <中断类型 中断号 中断触发方式>
中断类型:0代表SPI 1代表PPI
中断号:6代表了EINT[6]来索引中断号
触发方式:1上升沿触发 2下降沿触发 4高电平 8低电平
};
根文件系统:
- etc/:
- inittab:每行都有4个域,用:分隔
- 域1:域2:域3:域4
- 在嵌入式中前两个几乎不用.第三个域是一种动作,第四个域是完成具体动作使用到的命令或者脚本
- tmpfs、proc、sysfs都是文件系统类型,其中tmpfs可以被挂载多次,但是proc必须挂载到proc目录下,sysfs必须挂载到/sys目录下
- mount命令只能临时挂载
- fstab文件可以永久挂载