牛逼人配的:
https://github.com/abcamus/Linux-Kernel
博客:
http://blog.csdn.net/xie0812/article/details/10816059
http://www.cnblogs.com/pengdonglin137/p/5137941.html
http://www.embedu.org/Column/Column904.htm
http://blog.sina.com.cn/s/blog_7fdf9aa70102wa0a.html
http://blog.csdn.net/fengyuwuzu0519/article/details/74101380
http://blog.csdn.net/abcamus/article/details/53424619
问题:
http://blog.csdn.net/r_Jimy/article/details/54730609
分析:
http://blog.csdn.net/zqixiao_09/article/details/50820128
总结部分:
make uImage ---生成内核镜像 /arch/arm/boot/uImag
make dtbs ---生成设备树文件 /arch/arm/boot/dtb/exynos4412-fs4412.dtb
make modules ---把配置值选成M的代码编译生成模块文件。(.ko) 放在对应的源码目录下。
Kconfig 介绍:http://www.cnblogs.com/Daniel-G/archive/2013/08/27/3284791.html
所有配置工具都是读取arch/$(ARCH)/Kconnfig 文件来生成配置界面
CONFIG_LEDS_FS4412=y # 对应的文件被编进内核
CONFIG_LEDS_FS4412=m # 对应的文件被编成模块
CONFIG_LEDS_FS4412 # 对应的文件没有被使用
内核目录结构:http://www.cnblogs.com/holens/p/3792136.html
常用目录:
arch:体系结构相关代码
ipc:进程调度相关代码
mm:内存管理
Documentation:帮助文档
net:网络协议
lib:库
scripts:编译相关脚本工具
tools:编译相关工具
drivers:设备驱动
fs:文件系统实现
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1. 编译过程分析
决定编译哪些文件
Linux内核的编译过程从顶层Makefile开始,然后递归地进入各级子目录调用他们的makefile,分为3个步骤:
a -- 顶层Makefile 决定内核根目录下哪些子目录将被编进内核;
顶层Makefile将这14个子目录分为5类:init-y、divers-y、net-y、libs-y和core-y
b -- arch/$(ARCH)/Makefile 决定arch/$(ARCH)目录下哪些文件、哪些目录将被编进内核;
编译内核时,将依次进入init-y、core-y、libs-y、drivers-y和net-y 所列出的目录中执行它们的Makefile,每个子目录都会生成一个 built-in.o(libs-y所
列目录下,有可能生成lib.a文件)。最后,head-y所表示的文件将和在这些built-in.o、lib.a 一起被连接成内核映像文件 vmlinux。
c -- 各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编程模块(即驱动程序),进入哪些子目录继续调用它们的Makefile。
在配置内核时,生成配置文件.config。内核顶层Makefile使用如下语句间接包含.config 文件,以后就根据.config中定义的各个变量决定编译哪些文件。
怎样编译这些文件
即编译选项、连接选项是什么。这些选项分3类:全局的,适用于整个内核代码树;局部的,仅适用于某个Makefile中的所有文件;个体的,仅适用于某个文件。
全局选项在顶层Makefile和arch/$(ARCH)/Makefile 中定义,这些选项的名称为:CFLAGS、AFLAGS、LDFLAGS、ARFLAGS,他们分别是编译C文件的选项、编译汇编问价你的选项、连接文件的选项、制作库文件的选项。
怎样连接这些文件,它们顺序如何
对于ARM体系,连接脚本就是arch/arm/kernel/vmlinux.lds,它由 arch/arm/kernel/vmlinux/lds.S文件生成(文件名可能变化,反正是某个体系下的lds.S生成的)
总结:
a -- 配置文件.config 中定义了一系列的变量,Makefile将结合它们来决定哪些文件被编进内核、哪些文件被编进模块、涉及哪些子目录;
b -- 顶层Makefile和arch/$(ARCH)/Makefile决定根目录下哪些子目录、arch/$(ARCH) 目录下哪些文件和目录将被编进内核;
c -- 最后,各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被变成模块(即驱动程序),进入哪些子目录继续调用它们的Makefile;
d -- 顶层Nakedfile和arch/$(ARCH)/Makefile设置了可以影响所有文件的编译、连接选项:CFLAGS、AFLAGS、LDFLAGS、ARFLAGS;
e -- 顶层Makefile按照一定的顺序组织文件,根据连接脚本 arch/$(ARCH)/kernel/vmlinux.lds生成内核映像文件vmlinux。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2. 启动流程分析
linux 的过程可以分为两部分:架构/开发板相关的引导过程、后续的通用启动过程。对于uImage、zImage ,它们首先进行自解压得到vmlinux ,然后执行 vmlinux 开始“正常的”启动流程。
引导阶段通常使用汇编语言编写,它首先检查内核是否支持当前架构的处理器,然后检查是否支持当前开发板。通过检查后,就为调用下一阶段的start_kernel函数作准备了。这主要分如下两个步骤:
1)-- 连接内核时使用的虚拟地址,所以要设置页表、使能MMU;
2)调用C 函数 start_kernel 之前的常规工作,包括复制数据段、清除BSS段、调用start_kernel 函数。
以下是对第一部分的分析:
在连接文件中查看函数入口点,内核编译完成后会在arch/arm/kernel/下生成 vmlinux.lds 文件
stext 在 linux/arch/arm/kernel/head.S 中被定义,做为函数入口点,linux/arch/arm/kernel/head.S是linux内核映像解压后执行的第一个文件。
a -- 设定为SVC模式,关闭IRQ、FIQ;
b -- 确定CPU的ID号,判定其是否有效;
c -- 确定machine的ID号,检查合法性;
d -- 检查bootloader传入的参数列表atags的合法性
e -- 创建初始页表
f -- 使能MMU,跳转到start_kernel
a -- 确保处于SVC模式(就是设置CPSR 模式位,并屏蔽中断)
b -- 检查CPU ID 是否匹配
获取ID并放到 r9 寄存器中,调用_lookup_processor_type 函数, 函数主要用来判定内核是否和当前的CPU匹配,如果不匹配,r5寄存器的值应为0,此时会调用 _error_p函数,它用来打印错误信息,即内核和当前的CPU不匹配,此时内核时不能启动的;如果两者匹配,会返回一个描述处理器结构的地址(在r5寄存器中),然后调用下面的函数。
_lookup_processor_type 函数,在arch/arm/kernel/head-common.S 中定义:
就是一个地址转换过程,因为在判定CPU架构时未开启系统的MMU功能,所以均使用物理地址,而内核代码在连接时是以虚拟地址来实现的,因此要想用proc_info_list 结构体,就要先找到proc_info_list 结构的物理地址,这样必须使用上面的转换代码。
proc_info_list 结构体很重要。在Linux 内核映像中定义了很多个proc_info_list 结构,该结构表示的是内核所支持的CPU架构
proc_info_list 结构体 ,这个结构体在 arch/arm/include/asm/procinfo.h 中定义:对于 Cortex-A9 来说,其结构体在文件 arch/arm/mm/proc-v7.S 中初始化:
.section ".proc.info.init"表明了该结构在编译后存放的位置。在链接文件arch/arm/kernel/vmlinux.lds中:
如果CPU ID匹配,在编译内核文件时,会编译 proc-v7.S 这个文件,可以在arch/arm/mm/Makefile 中看到这个文件
c -- 检测 机器ID是否匹配
d -- 检查bootloader传入的参数列表atags的合法性
_vet_atags 函数用于检测参数列表atags的合法性(__vet_atags: 在arch/arm/kernel/head-common.S 中定义:)
内核参数链表的格式和说明可以从内核源代码目录树中的 中找到,参数链表必须以ATAG_CORE 开始,以ATAG_NONE结束。这里的 ATAG_CORE,ATAG_NONE是各个参数的标记,本身是一个32位值,例如:ATAG_CORE=0x54410001。其它的参数标记还包括: ATAG_MEM32 , ATAG_INITRD , ATAG_RAMDISK ,ATAG_COMDLINE 等。每个参数标记就代表一个参数结构体,由各个参数结构体构成了参数链表
e -- 创建初始页表
f -- 使能MMU,跳转到start_kernel
mov pc,
在文件linux/arch/arm/kernel/head-common.S中:
__mmap_switched_data:
__mmap_switched