- linux启动流程概括
设备上电后,SoC 片内 BootROM 从 Flash 读第一段启动代码到 片内 SRAM 执行,完成 DDR 初始化和最小硬件 init;然后加载 Bootloader到 DDR 。解压boot代码并把控制权交给Bootoader,Bootloader 从 Flash kernel 分区读出 uImage,连同 bootargs、initrd/ramdisk、设备树 一起准备好,按 ARM Linux 启动协议跳转到 Kernel 入口。Kernel 自身完成解压、MMU、驱动 init后挂载根文件系统,启动 init,再由 init/启动脚本拉起业务进程(如媒体主进程)。
整体链路:BootROM → miniboot/SPL → U-Boot → Linux Kernel → init → 业务。
- BootRom完成了什么任务?
BootRom是Rom里写死的,不可改。解决的问题是“没有DDR之前,怎么把能初始化DDR的代码跑起来”。主要完成的工作如下:
- 初始化最基础的始终,必要pin(引脚),识别启动介质(后续从哪里读取代码)。
- 从启动介质固定偏移读取一部分boot代码到片内Ram。
- 第二阶段:片内Ram完成了什么任务?
1)初始化DDR,配置DDR控制器、做信号校准、确认可用内存大小。、
2)最小硬件初始化,保证读Flash正常。配置时钟、引脚和flash控制器。
3)将boot代码从flash拷贝到DDR。
4、Bootloader主要完成了什么工作?
1) 自搬运:避免和内核加载冲突。
2) 初始化驱动
3)读分区表,准备bootargs
4) 从flash kernel分区读uImage到DDR
5) 准备initrd、设备树DTB
6) boot跳转kernel。Kernel会自解压
5、kernel主要完成了什么工作?
1) 自解压
2) 建页表、开MMU/Cache
3) start_kernel():架构init、内存管理、调度器、驱动init
4) 挂载根文件系统:按 bootargs 里 root= 挂 squashfs/yaffs2 等。
5)创建init进程(/linuxrc)
6、init进程如何启动业务
Init进程是busybox通用小程序,他只执行/etc/inittab里面的脚本,dnode脚本主要做的任务有:挂载文件系统,先让 mdev(busybox的小程序) 按内核已经注册好的设备把 /dev 补齐,再手工补上 Flash 分区要用的 /dev/mtd/*;rcS脚本启动应用程序。
7、常见问题
- bootargs 谁传给 kernel?
- U-Boot 把 bootargs 写进设备树的 /chosen/bootargs,跳进内核时 r2 指向这块设备树。(设备树保存在flash里)
- ATAG方式,U-Boot在临跳内核时在内存里搭一张标签链表,r2指向这张表。(这张表保存在内存里)
- initrd 干什么?
Linux内核分为内核(uimage)、根文件系统、u-boot。其中内核就是负责与硬件交互、进程调度管理等工作;而根文件系统就负责与用户的交互,其实就是将flash的一块区域挂载在/目录下。而initrd是在内存里维护一个临时的根文件系统,因为有些板子不能直接挂载真正的根文件系统,需要进行处理。
3.驱动什么时候起来?
驱动分为两批进行加载,第一批在内核镜像里,根分区还没有挂载上就初始化完了。第二批是ko,要等/linuxrc根文件系统跑进rcS,在这个脚本里面进行加载脚本。
4.媒体相关内存
Bootloader 通过 bootargs / 厂商 tag 把 预留物理内存告诉 kernel,媒体 SDK 在用户态或内核模块里映射使用。内核的用的内存本身就有规定好的字段,而媒体内存相关字段是厂商那边新加的。
7、常见问题
1.开MMU之前和之后,CPU用的地址一样嘛?
答:不一样,开MMU之前用的是物理地址,开了之后是虚拟地址。要先建好页表再开启MMU。
2.DMA和Cache为什么会看到不同的数据?
答:DMA读的都是内存里面的数据,而Cache是内存和CPU之间的缓存,CPU修改数据需要先修改cache再刷到内存里。所以就有可能cache的数据被修改了,但是还没有刷到内存里,所以DMA读到的数据就和cache里的不一样。
解决方法:数据是 CPU 准备好、要送给设备的:先把 Cache 里的新数据写回内存,再让 DMA 去读。这样 DMA 读到的才是刚写的。数据是设备经 DMA 写进内存、要给 CPU 用的:等 DMA 写完,把 Cache 里这块旧数据丢掉,再让 CPU 去读。CPU 发现 Cache 里没有,就会去内存拿设备刚写的内容。
3. 预留内存、CMA 和普通内存重叠会怎样?
答:普通内存谁都可以申请;预留内存从可用内存挖走,页分配器看不到;CMA 是内核里一块「平时能借给别人、用时要收回来」的连续物理内存;这三段必须互斥。媒体区被划进了内核可用区,两边会互相改同一块物理内存,现象是花屏、数据随机错、跑一会儿才崩。
4. 设备树一项怎样变成一个能用的设备?
compatible用来和驱动匹配,匹配程序执行驱动的peobe函数,然后申请资源,向用户态注册接口。
5.probe失败一般是什么原因?
匹配字符串不一致,模块的时钟或复位没准备好,寄存器范围错误,依赖的设备还没起来。
6.平台设备和字符设备是什么关系?
平台设备表示这颗SOC上有这个硬件,信息在设备树里,总线不会自己发现它。字符设备时给应用open的接口。常见顺序是:平台驱动 probe 成功,再注册字符设备。一个讲硬件怎么被发现,一个讲应用怎么打开它。
7.中断为什么不能睡眠?
硬中断没有进程上下文。它不是一个可以挂起、再唤醒的进程,所以不能睡眠。
8.上半部和下半部各做什么?
上半部:应答硬件,清理设备寄存器,确认中断,否则同一条中断会一直进来。
下半部:剩下比较耗时的所有任务。
9.下半部有什么方式?
软中断:硬中断返回后尽快执行,仍是中断上下文,不能睡眠,同一种软中断可以在多颗CPU上同时跑。
tasklet:建立在软中断上面,但同一个tasklet不会在两颗CPU上同时跑,不能睡眠。
工作队列:在内核线程里跑,有进程上下文,可以睡眠。
10.时钟、复位、引脚配错,分别是什么现象?
时钟没开,读寄存器常是全0,或者总线直接挂死。复位没释放,模块一直停在复位里,探测超时。引脚复用错,该有的波形没有。
11.根为什么做成只读,配置另放分区?
根要保证下次还能启动,用只读压缩文件系统,运行中不往根上写。配置和日志放可写分区,坏了可以恢复。
12.内核和根为什么分成两个分区?
两者体积和更新频率不同,可以只更新其中一块。内核先于根运行,根损坏时,只要bootloader和内核还在,仍有机会恢复。
13.
14.BSS为什么清?
未初始化的全局变量放在 BSS,镜像里不占空间,上电后内存是随机值。启动代码要把这段清零,这些变量才是 0。
15. 多核时从核谁来拉起?
主核先准备好从核入口和参数,再通过固件或 SoC 的启动寄存器释放从核。从核不能在主核的内存和锁还没准备好时进入内核。