1、1、内核启动过程分析前的准备
1、拿到一个内核源码时,先目录下的无用文件删除
2、建立SI工程
3、makefile
(1)makefile中不详细的去分析,几个关键的地方,makefile开始部分是kernel的版本号,这个版本号比较重要,因为在模块化驱动安装时会需要用到,要注意会查,会改,版本号在makefile中,改直接改的就行
(2)kernel顶层的makefile中定义的两个变量很重要,一个是ARCH,一个CROSS,ARCH表示我们当前的配置编译路径,如果我们的ARCH =ARM,那么就会去arch/arm/目录下去操作,CROSS_COMPINE这个变量用来指定我们的交叉编译工具链的路径和前缀。
(3)CROSS_COMPINE=XXX,ARCH =XXX,和O=XXX,在kernel顶层的makefile中都可以通过命令行make的时候都可以进行给这些传参,make O =XXX,就是将make后生成的东西全部放在了XXX这个指定的目录下
4、链接脚本的分析
(1)分析链接脚本的目的就是为了找到整个程序的入口(enter),kernel的链接脚本并不是直接提供的,而是提供了一个汇编文件vmlinux.lds.S(arch/arm/kernel/vmlinux.lds.S),然后在编译的时候去编译这个汇编文件,去得到一个真正的链接脚本vmlinx.lds。
思考:为什么Linux kernel中不直接提供链接脚本vmlinux.lds而是提供了一个汇编文件vmlinux.lds.S,用这个汇编文件去生成这个vmlinux.lds链接脚本呢?
猜测:这个.lds文件中只能写死,不能条件编译,不能灵活的处理,但是我们kernel中有时候链接脚本确实有条件编译的需求,而因为链接脚本的.lds格式又不能支持这种,所以我们kernel的工作者就找了个方法,就是把vmlinux.lds写成了一个汇编格式,完了再汇编器编译处理的时候,顺便把脚本给生成了,得到了一个经过条件编译后的生成的vmlinux.lds.S链接脚本。
(2)根据链接脚本,或者这个vmlinux.lds.S文件都可以找到程序的入口,因为程序的入口就enter这个标号后面的东西的位置嘛,所以在SI中搜索这个标号和后标号后面的东西就能找到程序的入口了,搜索后发现有两个一个是head.S一个是head-nommu.S文件。
(3)head.S是启用了MMU的情况下的kernel启动文件
1、2、head.S文件的分析
1、内核运行的物理地址与虚拟地址
(1)KERNEL_RAM_VADDR(VADDR 就是virtual address) 定义了内核运行的虚拟地址 0xC0000000 + 0X00008000
(2)KERNEL_RAM_PADDR 这个宏定义了内核运行时的物理地址 0X30000000 + 0X00008000
(3)相关的宏定义的如果在SI中找不到不代表没有,或者没定义,第一可以看.config中有没配置(一般都是以CONFIG_开头的),第二在Linux下进入到kernel的根目录下,用grep “XXX” *-nR 进行搜索
2、内核的真正入口
(1)ENTRY(stext),前面的__HEAD,定义了后面的代码的段属性,属于.head.text,段
(2)内核的起始代码是被解压代码调用的,uboot启动内核时,实际运行的是内核zImage前面的那一段的自解压代码,这段代码将zImage后面的内核解压开后,调用并运行真正的内核代码
关注这个入口前面的注释,可以发现内核的启动是有条件,需要给他传过来三个参数,分别是r0 = 0, r1 = 机器码,r2 = 给内核一些传参的内容的内存地址。
(3)ARM体系中,函数调用时,实际是通过寄存器进行传参的(函数调用时,传参的设计有两种,一种是寄存器传参,一种是栈内存传参),函数的参数一次是 r0 r1 r2 r3....
(4)kernel启动的时候要求是MMU是关闭的,但是我们的硬件是要用物理地址的,但是因为我们的kernel是一个整体(zImage),所以我们把内核只能链接到一个地址中去,因为内核后续是要用虚拟地址的,所以我们在uboot的阶段的时候,就讲内核链接到了虚拟地址中去了,但是内核启动开始的时候MMU是关的,所以head.S中前段的这些在MMU开启之前的这些代码就会很难受,因为你的内核被链接到物理地址中去了,你的MMU又是关的,你的硬件又要运行在物理地址上去,所以这段代码只能是位置无关码,而且其中涉及到使用寄存器的时候,必须使用物理地址。所以__pa()这个宏的意思就是,将以个虚拟地址转换成一个物理地址
3、内核启动的汇编阶段
(1)开始的时候是进制快速中断,中断,设置成SVC特权模式
1、_lookup_processor_type
(1)读取CP15协处理器中的c0寄存器中的CPU ID ,然后调用这个函数来做合法性的检验,如果合法则继续启动,如果不合法则停止启动,转向__error_p启动失败。
(2)这个函数检验CPU id合法的方法是:内核会实现维护一个本内核支持的cpu id数组,这个函数做的任务就是,将从硬件读取到的cpu id号码和内核中维护的那一份cpu id号码进行依次对比,如果没有一个相等的就不合法,如果有一个相等就合法。这么设计也是为了给自己启动的安全性考虑的
2、__lookup_machine_type
(1)本函数校验的是机器码
3、__vet_atags
(1)这个函数的设计理念和上面的两个一样,不同之处是来校验uboot给内核的传参atags格式是否正确,指的是uboot通过tag给内核传的参数,主要是板子的内存分布memtag,uboot的bootargs这个两个
(2)内核认为如果uboot给自己传参的格式不对,就不启动
(3)uboot给内核传参部分的格式和设置值不对,是会导致内核传参不启动的
4、__create_page_tables
(1)这个函数用来建立页表,Linux内核本身被链接到了虚拟地址中去,所以内核想要尽快的建立页表并且开启MMU,使用虚拟地址,不然操作内存时会很难过,但是Linux内核的页表的建立很麻烦,所以Linux内核想了好办法
(2)所以linux内核建立页表分为两步,第一步,先建立一个段式页表,页表以1MB为单位来区分的,(页表项占多少内存呢,1MB来映射,4G空间就需要4096个页表项,一个页表项需要4字节内存空间,所以一共需要16KB内存空间,所以段式页表需要占用16KB的内存)。第二步在去建立以个细页表(以4KB为单位的区分),然后启用使用细页表,废除段式页表
(3)内核启动的早期建立了一个段式页表,并在内核启动的前期使用,内核启动的后期会建议细页表,并启用,等内核工作起来以后就只剩下来了细页表了。
5、__switch_data
(1)这个东西可以理解成一个有函数指针,有数字,有地址的数组
6、__mmap_switched(__switch_data中的)
(1)复制数据段,清BSS段,设置栈,目的是构建C语言运行环境,将机器码,ID,tag传参首地址保存到寄存器中
(2)start_kernel跳转到C语言运行阶段
总结:内核的汇编阶段,就是进行相关的校验 CPU ID ,机器码,tags等进行校验,建立段式页表,MMU开启,复制数据段,清BSS段,设置栈,搭建C语言运行时环境,跳到start_kernel转到C语言运行阶段,
1、3、内核启动的C语言阶段
(1)具体学习方法:
顺着代码的执行顺序,执行路线,抓全局。这是学习的主要路线。
对着内核启动时的打印信息进行分析
2、几条主要的学习路线:
(1)uboot给kernel传参的影响和实现
(2)硬件初始化与驱动加载
(3)内核启动后的结局与归宿
1、4、内核启动的C语言阶段1
1、杂碎
(1)smp, smp就是对称多处理器(其实就是多核心CPU)
(2)lockdep_init,锁定依赖, 内核的一个调试模块,处理自旋锁的死锁问题
(3)cgroup 是内核提供的一种处理进程组的技术
·
·
·
2、内核版本打印信息
(1)printk(KERN_NOTICE "%s", linux_banner); 在kernel/init/main.c中的572行
(2)printk函数是内核中用来从console打印信息的,类似于应用层编程中的printf,内核编程时不能使用标准库函数,因此不能使用printf,其实printk就是内核自己实现的一个printf
(3)printk函数的用法和printf几乎一样,不同的是printk中可以在参数的前面加一个宏来定义消息输出的级别,为什么要有这种级别呢,主要是Linux内核太大了,代码量太大了,printk打印信息太多了,如果所有的printk都能打印出来而不加限制,则最终内核启动后打印出来的信息就是海量的了
(4)为了解决这个打印信息过多,无效信息会淹没有效信息的问题,就是给每个printk添加一个级别,级别定义是0-7,编程的时候要用相应的宏定义,不能直接用数字,分别代表8种输出的重要性级别,0表示最重要,7表示最不重要,我们在printk的时候自己根据自己的消息的重要性,来设置级别。
(5)linux的控制台有一个监测消息级别的过滤显示机制,控制台本身有一个级别的定义,如果你要输出的信息的级别没有控制台的大的话,就不会被输出到控制台上显示控制台只会输出比自己定义的级别高的信息。
(6)Linux banner中的信息是在编译的时候自动生成的,相关的宏可以在用grep "" * -nR去搜索,在已经编译好的内核目录下找
3、setup_arch
(1)CPU架构相关的一些创建过程,这个函数是用来确定我们当前内核的机器(arch,machine)的。因为我们的内核只能运行在一个CPU上,我们要先给这个内核建立以个架构来确立我们的这个内核要运行在什么样的CPU,什么样的开发板上的,给内核确定一个CPU架构,机器码是多少等
(2)setup_processor函数用来查找CPU的ID,并将CPU的信息进行填充等
(3)setup_machine函数传递的参数是机器码,这个机器码machine_arch_type,在include/generated/mach-types.h中的32039-32050行定义,我们配置的架构是CONFIG_SMDKV210,所以我们的机器码分析后查到是2456.
这个函数的功能,就是根据我们给的这个机器码对应的描述符返回一个指针给这个描述符machine_desc类型的结构体指针
。
(4)setup_machine函数中的这个lookup_machine_type函数,才是找这个机器码对应的CPU架构的相关信息的那个machine_desc的结构体指针。内核在建立的时候,就将各种CPU架构的相关信息放在了一个段上,这个段的段属性是.arch.info.init,这里面有一个一个的machine_desc结构体实例,我们在查找的时候,就是根据我们传过来的这个机器码,去这个段中去寻找和我们机器码相匹配的那个,找到这个结构体实例后返回这个结构体的指针给这个machine_desc结构体指针
(5)setup_arch函数中的那个cmdline,就是uboot给内核传参时传递的命令行启动参数,就是uboot中的那个bootargs
@1:default_command_line 这个是一个全局的内链接属性的一个字符数组,这个字符数组的值是CONFIG_CMDLINE,这个宏是在.config文件中的,我们可以make menuconfig的时候去改变他,这个default_command_line是内核默认的命令行参数
@2:内核对cmdline处理的思路是:如果uboot在传从cmdline命令行参数给kernel的时候,如果传递成功了,内核会优先使用uboot传递过来的cmdline命令行参数,如果uboot给kernel传递的这个命令行参数是失败的,则kernel会使用自己默认的那个命令行参数default_command_line,也就是.config文件中配置的那一个CONFIG_CMDLINE,
这个处理思路就是在setup_arch函数中实现的
4、parse_early_param&parse_args
(1)解析cmdline和其他传参,将这cmdline参数解析成一个一个的放在字符数组中
5、杂碎
trap_init设置异常向量表的
mm_init 内存管理模块相关的初始化
sched_init 调度系统的初始化
early_irq_init 中断初始化
console_init 控制台的初始化
6、rest_init
这个函数之前,内核的基本组装已经完成,剩下的一些工作,就比较重要了,放在了一个单独的函数中叫rest_init函数中
start_kernel函数的总结:这个函数做的任务大概上大的方向上就是,内核版本的信息的打印,在放架构信息的那个段上,根据机器码找到属于这个机器码的那个段的地址,是一个结构体类型的,cmdline的处理,uboot给内核传递的命令行参数是正确的就用uboot给的,如果不正确就用内核本身默认的,还有一些内存管理模块,调度系统模块,驱动加载等等一些列的模块的初始化,让kernel大体上可以先工作,最后到了这个rest_init函数
7、rest_init函数的分析
@1:rest_init函数做了那些事情呢?操作系统去哪了?
(1)这个函数调用了kernel_thread函数启动了两个内核线程,分别是:第一个内核线程是kernel_init,第二个是kthreadd
@2:调用schedule()函数,开启了内核的调度系统,从此Linux系统转起来了。
@3:rest_init函数最终调用了一个cpu_idle函数结束了整个内核的启动,也就是说Linux内核最终结束与一个函数cpu_idle,这个函数里面肯定是死循环
@4:简单来说,Linux内核最终的状态是这样的,有事干的时候去做有意义的事情(执行各个任务的进程),实在没活干的时候,就去死循环(空闲进程),实际上这个死循环也可以看成是一个任务
@5:调度系统开启了以后,调度系统会去考评系统中的所有进程,如果有进程的话,调度系统就会终止cpu_idle这个空闲进程,转而去执行有意义的干活的进程,这样操作系统就转起来了
8、什么是内核线程
(1)进程和线程,简单来理解,一个运行的程序就是一个进程,严格说这样是不对的。进程就是任务,就是一个独立的程序,独立的意思是这个程序和别的程序是分开的,这个程序可以被内核单独的调用或者暂停
(2)在Linux系统中线程和进程非常的相似
(3)进程或线程就是一个独立的程序,应用层运行一个程序,就构成了一个用户进程/线程,那么我们在内核中运行一个函数(这个函数其实就是一个程序)就构成了一个内核进程/线程,这个函数运行起来以后将来是可以被内核调度的
(4)所以我们kernel_thread函数运行一个函数,就是把这个函数变成了一个内核线程去运行起来,然后他可以被内核的调度系统去调度(可以暂停他运行别人,也可以暂停别人运行它),说白了就是去调度器注册了一下,以后人家调度的时候回考虑你,实际上我们目前为止注册了三个到调度器上,分别是kernel_init,kthread,cpu_idle
9、进程0、进程1、进程2
(1)截止目前为止,我们一共涉及到三个内核线程/进程,因为我们当前在内核态呢,没有到用户的空间,用户态
(2)操作系统是用一个数字来表示/记录一个进程/线程的,这个数字就被称为这个进程的进程号,号码是从零开始分配的,因此这里涉及到的三个进程,分别是Linux系统的进程0、进程1、进程2
(3)Linux命令行下,可以使用ps命令来查看当前Linux系统中运行的进程情况,ps -aux 查看所有进程
(4)我们Ubuntu下用ps -aux看所有的进程情况,是看不到进程0的,因为进程0不是一个用户进程,而属于内核进程
(5)三个进程,起始进程
进程0:进程0其实就是刚才讲过的idle进程,也就是空闲进程(死循环)。这个进程被称为内核进程
进程1:进程1是kernel_init函数,这个进程被称为init进程
进程2:kthreadd就是进程2,这个进程是Linux的守护进程,维护内核正常运转的
总结:内核的启动过程,最后达到了一个稳定的状态,到了idle这个进程0,内核进程中,也就是一个死循环,但是这个死循环是可以被调度系统打断的,别的进程要工作的时候,调度系统会终止这个死循环,去那个进程去工作。是可以被调度系统调度的。实现宏观上的并行,微观上的串行
1、5、init进程详解1
1.init进程完成了从内核态向用户态的转变
(1)init进程,刚开始的时候,init进程刚开始的时候是内核态的,它属于一个内核线程,然后他自己运行了一个用户态的程序后强行将自己转换成了用户态,因为init进程自己完成了从内核态到用户态的过度,因此后面的其他进程都可以工作在用户态下面了,因为内核是为了运行应用程序,而应用程序是要运行在用户态的,所以init进程1,就承担了这个责任。
(2)init进程工作在内核态下面的时候做了什么?
重点做的事情就是挂载根文件系统并且试图找到在用户态下的那个init程序,init进程要把自己从内核态转变成用户态,就要运行一个用户态下面的应用程序(这个应用程序的名字一般也叫init),要运行着个应用程序就必须要找到这个应用程序,要找到它就必要挂载根文件系统,因为我们所有的应用程序都在我们的文件系统中。
内核源代码中所有的函数都是在内核态下面的,所以我们执行一个函数的时候不能说是执行了一个应用程序,因为真正的执行一个应用程序应该是执行一个在用户态下面的应用程序。应用程序必须不属于内核源代码,这样才能保证自己是用户态,也就是我说我们要执行的那个应用程序init,不在内核中,他是另外提供的,提供者个init应用程序的那个人就是根文件系统,所以Linux内核为什么需要一个根文件系统,就是因为Linux内核需要根文件系统提供的一个应用程序,让自己从内核态转换到用户态。
所以,init进程在内核态下做的事情就是挂载根文件系统,挂载好了之后,就去根文件系统下找到那个init应用程序,完了执行这个用户态的应用程序,让自己从内核态转换到用户态,所以init进程在内核态做的事情的目的就是让自己不在内核态
(3)init进程工作在用户态下面的时候做了什么?
init进程除了在内核态挂载根文件系统进入用户态后,大部分时间都是在用户态下的。
init进程对操作系统的意义在与,其他所有的用户进程都是直接或者间接的派生自init进程,init进程生了后面的所有进程,所以用户太做的事情就是生进程,完了生下来的每个进程开始做自己的事情了。构建了用户交互界面
(4)init进程如何从内核态跳到用户态的?还能回来不了?
init进程在内核态通过一函数kernel_execve来执行一个用户空间下编译链接的应用程序,就跳跃到了用户态了,注意:这个跳跃过程进程号是没有变的,一直是进程1,这个跳跃过程是单向的,去了就回不来了,一旦执行了用户空间的init应用程序转到用户态了,整个操作系统就算真正的运转起来了,以后只能在用户态下面工作了,用户态下想要进入内核态,只能走API这一条路了。
2、init进程构建了用户交互界面
(1)init进程是其他所有用户进程的老祖宗,Linux系统中一个进程的创建是通过父进程创建出来的,所以根据这个理论,只要有了一个父进程,就能生出来一堆进程,我们现在的进程就是进程1
(2)init启动了login进程、命令行进程、shell进程,是比较重要的。形成了我们平时看到的操作系统运行起来后的样子
login进程是用来做用户登录的
命令行进程提供一个命令行的环境
shell进程提供对命令行中命令的解析和执行
shell进程可以启动其他用户进程,命令行和shell进程一旦工作了,用户就可以在命令下通过./xxx来执行其他的应用程序,每一个应用程序的运行就是一个进程
1、6、init进程详解2
1、打开控制台(sys_open这个函数打开的)
(1)Linux系统中,每一个进程都有自己的一个文件描述符表,表中存储的是本进程打开的文件。
(2)Linux系统中,有一个设计理念,就是一切皆是文件,所以设备也是以文件的方式来访问的,我们要访问一个设备就是要打开这个设备对应的文件描述符,譬如:/dev/fb0这个设备文件,就代表LCD显示器设备,/dev/buzzer就代表蜂鸣器设备,dev/console就代表控制台设备
(3)我们打开一个设备对应的文件,就会得到这个设备文件的文件描述符(编号),我们就可以用这个编号来操作我们的设备,这里我们打开了这个设备文件/dev/console,并且将这个文件描述符复制两次,一共得到了三个文件描述符,这三个文件描述符分别0、1、2,文件描述符实质是编号,打开一个依次长一个编号,这三个文件描述符,就是我们的标准输入,标准输出,标准错误。我们在init进程的时候就打开了这三个
(4)进程1打开了三个标准输入输出错误文件,因此后续进程1衍生出来的进程,默认的都有这三个文件描述符,父进程创建子进程的时候,子进程是默认就有父进程的所有文件描述符的,所以0、1、2这三个文件描述符,在后续由init进程衍生出来的进程中都有这三个文件描述符
2、挂载根文件系统
(1)prepare_namespace这个函数中挂载的
(2)根文件系统在哪里?根文件系统的文件系统类型是什么?
uboot传参中的bootargs参数中的root=/dev/mmcblk0p2 rw 就是在告诉内核根文件系统在哪里
mmcblk0p2 意思是根文件系统在MMC设备上的SD/MMC通道0上的那个SD/MMC设备的第二分区上了,rw表示可读可写
uboot传参中的rootfstype=ext3 就是告诉内核根文件系统的文件系统类型是什么
(3)内核如果挂载根文件系统成功的话,串口就会打印出来 VFS: Mounted root (ext3 filesystem) on device 179:2
(4)如果内核启动时,挂载rootfs失败,则后面肯定没有办法执行了,肯定会死,内核中设置了启动失败重启5秒钟
(5)如果挂载根文件系统失败,可能的原因常见的有:你的uboot传参不对,bootargs设置不对。还有可能是你的根文件系统烧录失败(fastboot烧录出错概率不大)。还有就是根文件系统本身制作有问题
3、执行用户态下的进程1用户程序(执行完毕后,操作系统就运行起来了,就到了用户态,想要访问内核态,就只能通过API接口)
(1)上面一旦挂载rootfs成功,则进入这个rootfs中寻找用户的应用程序的init程序,这个程序就是我们用户空间下的进程1,找到后用run_init_process去执行他
(2)如何确定init程序是谁?
方法是,先看uboot传参cmdline中有没有指定,如果有指定先执行cmdline指定的程序,cmdline中的init=/linuxrc这个就是指定根文件系统中哪个程序是init程序,意思就是告诉内核,这个init应用程序,在rootfs根目录下的,目录下的linuxrc就是这个init程序。
如果uboot传参中没有指定那个init=xx或者指定了这个但是执行这个init=xx这个指定的程序失败后,还有备用方案:
第一备用方案:/sbin/init
第二备用方案:/etc/init
第三备用方案:/bin/init
第四备用方案:/bin/sh
如果以上都没有执行成功,就彻底失败了
1、7、cmdline常用参数(对应uboot中的bootargs)
1.格式简介
(1)很多个项目,每个项目之间用空格隔开,每个项目都有项目名和项目值
(2)cmdline参数在内核启动的时候,内核会将cmdline解析分割成一个一个的项目名和项目值的字符串,这些字符串会在次的被解析影响启动的过程
2、root=
(1)这是用来指定根文件系统在哪的
(2)一般格式是root=/dev/xxx 后面的这个xxx表示我们的设备是什么、在哪个通道、用的哪个分区,一般的时候如果我们的设备是nandflash,则是/dev/mtdblock2,为什么是2呢,因为一般我们烧录的时候,我们的0分区放的是uboot,1分区放的是内核,2分区放的根文件系统,如果是inand/sd,则是/dev/mmcblk0p2,0表示是我们的mmc设备在SD/MMC通道0上,p2表示是第二分区。
(3)如果是nfs的rootfs,则一般是/dev/nfs,这种叫网路文件系统
3、rootfstype=
(1)这个是根文件系统的文件系统类型,一般格式是rootfstype=xxx,xxx一般是是jffs2、yaffs2、ext2、ext3、ubi
4、console=
(1)控制台信息声明,如console=ttySAC0,115200 ,表示控制台用的是串口0,波特率是115200
(2)在正常的情况下,console=这个项目,内核在启动的时候会根据这个项目初始化硬件,并且重定位console到一个具体的串口上,就是说,这里写的值,是真的会控制最终硬件的哪个工作,哪个不工作的(串口),所以这个项目可以决定我们的串口是否是够从终端上接受到内核的信息
5、mem=
(1)mem=用来告诉内核当前系统的内存有多少
6、init=
(1)init=用来指定进程1程序的pathname,一般都是init=/linuxrc
7、常见的cmdline介绍
(1)我们在开发的时候,一般常见的就两种
第一种:
console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3
第一种对应的rootfs是在SD/iNand/Nand/norflash等物理存储设备上,如果我们的根文件系统是烧录到了SD/INAND用的是上面的那个一样的,如果是nand可能就是mtdblock2,如果是norflash可能这里也还不一样,但是总的来说是这样的形式。
这种对应产品真正出货时的情况,因为我产品在出货的时候,根文件系统都是烧录好了再物理存储设备上的。
第二种:
root=/dev/nfs nfsroot=192.168.1.141:/root/s3c2440/build_rootfs/aston_rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200
第二种方式对应的文件系统在nfs上,也就是网络文件系统,说白了就是通过网络连接到主机上面的一个文件系统上,这个时候的文件系统不在我们开发板上,在我们主机上搭建的。
这种对应我们在实验室开发产品做调试的时候,因为我们产品还没有出货,所以一般情况下,这个时候我们根文件系统并没有烧录到我们的存储设备上,所以如果我们告诉内核去这个存储设备上找的话,是找不到我们根文件系统的,所以只能是nfs网络文件系统,告诉内核是不在开发板上,是在主机上的。
1、8、内核中架构相关代码简介
1、内核代码基本分为3块
(1)第一目录就是
arch目录下的代码,这个目录下的代码全部都是跟我们的CPU架构有关的代码,这些代码是完全和硬件有关的,非常大
(2)第二个目录就是
drivers目录下的代码,全都是一些硬件的驱动,这些驱动代码是跟CPU无关的,所以可以说是通用的
(3)其他 这些目录下的代码相同点都是和硬件无关,因此在做内核移植和系统开发的时候,这些目录下的代码其实都是不用关注的,最多就是参考他,分析他,不会去改他的
2、架构相关的常用目录名及含义
(1)mach (mach就是machine architectrue)机器的架构。arch/arm这个目录下的一个mach-xxx,就代表一个类machine的定义,这类machine的共同点是都用xxx这个CPU来做主芯片,mach-XXX目录下的一个mach-yyy,就是定义了一个开发板(这个是可以扩展的),这个开发板用的主芯片是XXX,开发板的名字叫做yyy(一个开发板对应一个机器码)
(2)plat
plat是platform的缩写,含义是平台,plat这里可以理解为SOC,就是这个目录下里面都是我们SOC里面一些硬件(内部外设)相关的代码。在内核中和我们SOC内部外设相关的硬件代码就叫做平台设备驱动(不是真正的驱动,是驱动的数据),也就是plat设备
(3)include 是arch/arm/include
这个include里面的所有代码都是架构相关的头文件不是通用的,(Linux内核通用性的头文件全在内核源码树根目录下的include中)