正点原子IMX6ULL_linux内核笔记

linux内核基础知识

  1. 编译Linux内核
    1> 编译内核需要编译环境,故要先在编译服务器Ubuntu上安装lzop库
    2> 将从网上下载的内核压缩包存放到ubuntu中开始编译三部曲
    1. 解压文件
    2. 创建编译脚本文件,给权限
    3. 脚本文件中写入编译命令
    编译命令如下
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean //清理工程,这里会删除.config文件
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig //通过默认配置文件生成初始.config文件和三个软件(fixdep软件,conf软件,bin2c软件)
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig //使用图形化界面来配置内核 ==>通过图形化界面修改.config文件 静默编译生成软件mconf,该软件根据KCONFIG文件形成图形界面
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16 //编译Linux源码 ==>生成内核镜像文件最终版的zImage和设备数文件.dtb
    3> make menuconfig 命令分析; https://blog.csdn.net/qingkongyeyue/article/details/52503313
    这个博客非常好 ***************** 顶******************

  2. linux文件结构中重点文件分析
    1> arch/arm/configs 默认配置文件(xxx_defconfig)的存放路径 arm那个目录对应说明该linux源码的系统架构是arm架构
    2> arch/arm/boot 该命令下保存我们编译出来的Image和zImage镜像文件 (注:我们烧写的linux镜像文件是:zIamge)
    3> arch/arm/mach-imx 保存着LMX系列CPU驱动和初始化文件
    4> block 保存着管理块设备(SD卡,EMMC,NAND等)相关的文件
    5> driver **** 非常关键 ****,这里存放着驱动相关的文件
    6> fs 存放文件系统
    7> init 存放Linux内核驱动时的初始化代码
    8> ipc IPC为进程间通信,该命令下场地在进程间通信的具体实现代码
    9> kernel linux内核代码
    10> lib 存放一些共用库
    11> mm 存放内存管理相关的代码
    12> net 存放网络相关的代码
    13> scripte 存放脚本文件,Linux编译时会用到的很多脚本文件就保存在该目录下
    14> sound 存放音频相关驱动文件,因为音频驱动文件并没有存放到drivers目录中
    15> tools 存放一些编译的时候使用到的工具

    16> .config文件 这个文件里面保存着Linux最终的配置信息,编译linux时会读取此文件中的配置信息,最终根据这些配置信息来选择要编译Linux模块
    17> Kbuild文件 有些Makefile会读取此文件
    18> Kconfig 图形化配置界面的配置文件(图形界面要展示的内容) 来源==> 图形化界面应该依据的KCONFIG文件,应该是arch/框架/ 下的Kconfig文件 arm框架最好在根目录下的Makefile文件中表明 arch ?= arm
    19> Makefile Linux源码的顶层Makefile文件
    20> README 该文件详解了如何编译linux源码以及Linux源码的目录信息

    重要文件:
    include/generated/autoconf.h 在我们make menuconfig时最好保存退出时,除了将配置信息更新到.config外还会将KConfig中的选项以宏的形式保存在autoconf.h文件
    关键点: 内核中的源码都会包含该文件,这就是linux内核中宏定义条件编译怎么方便的原因

  3. 顶层Makefile文件详解
    知识点: uboot中的顶层Makefile文件就是参考了linux中的顶层Makefile文件
    1> 版本号
    在顶层Makefile中记录了该linux内核对应的版本号
    VERSION = 4
    PATCHLEVEL = 1
    SUBLEVEL = 15
    ==> 该linux内核版本号为 4.1.15
    2> 交叉编译工具变量设置
    首先我们会在顶层Makefile中声明系统架构和交叉编译工具链前缀
    ARCH ?= arm 0
    CROSS_COMPILE ?= arm-linux-gnueabihf-

    然后开始定义和交叉编译器应该的变量
    AS	= $(CROSS_COMPILE)as
    LD	= $(CROSS_COMPILE)ld				//链接工具		将n个.o文件链接形成应该elf文件(可执行文件)
    CC	= $(CROSS_COMPILE)gcc				//编译工具		将.s文件编译生成.o文件	
    ... ... ...
    OBJCOPY	=	$(CROSS_COMPILE)objcopy		//格式转换工具	将elf文件转换为.bin文件
    OBJDUMP = 	$(CROSS_COMPILE)objdump		//反汇编工具	对elf文件反汇编生成.dis文件
    
    所以一般执行流程: 例如对led.s文件进行编译
    CC  		led.s --> led.o   
    LD			led.o --> led.elf
    OBJCOPY		led.elf --> led.bin
    OBJDUMP		led.elf	--> led.dis
    
  4. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean 命令
    distclean: mrproper
    @find $(srctree) $(RCS_FIND_IGNORE)
    ( -name ‘.orig’ -o -name '.rej’ -o -name ‘~’
    -o -name '
    .bak’ -o -name ‘##’ -o -name '..orig’
    -o -name ‘..rej’ -o -name '%’ -o -name ‘core’ )
    -type f -print | xargs rm -f

    同uboot一样在这里会删除许多配置文件,关键点这里会删除.config文件
    
  5. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig 命令详解
    1> 会引用arch/arm/Makefile文件 include arch/$(SRCARCH)/Makefile $(SRCARCH):表示系统架构 这里是arm架构
    **** 这个被引用的Makefile中定义了如何编译生成zImage,uImage等文件 ****
    arch/arm/Makefile文件分析
    先调用这里 all: $(KBUILD_IMAGE) $(KBUILD_DTBS) //KBUILD_IMAGE == zImage ;KBUILD_DTBS == dtbs
    |
    然后 zImage是由bzImage转换来的 == bzImage: zImage (Convert bzImage to zImage)
    |
    bzImage的生成的同时还会有这个东西(BOOT_TARGETS )被编译生成: PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS)
    |
    BOOT_TARGETS = zImage Image xipImage bootpImage uImage;
    $(BOOT_TARGETS): vmlinux
    ( Q ) (Q) (Q)(MAKE) ( b u i l d ) = (build)= (build)=(boot) MACHINE=$(MACHINE) ( b o o t ) / (boot)/ (boot)/@
    所以最后会在arch/arm/boot文件下生成 BOOT_TARGETS变量中定义的这些文件
    2> make … imx_v7_defconfig 匹配 %configs
    %config目标的依赖文件会编译生成 fixdep和bin2c软件
    3> %config目标的命令会生成conf软件
    关键点: *** conf软件的作用是:将默认配置文件%defconfig中的配置输出到.config文件中 ****
    知识点: a. mconf软件和conf软件的区别
    mconf软件: 在make menuconfig时被生成,用于根据根目录下的Kconfig文件来构建图形配置界面
    conf软件: 在make ***defconfig时被生成,用于将默认配置文件中的配置输出到.config文件中
    b. uboot和linux执行该make ***defconfig时在生成软件上的区别
    linux比uboot多生成一个软件(bin2c)

  6. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16 的编译流程
    1> 没有指定目标,故编译的目标是默认目标_all ,
    2> 目标_all依赖于all
    3> 目标all依赖于vmlinux
    4> 目标vmlinux依赖 scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
    scripts/link-vmlinux.sh
    vmlinux-deps依赖于: ( h e a d − y ) 、 (head-y) 、 (heady)(init-y)、$(core-y) 、 ( l i b s − y ) 、 (libs-y) 、 (libsy)(drivers-y) 、$(net-y)、arch/arm/kernel/vmlinux.lds
    FORCE:保证FORCE比目标新,故每次都会执行命令来生成新目标
    5> ( h e a d − y ) 、 (head-y) 、 (heady)(init-y)、$(core-y) 、 ( l i b s − y ) 、 (libs-y) 、 (libsy)(drivers-y) 、$(net-y):这些都是子目录下编译生成的 built-in.o文件或者.a库
    6> arch/arm/kernel/vmlinux.lds: 该文件是整个linux的链接脚本文件,在这个链接脚本文件中会调用scripts/link-vmlinux.sh脚本来具体实现将前面所有built-in.o文件和.a库链接生成可执行文件vmlinux
    7> built-in.o文件的生成过程
    1. 在顶层Makefile中定义了 vmlinux-deps的依赖
    sort $(vmlinux-deps)): ( v m l i n u x − d i r s ) ; / / 表 示 对 变 量 v m l i n u x − d i r s 代 表 的 字 符 串 列 表 进 行 排 序 并 去 掉 列 表 在 重 复 的 单 词 , v m l i n u x − d i r s 中 的 字 符 串 列 表 就 是 m a k e 整 个 工 程 中 要 编 译 的 所 有 目 录 来 生 成 b u i l t − i n . o 和 . a 库 2. v m l i n u x − d i r s 依 赖 于 p r e p a r e 和 s c i p t s , 命 令 为 : @ m a k e − f . / s c r i p t s / M a k e f i l e . b u i l d o b j = (vmlinux-dirs) ; //表示对变量vmlinux-dirs代表的字符串列表进行排序并去掉列表在重复的单词 ,vmlinux-dirs中的字符串列表就是make整个工程中要编译的所有目录来生成built-in.o和.a库 2. vmlinux-dirs依赖于prepare和scipts,命令为: @ make -f ./scripts/Makefile.build obj= (vmlinuxdirs);//vmlinuxdirs,vmlinuxdirsmakebuiltin.o.a2.vmlinuxdirspreparescipts,:@makef./scripts/Makefile.buildobj=@
    -f: 用来指定要编译的文件
    obj= @ : 表 示 将 要 编 译 的 所 有 目 录 路 径 传 递 给 . / s c r i p t s / M a k e f i l e . b u i l d 文 件 3. 关 键 点 : 为 什 么 要 传 递 所 有 要 编 译 的 目 录 路 径 , 因 为 我 们 要 将 每 个 目 录 对 应 编 译 好 的 b u i l t − i n . o 文 件 放 在 对 应 目 录 下 m a k e − f . / s c r i p t s / M a k e f i l e . b u i l d o b j = @: 表示将要编译的所有目录路径传递给./scripts/Makefile.build文件 3. 关键点: 为什么要传递所有要编译的目录路径,因为我们要将每个目录对应编译好的built-in.o文件放在对应目录下 make -f ./scripts/Makefile.build obj= @:./scripts/Makefile.build3.:builtin.omakef./scripts/Makefile.buildobj=@ ,默认目标__build
    __build的依赖包含了$(builtin-target)
    builtin-target的值: builtin-target := $(obj)/built-in.o
    而built-in.o文件的由来: 将某个目录下的所有.o文件链接在一起,最终形成该目录对应的built-in.o文件
    8> 编译生成可执行文件vmlinux流程总结;
    将所有要编译的目录下的文件编译形成.o文件,然后通过该目录下的链接脚本将这些.o文件链接生成该目录对应的built-in.o文件,然后在根据linux总链接脚本arch/arm/kernel/vmlinux.lds,将所有built-in.o文件和.a文件链接
    生成可执行文件vmlinux。
    9> 知识点: vmlinux,Image,zIamge,uImage的区别
    vmlinux是ELF格式的文件,而zImage和uImage是内核镜像文件
    vmlinux: ELF文件,编译出来的最原始内核文件,是未压缩的 存放在linux目录下 未压缩 16M
    Image: linux内核镜像文件,Image就是使用objcopy(反汇编)取消vmlinux中的一些其他信息 未压缩 12M
    zIamge: linux内核镜像文件,Image经过gzip压缩后得到zImage 压缩 6M
    uImage; linux内核镜像文件,uImage是在zImage的基础上加上64byte的头信息,这个头信息描述了该镜像文件的类型,加载时间,生成时间,大小等信息,老版本uboot专用的镜像文件 压缩 6M+64byte
    10> 故最后需要将前面生成的原始内核文件vmlinux压缩成zImage文件

  7. linux内核启动流程分析
    1> 要分析一个流程,首先你要找到它的起始地址,故需要通过它的链接脚本文件来确定它的起始地址
    分析Linux内核的链接脚本文件 arch/arm/kernel/vmlinux.lds
    要找入口地址,找链接脚本中的ENTER标签即可, ENTER(stext) 表明linux内核的入口地址为stext,stext定义在文件arch/arm/kernel/head.S中
    a. 确保CPU的工作模式为SVC模式
    b. 关闭所有的中断 //在uboot启动linux内核时也有这个步骤
    c. 读处理器ID,将ID值保存在r9寄存器中
    d. 检查当前系统是否支持此CPU,如果支持就获取该cpu对应的procinfo信息,并将procinfo信息保存到r5寄存器中
    知识点1: linux内核中将每种处理器都抽象为一个proc_info_list结构体,即每种CPU对应一个procinfo,而proc_info_list中的成员包括cpu ID,故步骤d中可根据CPU ID找到该CPU对应的peocinfo信息,有找到代表支持,没找到就是不支持
    e. 验证设备树文件的合法性 //这个设备树文件是uboot启动linux时传递进来的 83000000
    f. 创建页表 //cpu内核操作是按页操作的
    g. 将函数__mmap_switched的地址保存在r13寄存器中,关键点: *** __mmap_switched函数最终会调用satrt_kernel函数,start_kernel函数才是启动linux内核的主角
    h. 打开MMU,在这里会调用r13寄存器中保存的__mmap_switched函数

    流程主线: 打开MMU时调用__mmap_switched函数,__mmap_switched又会调用start_kernel函数
    		知识点2: Linux内核启动之前的要求如下 ==>uboot启动linux内核要干的事 
    				1. 关闭MMU
    				2. 关闭D-cache
    				3. r0=0
    				4. r1 = 机器ID
    				5. r2 = 设备树首地址		
    				//3,4,5:是通过uboot中执行 kernel_entry(0, machid, r2)函数传递给linux内核的
    

    2> start_kernel函数通过调用总多的子函数来完成Linux启动之前的一些初始化工作,在这些初始化工作完成之后,最后调用rest_init函数
    3> rest_init函数干的事情
    1. 首先rest_init函数定义在init文件下的main.c中,init文件是完成linux初始化
    2. 调用RCU锁调度器来为后面创建的两个进程做准备
    3. 创建pid=1和pid=2的两个进程,pid=1 == init进程(祖先进程),pid=2:负责所有内核进程的调度和管理
    4. 最后进入idle进程,idle进程:pid=0(由主进程演化而来),被称为空闲进程,即让CPU没事的时候去空闲进程中"瞎逛",即让cpu有事情做,当其他进程要工作的时候就会夺取CPU使用权
    知识点1:init进程的变化过程,init刚被创建是是内核进程(运行在内核态),后面init进程会在根文件系统中查找"init"相关的程序,最后会运行这个程序
    因为"init"程序是处于用户空间,故此时init进程变为用户进程。这个"init"程序是uboot中使用bootargs传递给linux内核的
    4> kernel_init函数就是init进程具体做的工作
    1. 完成运行一些其他初始化工作
    a. 完成Linux下设备驱动初始化工作(驱动模型子系统的初始化)
    b. 打开串口1对应的设备文件"dev/console“,作为标准输入,fd重定向来设置标准输出和标准错误
    c. 挂载根文件系统

    	2. 寻找"init"程序并运行该程序
    			寻找过程,先判断ramdisk_execute_command变量中是否保存着"init"程序,这个是通过bootargs下的"rdinit=xxx"来传递过来的。如果ramdisk_execute_command变量中没接收到"init"程序
    				   就看execute_command变量中是否保存着“init”程序,execute_command变量是通过bootargs下的"init=/xxx"来传递的,表示根文件系统下的xxx程序就是要执行的用户空间的init程序(我们使用的就是该方法)
    				   如果前面两个变量中都为空,就依次查找/sbin/init,/etc/init,/bin/iniy/bin/sh,这4个备用init程序,如果这4个文件中备用的init都不存在,那么Linux启动失败,显示提示信息
    
  8. linux内核移植
    1> 找到半导体厂商的bsp包,然后通过编译3步曲(make clean,make xxx_defconfig,make -jn)来编译生成zImage文件和设备树文件
    2> 设置uboot传递给linux内核的bootagrs环境变量的基本值
    setenv bootargs ‘console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw’
    saveenv
    3> 通过tftp将zImage文件和设备树文件拷贝到DRAM中的指定地址处,并启动linux内核,观察是否可以正常开启
    4> 通过linux内核启动的打印信息判断,那些东西需要重新适配

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值