uboot-启动程序脚本位置-init进程
- UBoot作用:设备上电需要初始化硬件,但是不同设别区别较大,因此,设计UBoot实现管理开机初始化设备的功能
参考链接
- 【参考链接-图表】https://www.runoob.com/linux/linux-system-boot.html
- 【参考链接-详细】https://www.jianshu.com/p/e48bff8c94cc
示例
- 在./project/image/putput/rootfs/bootconfig路径下有demo.sh,里面是启动时运行的各种指令程序
- 此demo.sh,可能也是init进程启动后才执行的脚本
疑问是在哪里写了执行这个demo.sh的呢?
解答
-
在编译镜像后,在project文件夹下有“./project/image/configs/i6b0/rootfs.mk:32: echo export PATH=$$PATH:/config >> ${OUTPUTDIR}/rootfs/etc/profile”
-
打开rootfs.mk显示:
'''
echo export PATH=$$PATH:/config >> ${OUTPUTDIR}/rootfs/etc/profile
''' -
继续打开profile后,显示代码:
'''
if [ -e /bootconfig/demo.sh ]; then
/bootconfig/demo.sh
fi;
''' -
最后打开“./project/image/output/rootfs/bootconfig/demo.sh”可看到init进程运行后需要执行的程序命令
内核初始化
合成内核镜像
- 1.二进制ELF内核主题vmlinux,先去除符号、标注等,成为二进制目标文件
- 2.通过gzip压缩得到piggy.gz的压缩二进制内核文件
- 3.通过asm汇编链接得到可启动的内核镜像
其镜像文件结构
- 由顶层往下为:
- piggy.o,msic.o(包含镜像解压的函数),big_endian.o(转为大端字节序程序),head-xscale.o(为XScale处理器初始化),head.o(启动代码,引导程序将控制权交给这个对象,其是汇编程序完成处理器初始化任务)
初始化的控制流
- 系统上电后,存储在非易失存储介质flash和ROM上的引导加载程序会立刻获取处理区的控制权
- 引导加载程序的作用是:底层初始化、硬件检查、镜像加载运行
- 之后引导加载程序会将控制权交给启动加载程序的head.o,用以执行
内核入口head.o
- 此文件是由head.S编译而来,主要作用是:
- 1.初始化MMU内存
- 2.初始化处理器架构
- 3.初始化页表Page Table
- 4.跳转到start_kernel()函数
- start_kernel()函数位于./init/main.c文件中,main.c是负责内核启动的源文件,是内核开发主要的对象
- start_kernel()函数会调用./arch/arm/kernel/setup.c文件函数用于设置架构相关
内核命令行处理
- 1.在main.c->start_kernel()->setup_arch()中,会传入内核参数
- 内核参数的处理需要__setup宏
- 2.控制台初始化由./kernel/printk.c中的console_setup()函数完成,后续才是__setup宏的使用
- 3.在./include/linux/init.h中,有__setup系列宏夫人定义,其内容为初始化数组,将内容转换为console=输出,在链接阶段会汇集到.init.setup段中
- 4.内核字符串处理的函数,由./kernel/params.c的函数解析获得
- 5.借鉴./kernel/params.c中parase_args()函数
- 6.在文件./include/linux/moduleparam.h中,有module_param*系列函数
子系统初始化
- 除了上述的初始化函数,还有用连接器构造一个函数指针列表,每个指针指向一个初始化函数,然后在循环中依次执行这些函数
- 在./arch/arm/kernel中的static int __init customize_machine(void)可自定义添加设备初始化
- 此过程使用了__init宏,并防止在vmlinux ELF的.init.text段中
- 其他类似的函数initcall宏
init线程
- 1.在./init/main.c赋予内核以生命后,函数start_kernel()函数执行一系列初始化函数,生成第一个内核进程init,其PID为1
- 2.此时,系统引导有2个线程,一个是start_kernel(),另一个是init(),且前者会变成空闲进程
- 3.start_kernel()会调用rest_init(),其中有kernel_thread()执行kernel_int()函数,创建init进程
- 4.最后start_kernel()会调用rest_init()函数中的cpu_idle()后,该最后start_kernel线程进入死循环。因为start_kernel()函数很大,退出需要回收内存资源,所以进入等待状态,而会调用rest_init()函数(此函数运行在start_kernel线程上)占用资源少其变成系统的空闲进程
- 5.最后通过init进程完成最后的初始化任务。通过initcalls、initcall_debug等实现
- 6.最后的内核引导步骤:./kernel/main.c/init_post(),其作用是释放初始化函数和内存资源,打开系统控制台设备,启动第一个用户进程
用户空间初始化
- 内核在main.c中会挂载文件系统,最后执行的指令就会执行各系统目录下的init进程/sbin/init
- 这是一个软连接,会指向busybox,也就是内核执行的第一个用户进程
- 系统配置项在目录/etc/inittab
- 系统运行级别相关配置在/etc/rc.d
其他
- ldd 用于库连接依赖
- libc.so.6 : 标准C程序库
- ld.so.1 :Linux动态加载器
- 初始化RAM ,使用initrd进行引导,需要initrd的支持,linuxrc,initramfs
启动程序流程参考资料
- 【Linux系统启动管理】https://c.biancheng.net/linux_tutorial/12/