一、温故知新
1、分析uboot源码目录
每个目录基本上都会有自己的Makefile进行当前层级目录的编译,最后在整个uboot源码目录中会有一个Makefile文件进行整合,将每一层级编译出的目标文件,整合到一起,链接到一起,最终生成ubootpak.bin
2、启动过程
uboot启动时,执行第一条代码 b reset
设置SVC管理模式、关闭看门狗、禁止L1与L2缓存以及关闭MMU、 判断CPU-ID是否合法、清空bss段、打开MMU、跳转到board_init_f对硬件进行初始化、跳转到board_init_r指定堆栈区域并初始化EMMC、跳转到main_loop、cli_loop、cli_simple_loop
board_init_f完成一系列硬件的初始化
8大硬件(CPU、定时器、看门狗、串口、USB、网卡、EMMC、中断)
board_init_r完成存储类的初始化,解析uboot命令行的内容
bootcmd、bootargs
3、修改uboot默认的环境变量
【1】uboot倒计时
【2】本地ip地址
【3】服务端ip地址
【4】bootcmd
【5】bootargs
【6】uboot终端提示符
4、修改启动logo
二、移植Linux内核
1、查看linux内核版本
uname -a
2、linux内核的源码目录结构
1)arch体系结构 与uboot中的arch类似
包含了与特定硬件架构相关的代码,如:x86 \ ARM等
2)block块 块设备的IO操作 3)crypto加密 加密的API 4)Documentation文档 内核中各部分的文档 5)drivers设备 设备驱动程序 6)firmware固件 用于存储固件的目录 7)fs文件系统 用于存储文件系统 8)include头文件 内核提供的API头文件 9)init初始化 内核启动的初始化代码 a)ipc进程通信 进程间通信的代码 b)kernel内核 内核的核心代码,包括:进程调用 \ 时钟等 c)lib库 包含了内核用到的库 d)mm内存管理 存放内存管理的代码
linux内核的5大功能
1】进程间通信
2】进程管理
3】网络子系统
4】虚拟文件子系统
5】内存管理
e)net有关网络 网络相关的代码 f)scripts脚本 配置内核所用的脚本 g)security安全 安全模块 h)sound声音 音频相关的驱动和模块 i)tools工具 构建内核时需要使用的工具 j)usr用户 提供了用户空间的内核空间的接口 k)virt虚拟 虚拟化的支持
3、编译Linux内核
1)修改Makefile文件
vim kernel/Makefile
/*在开头添加以下内容*/
ARCH ?= arm
CROSS_COMPILE ?= arm-cortex_a9-linux-gnueabi-
2)拷贝文件
/*将arm架构下的默认配置文件拷贝到kernel目录*/
cp arch/arm/configs/x6818_deconfig .config
/*将uboot源码中的mkimage工具拷贝到根目录下的bin目录*/
sudo cp uboot/tools/mkimage /bin
3)编译内核
make uImage -j4
注释:
-j4:指定make同时运行的任务数,4核并发操作
4)将编译好的uImage拷贝到tftp共享目录中
cp arch/arm/boot/uImage /tftpboot/
5)修改环境变量在emmc中的存储位置
vim uboot/include/configs/x6818.h
原因:咱们之前添加一个logo,大小为900*1024,防止存储位置产生重叠,我们将环境变量的位置进行了后移。由于uboot大小的变化,我们需要重新对EMMC进行分区。
6)对EMMC重新分区
【1】擦除emmc
mmc erase 0 100000
【2】重新分区
fdisk 2 3 0x400000:0x4000000 0x4400000:0x2f200000 0x33600000:0
【3】重新移植uboot
这次给它分了4Mb的空间
7)下位机操作
tftp 0x48000000 uImage
mmc write 48000000 2000 3000
setenv bootcmd mmc read 0x48000000 2000 3000\; bootm 48000000
setenv bootargs root=/dev/nfs
nfsroot=192.168.1.8:/nfs_share/rootfs
ip=192.168.1.6:192.168.1.8:192.168.1.1:255.255.255.0
init=/linuxrc console=ttySAC0,115200 maxcpus=1 lcd=wy070ml
tp=gslx680-linux loglevel=2
saveenv
注释:
bootcmd:告诉uboot去哪个地方加载内核
bootargs:告诉linux内核去哪个地方挂载根文件系统,以什么方式挂载
loglevel=2:将log日志设置为2级
三、Linux内核的配置
1、配置Linux的原因
1)linux支持多种架构
linux支持arm、x86。。。架构,可以将我们需要的硬件平台的代码与通用代码一起参与编译,最终生成我们需要的硬件平台的代码uImage
2)linux内核集成了大量的功能
当我们具体到某一款嵌入式产品时,可能只需要其中一部分的功能,如:路由器,我们不需要显示屏,也不需要触摸屏,所以我们可以将内核中有关这两部分的代码进行裁减,Linux内核允许通过配置方式将程序员选中的功能编译进内核,用来选择哪些功能参与编译,哪些功能不参与编译,这个过程就是内核的裁减
2、使用menuconfig工具
make menuconfig
--------------------------------------------
“↑”、“↓”:进行菜单的选择
"←"、"→":进行功能的选择(<Select>、<Exit>、<Help>)
回车:进入所选内容进行配置
空格:选中该项内容 / 取消该项内容
--------------------------------------------
3、其他配置linux的方法
【1】make menuconfig // 图形化界面配置linux内核
【2】make config // 纯文字终端界面
【3】make xconfig // 需要QT4的支持
【4】make gconfig // 需要GTK的支持
注意:通过make menuconfig修改的Linux内核配置会影响.config的文件内容
四、Linux的编译
1、进行裁减的逻辑
make menuconfig配置完成之后,会影响.config文件
在make uImage时,Makefile文件会按照.config文件中的配置进行编译,并影响到内核的生成
2、编译后的生成文件
内核编译之后会生成三个文件:uImage、zImage、Image
uImage:uboot专用的映像文件
zImage:内核的映像压缩文件
Image:内核映像文件
区别:uImage-zImage
uImage是在zImage之前加上了一个长度为64字节的“头”,记录了在这个内核的版本 \ 加载的位置 \ 生成的时间 \ 大小,在64字节之后的内容与zImage没有区别
3、vimlinux、vimlinuz、vimlinux.bin
vimlinux:是Linux内核编译后未经任何压缩或处理的原始二进制映像,不可以引导的内核
vimlinuz:是vimlinux经过gzip压缩的linux内核映像文件,可以引导的内核
vimlinux.bin:Linux原始影像的二进制文件
五、Linux内核的启动流程
1、查找入口点文件
rm vmlinux // 使内核重新编译
make V=1 > 1.txt //将编译和链接的详细过程都显示出来并重定向到1.txt文件中
grep "vmlinux" 1.txt //筛选包含"vimlinux"关键字的行,打印到终端
arch/arm/kernel/head.o // 编译后的内核入口文件,原文件应该是head.S
2、汇编阶段
vim arch/arm/kernel/head.S
1)设置SVC模式、禁止IRQ中断、获取进程id、获取CPU id
2)检测CPU id
3)创建页表
4)使能MMU
5)清空BSS段,拷贝数据段
vim arch/arm/kernel/head-common.S
3、C语言阶段
1)start_kernel()开始各种硬件初始化(更换了管理者,需要重新初始化)
2)rest_init()处理剩下未处理完成的事情,证明我们现在还活着
3)kernel_thread()创建一个内核线程
4)kernel_init()在进行kernel初始化时,准备命名空间
5)prepare_namespace()在命名空间里,挂载根文件系统
6)kernel_init()挂载完根文件系统,初始化段,打开用户模式
7)init_post()开始在用户模式跑起来
8)执行后续进程、执行sh、执行用户输入的命令
4、执行过程总结
【1】入口点文件:arch/arm/kernel/head.S
【2】设置SV管理模式
【3】检查CPU-ID
【4】创建页表
【5】使能MMU
【6】清空BSS,拷贝数据段
【7】b start_kernel
【8】rest_init()
【9】创建内核线程
【a】准备命名空间
【b】挂载根文件系统
【c】执行1号进程
【d】执行后续进程
【e】执行/bin/sh
【f】执行用户输入的命令
5、板子上电之后的执行流程
1)执行uboot
【1】设置cpu为svc管理模式
【2】关闭看门狗,防止系统复位重启
【3】禁止L1 L2缓存,以及关闭MMU,使用物理地址
【4】检验CPU-ID是否合法,防止不法厂商
【5】清除BSS段
【6】打开MMU,使用虚拟地址
【7】跳转到board_init_f初始化硬件(关键!!!)
【8】跳转到board_init_r指定堆栈区域,初始化EMMC(关键!!!)
【9】main_loop
【a】cli_loop
【b】cli_simple_loop
2)通过环境变量bootcmd找到内核,执行内核
【1】入口点文件:arch/arm/kernel/head.S
【2】设置SV管理模式
【3】检查CPU-ID
【4】创建页表
【5】使能MMU
【6】清空BSS,拷贝数据段
【7】b start_kernel
【8】rest_init()
【9】创建内核线程
【a】准备命名空间
【b】挂载根文件系统
【c】执行1号进程
【d】执行后续进程
【e】执行/bin/sh
【f】执行用户输入的命令
六、Makefile
1)Makefile规则
obj-y += xxx.o // 将xxx.o文件编译进uImage
obj-m += xxx.o // 将xxx.o文件编译为一个独立的模块文件
obj- += xxx.o // 构建对象但不执行类型(不编译进uImage)
2)=、:=、+=、?=
= // 最基本的赋值
:= // 覆盖之前的值
?= // 如果之前没有赋值,就赋值等号后面的值
+= // 添加等号后边的值
3)Kconfig、make menuconfig、.config、Makefile
Kconfig make menuconfig .config Makefile <*> =y obj-y <m> =m obj-m < > 注释掉了 obj- 添加menuconfig的选项
七、实操
将led_drv.c编译进uImage
1)拷贝文件到内核的字符设备目录中
cp /mnt/hgfs/music/easthome_porting/led_drv.c ./drivers/char/
2)修改Makefile
vim ./drivers/char/Makefile
3)制作Linux内核
make uImage
4)将编译好的uImage拷贝至tftpboot目录
cp ./arch/arm/boot/uImage /tftpboot/
5)下位机下载linux内核
tftp 0x48000000 uImage
6)将uImage写入emmc
mmc write 48000000 2000 3000
7)修改环境变量
setenv bootcmd mmc read 48000000 2000 3000 \; bootm 48000000
8)重启,进入内核,查看led_init是否被调用
re:在uboot输入,重启开发板
dmesg:在linux内核终端输入,查看内核的启动信息