Linux 内核移植过程学习
简单修改内核1.内核启动流程内核目录如下book100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$1.1修改Makefile修改Makefile中上述位置如下//arm架构ARCH?ARM 交叉编译工具链时arm-linux-CROSS_COMPILE?arm-linux-//查看编译链版本信息 arm-linux-gcc-v1.2使用默认配置在内核中查找默认配置find-name*defconfig查找到有不同的架构。arm架构在 arch/arm 目录下。先在设备树中修改设备树中的晶振频率。在内核根目录执行下面命令在内核根目录下生成 .configmake100ask_imx6ull_defconfig1.3make uImage执行makeuImage生成的内核在 arch/arm/boot/uImage使用u-boot命令设置machine id 如果没有设置machid就会使用默认id。设置 machine id适用于无设备树的老平台setmachid33245save在内核源码 linux-4.1.15/arch/arm/tools/mach-types 中找到 machine idmachine_is_xxx内核代码判断板子用的宏比如 machine_is_smdk2440()CONFIG_xxxx内核编译开关make xxx_defconfig 会开启这个配置MACH_TYPE_xxxU-Boot / 内核头文件里的宏名如 MACH_TYPE_SMDK2440number十进制机器 ID你 set machid 后面填的数字也可转十六进制输入对应imx6ullu-boot命令修改串口波特率setbootargsconsolettymxc0,115200root/dev/mmcblk1p2rootfstypeext4 saveenvttymxc0是NXP的串口驱动命名/dev/mmcblk1p2 开发板板载 SD 卡mmc1mmcblk1p1 SD卡1分区。标准双分区布局mmcblk1p1FAT32存放u-boot.bin、zImage、imx6ull-xxx.dtb内核 设备树U‑Boot 读取mmcblk1p2ext4存放完整 Linux 根文件系统bootargs 里 root/dev/mmcblk1p2只要把 U-Boot 读取的 FAT 启动分区挂载到 Linux 的 /boot 目录修改 /boot 里的镜像就会同步更新启动文件重启加载新内核。# 内核启动之后将第一个FAT分区挂载到本机/bootmount/dev/mmcblk0p1 /boot使用u-boot里的nfs命令把内核文件下载到内存如下0x32000000 把 PC 下载的完整 uImage带 64 字节头部的压缩内核临时存在这里。nfs 0x32000000192.168.1.123:/work/nfs_root/uImage_newu-boot命令启动内核bottom32000000修改分区和制作根文件系统修改分区对于 eMMC 焊死在开发板 PCB 上无法读卡器直插 PC不直接 PC 操作 eMMCPC 先使用fdisk工具制作带完整分区的 TF 卡将uboot、zImage、dtb通过 TF 卡下载镜像整块写入 eMMC。制作根文件系统详细参考rootfs根文件系统制作及挂载方法-详解环境说明1.PC Ubuntu 终端编译 BusyBox、复制库、构建 rootfs、编译内核、制作 ext4 镜像2.开发板 U-Boot 串口配置网络、烧写镜像、设置 bootargs、启动内核3.工程根目录~/100ask_imx6ull-sdk4.交叉工具链路径~/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf5.目标根文件系统目录~/100ask_imx6ull-sdk/rootfs6.平台特性设备树架构无 machid串口ttymxc0存储使用 SD/eMMCext4不使用 JFFS2/YAFFS2一、前置环境配置PC Ubuntu1.配置交叉编译器环境变量# 打开环境变量配置文件vim~/.bashrc# 在末尾添加 export PATH$PATH:~/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf-# 生效配置source~/.bashrc# 验证编译器是否可用arm-linux-gnueabihf-gcc-v**也可以将Toolchain信息添加到Busybox的根目录下的Makefiel中。**修改CROSS_COMPILE ?和ARCH ? arm。二、交叉编译安装 BusyBoxPC Ubuntu建议编译内核和应用使用同一个编译器1.进入 BusyBox 源码目录cd~/100ask_imx6ull-sdk/busybox-1.29.02.图形化配置# 先加载默认配置在根目录下生成基础.config配置文件makedefconfig# 图形化裁剪功能# Busybox Setting - Build Options - Cross Compiler prefix 修改为 arm-linux-makemenuconfig3.编译并安装到 rootfs 目录提前创建rootfs目录# 使用CONFIG_PREFIX指定安装目录makeinstallCONFIG_PREFIX~/100ask_imx6ull-sdk/rootfs此时在rootfs中已具备完整 shellash/hush、文件操作命令ls、cp、mv、mkdir、rm网络工具ifconfig、ip、ping、route、httpd系统管理init、reboot、halt、mdev、mount、insmod文本工具vi、sed、grep、echo内核启动入口 linuxrcrootfs目录下此时有 bin、linuxrc、 sbin、 usr 目录。在roofts/bin目录中命令文件均为软链接不存储任何程序代码仅记录指向主程序可执行文件 rootfs/bin/busybox 的相对路径。当开发板执行 ls 命令时系统读取 bin/ls识别到是软链接跳转至实体文件 busybox操作系统将链接文件名 ls 作为参数传入 busyboxbusybox 内部根据传入文件名匹配对应功能执行列出目录的逻辑。执行 ls rootfs/bin/ -l 命令输出# 首字符l代表该文件为软连接# ls是用户输入的命令名、链接文件名# - busybox代表此链接指向同目录下的实体程序 busyboxlrwxrwxrwx1book book77月410:00ls-busyboxroofts/bin目录:存放管理员系统底层操作命令重启、驱动、分区、挂载。roofts/usr/bin :上层普通工具非开机必需日常辅助命令find、du、df、grep、sed 等。rootfs/usr/sbin:上层管理工具网络服务、后台程序httpd、telnetd 等小型服务器工具。roofts/linuxrc根目录顶层独立软链接文件指向 roofts/bin/busybox是内核挂载根文件系统后执行的首个程序负责启动系统初始化流程。三、复制交叉编译器 GLIBC 动态库PC Ubuntu修正正确路径执行 echo $PATH 输出交叉编译工具链位置/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bn:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin1.创建存放库的目录# -p 选项一次性创建usr、lib两个目录mkdir-p~/100ask_imx6ull-sdk/rootfs/libmkdir-p~/100ask_imx6ull-sdk/rootfs/usr/lib2.复制核心 glibc 系统库必须含 ld-linux-armhf.so.3 动态加载器# -d 选项将原来的链接文件依旧作为链接文件拷贝减小文件体积cp~/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/*.so* ~/100ask_imx6ull-sdk/rootfs/lib-d3.复制 GCC/C 运行时动态库运行 C 程序必备cp~/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib/*.so* ~/100ask_imx6ull-sdk/rootfs/lib-d4.复制 usr 应用层库文件cp~/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib/*.so* ~/100ask_imx6ull-sdk/rootfs/usr/lib-d说明仅复制*.so*动态库.a静态库、.la、ldscripts、.o无需复制。./libc/lib、./libc/usr/lib中的库只满足运行C程序若想运行C程序还要拷贝 ./lib 中C相关的依赖库。四、构建根文件系统标准目录PC Ubuntu查看完整文档文件夹内容lsBusybox_1.30.0/docs/1.拷贝 /etc 系统配置模板cp-r~/100ask_imx6ull-sdk/etc_template/* ~/100ask_imx6ull-sdk/rootfs/etcetc/fstab、init.d、inittab ,内核启动的第一个进程就会打开 inittab 之后根据 inittab 的内容执行 init.d/rcS 脚本脚本中的 mount -a 就是根据fstab 文件的内容挂接各种根文件系统。2./dev 设备目录说明mkdirdev# 手动创建设备节点 c表示字符设备 5主设备号 1次设备号内核打印日志、串口终端输入输出都依赖 /dev/consolesudomknoddev/console c51# 空设备节点写入任何数据都会直接丢弃读取永远返回空常用场景屏蔽程序输出sudomknoddev/null c13程序输出屏蔽示例# 把程序打印全部丢进黑洞不输出到串口./test/dev/null手动创建这两个节点在 init.d/rcS 脚本中的 mdev -s 会自动创建dev中的其他内容。只要内核同时开启两个配置CONFIG_DEVTMPFSy CONFIG_DEVTMPFS_MOUNTyNXP 全系列 i.MX 平台完全不需要提前手动在rootfs 里创建任何设备文件。但是要先存在 rootfs/dev 空目录作为挂载点。3.创建 Linux 标准空目录cd~/100ask_imx6ull-sdk/rootfsmkdirproc sys mnt tmp var root mnt/nfs mnt/mmc/proc、/sys内核虚拟文件系统/tmp、/var临时文件、运行日志/mnt外挂存储挂载点五. rootfs打包 ext4 镜像用于 SD/eMMC 烧录PC Ubuntu# 1. 生成128M空白镜像文件ddif/dev/zeroofrootfs.ext4bs1Mcount128# 2. 格式化为ext4文件系统mkfs.ext4-Frootfs.ext4参考https://www.cnblogs.com/fuzidage/p/18019053编译并下载内核镜像及根文件系统一、编译 Linux 内核PC Ubuntu生成 zImage dtb 设备树编译前的准备1.defconfig将要编译要的 imx6ull_100ask_defconfig 放到 arch/arm/configs 目录下按照芯片架构修改 imx6ull_100ask_defconfig 中的内容。2.dtb将要编译的 xxx_xxx_xxx.dts 文件放到 arch/arm/boot/dts 目录下修改 arch/arm/boot/dts/Makefile 找到“dtb-$(CONFIG_SOC_IMX6ULL)”配置项在此配置项中加入“xxx_xxx_xxx.dtb”。不然编译的时候是不会编译刚添加的这个设备树的。cd~/100ask_imx6ull-sdk/Linux-4.9.88# 加载100ask imx6ull默认内核配置打开eabi支持防止出现非法指令报错makeARCHarmCROSS_COMPILEarm-linux-gnueabihf- imx6ull_100ask_defconfig# 并行编译内核镜像与设备树makeARCHarmCROSS_COMPILEarm-linux-gnueabihf- dtbs-j4makeARCHarmCROSS_COMPILEarm-linux-gnueabihf- zImage-j4二、文件下载、固化uboot命令网络配置示例初始化网络参数仅首次配置永久保存# 开发板静态IPsetipaddr192.168.1.50# PC Ubuntu主机NFS服务器IPsetserverip192.168.1.123# 保存到Flash重启不丢失saveenv1.下载、固化U-BootROM中有精简的TFTP服务端程序可以直接用来传输uboot。# 1.TFTP下载u-boot.imx固件到DDR缓存# TFTP 服务会提前指定一个固定共享根目录Ubuntu 默认/srv/tftp只要把u-boot.imx放进这个共享目录U-Boot 的tftp命令会自动在这个目录里查找文件。如果存放在tftp的子文件夹下就需要写相对目录。tftp 0x80800000 u-boot.imx# 2.切换到eMMC设备mmc dev1# 3.擦除eMMC头部裸区存放uboot的扇区mmc erase00x400# 4.将DDR内uboot裸写入eMMC头部起始扇区0裸区写入ubootmmcwrite0x808000000$filesize# 烧写完成重启新U-Boot生效reset2.DDR临时启动1.TFTP 下载 zImage 内核到 DDRtftp 0x80800000 zImage2.TFTP 下载 dtb 设备树到 DDRtftp 0x83000000 imx6ull-100ask.dtb3.配置 NFS 网络根文件系统启动参数DDR 调试启动不走 “下载 rootfs 镜像到 DDR”是为了节省内存、加快启动、方便实时调试。# 根文件系统走网络 NFS 挂载setbootargsconsolettymxc0,115200root/dev/nfsnfsroot192.168.1.123:/home/book/100ask_imx6ull-sdk/rootfsip192.168.1.50:192.168.1.123::255.255.255.0::eth0:offrootfstypeext4 rw4.DDR 内存直接拉起内核启动bootz 0x80800000 - 0x830000003.eMMC本地固化启动上面U-Boot已经固化到emmceMMC 分区结构前置说明裸区 / FAT32 /ext4 三区划分i.MX6ULL eMMC 整体分为裸存储区RAW 裸区、FAT32 Boot 分区、ext4 根文件分区三块扇区物理隔离、互不重叠裸区RAW 无文件系统全局扇区 0 ~ 0xFFFFF无格式化、无文件系统存放 U-Boot 固件、U-Boot 环境变量分区表使用 mmc write 裸扇区读写直接操作物理块上电 SOC BootROM 优先读取该区域 U-Boot 引导程序。mmc1:1 FAT32 分区全局起始扇区 0x100000 前裸区末尾分界标准 FAT32 文件系统存放 zImage 内核、imx6ull-100ask.dtb 设备树U-Boot 通过 fatload / fatwrite 按文件名称读写自动管理分区内偏移。mmcblk1p2 ext4 分区全局起始扇区 0x100000Linux 标准 ext4 文件系统存放完整 rootfs.ext4 根文件系统U-Boot 无 ext4 读写能力只能通过 mmc write 整块镜像裸写入扇区Linux 内核启动后挂载该分区作为系统根目录。1.固化 zImage 内核到 eMMC FAT32 boot 分区 mmc1:p1# uboot命令NFS下载内核镜像到DDR缓存# 0x80800000DDR 内存起始物理地址存放下载的 zImagei.MX6ULL 标准加载地址避开栈、环境变量占用区# 192.168.1.123NFS 服务器Ubuntu 电脑IP对应前面serverip配置# :/xxx/zImage主机 NFS 共享目录下内核文件完整路径nfs 0x80800000192.168.1.123:/home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/zImage# 切换选中板载eMMC设备# mmcU-Boot 操作存储介质指令# dev 1切换存储设备dev 0SD 卡dev 1 板载 eMMCmmc dev1# FAT文件系统写入zImage到FAT32分区1仅操作boot分区内部空间不触碰裸区与ext4根文件扇区# fatwrite向 FAT 文件系统写入文件仅 FAT32 分区可用# mmc 1:1存储设备 分区号1eMMC1 第 1 个分区FAT32 boot 分区# 0x80800000源地址DDR 中 zImage 存放位置# zImage写入 FAT 分区后的文件名启动时fatload按此名称读取# $filesizeU-Boot 内置环境变量自动记录上一条 nfs/tftp 下载文件的字节大小不用手动填镜像长度fatwrite mmc1:1 0x80800000 zImage$filesize2.固化 dtb 设备树到 eMMC FAT32 boot 分区 mmc1:p1# NFS下载dtb到DDR缓存nfs 0x83000000192.168.1.123:/home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/imx6ull-100ask.dtb# 写入FAT32 boot分区文件系统fatwrite mmc1:1 0x83000000 imx6ull-100ask.dtb$filesize3.固化 rootfs.ext4 根文件系统到 eMMC ext4 第二分区 mmcblk1p2# NFS下载完整ext4根文件系统镜像到DDRnfs 0x80800000192.168.1.123:/home/book/100ask_imx6ull-sdk/rootfs.ext4# 选中eMMC设备mmc dev1# 裸盘扇区写入从全局绝对扇区0x100000开始覆盖ext4完整分区U-Boot不支持ext4只能绕过文件系统直接裸写磁盘# mmc write裸扇区写入命令不识别文件系统直接按扇区覆盖存储介质# 0x80800000源 DDR 地址存放完整 rootfs.ext4 镜像# 0x100000eMMC 全局绝对起始扇区扇区, 0 ~ 0xFFFFFeMMC 裸区 mmc1:1 FAT32 boot 分区,从 0x100000 扇区开始对应 mmcblk1p2 ext4 根文件分区# $filesizerootfs 镜像总字节数U-Boot 自动换算占用扇区数量完整覆盖第二分区mmcwrite0x80800000 0x100000$filesize4.配置 eMMC 本地启动环境变量# 设置内核启动参数告知Linux根文件系统位于eMMC ext4第二分区mmcblk1p2# consolettymxc0,115200串口控制台配置串口 0波特率 115200内核日志打印到此串口# root/dev/mmcblk1p2指定根文件系统设备eMMC 第 2 个逻辑分区# rootfstypeext4告诉内核该分区文件系统格式为 ext4# rw根分区挂载为可读可写不加则默认只读setbootargsconsolettymxc0,115200root/dev/mmcblk1p2rootfstypeext4 rw# 配置上电自动执行eMMC本地启动脚本bootcmd从FAT32分区读取zImage/dtb到DDR启动setbootcmdmmc dev 1; fatload mmc 1:1 0x80800000 zImage; fatload mmc 1:1 0x83000000 imx6ull-100ask.dtb; bootz 0x80800000 - 0x83000000# 永久保存所有环境变量至eMMC裸区断电不丢失# 将所有 set 配置的变量ipaddr、bootargs、bootcmd 等保存到 eMMC 硬件 BOOT 裸区断电不丢失。saveenv脚本内容解释bootcmd上电自动执行的启动脚本分 4 条指令用;分隔串行执行mmc dev 1选中 eMMC 设备fatload mmc 1:1 0x80800000 zImage从 eMMC p1 FAT 分区读取 zImage 到 DDR 0x80800000fatload mmc 1:1 0x83000000 imx6ull-100ask.dtb读取 dtb 到 DDR 0x83000000bootz 内核地址 - dtb地址拉起 zImage 启动bootz启动 zImage 格式内核的专用命令中间-无 ramdisk 内存盘填空占位5.测试 eMMC 本地启动不重启开发板直接手动执行启动流程验证镜像是否正常run bootcmd无启动报错后重启开发板即可自动从 eMMC 裸区 U-Boot 引导读取 FAT32 分区内核、挂载 ext4 分区根文件系统完整本地运行。记得将拨码开关选择到emmc启动ROM 只会从你拨码选定的存储介质加载引导程序uboot三、根文件系统添加 etc 配置脚本创建/etc/init.d/rcSrcS 是个 shell 脚本Linux 内核启动以后需要启动一些服务而rcS 就是规定启动哪些文件的脚本文件。在 rootfs 中创建/etc/init.d/rcS 文件然后在 rcS 中输入如下所示内容PATH/sbin:/bin:/usr/sbin:/usr/bin:$PATH LD_LIBRARY_PATH$LD_LIBRARY_PATH:/lib:/usr/lib export PATH LD_LIBRARY_PATH mount -a mkdir /dev/pts mount -t devpts devpts /dev/pts echo /sbin/mdev /proc/sys/kernel/hotplug mdev -sPATH 环境变量保存着可执行文件可能存在的目录;LD_LIBRARY_PATH 环境变量保存着库文件所在的目录;mount 命令来挂载所有的文件系统这些文件系统由文件/etc/fstab 来指定;创建目录/dev/pts然后将 devpts 挂载到/dev/pts 目录中。使用 mdev 来管理热插拔设备通过这两行Linux 内核就可以在/dev 目录下自动创建设备节点。这只是一个很简单的rcS。chmod777rcS重新启动 Linux 内核生效。创建/etc/fstabfstab 在 Linux 开机以后自动配置哪些需要自动挂载的分区6列格式如下第一列要挂载的特殊的设备也可以是块设备比如/dev/sda 等等第二列挂载点。第三列文件系统类型比如 ext2、ext3、proc、romfs、tmpfs 等等。第四列挂载选项一般使用 defaultsdefaults 包含了 rw、suid、 dev、 exec、 auto、 nouser 和 async。第五列为 1 的话表示允许备份为 0 不备份一般不备份因此设置为 0。第六列磁盘检查设置为 0 表示不检查。根目录‘/’设置为 1其他的都不能设置为 1因此这里一般设置为 0配置如下fstab 文件创建完成以后重新启动 Linux打印如下自动挂载成功。创建/etc/inittabinittab 的详细内容可以参考busybox 下的文件 examples/inittab。BusyBox提供的 init 程序会读取/etc/inittab这个文件之后才会去执行init.d/rcS 脚本inittab 由若干条指令组成。busybox 支持的动作如下sysinit: 在系统初始化的时候 process 才会执行一次。respawn: 当 process 终止以后马上启动一个新的。askfirst:和 respawn 类似在运行 process 之前在控制台上显示“Please press Enter to activate this console.”。只要用户按下“Enter”键以后才会执行 process。wait: 告诉 init要等待相应的进程执行完以后才能继续执行。once: 仅执行一次而且不会等待 process 执行完成。restart: 当 init 重启的时候才会执行 procee。ctrlaltdel: 当按下 ctrlaltdel 组合键才会执行 process。shutdown: 关机的时候执行 process。# 系统启动以后运行/etc/init.d/rcS 这个脚本文件。 ::sysinit:/etc/init.d/rcS # 将 console 作为控制台终端也就是 ttymxc0。 console::askfirst:-/bin/sh # 重启的话运行/sbin/init。 ::restart:/sbin/init # 按下 ctrlaltdel 组合键的话就运行/sbin/reboot看来 ctrlaltdel 组合键用于重启系统。 ::ctrlaltdel:/sbin/reboot # 关机的时候执行/bin/umount也就是卸载各个文件系统。 ::shutdown:/bin/umount -a -r # 关机的时候执行/sbin/swapoff也就是关闭交换分区。 ::shutdown:/sbin/swapoff -a创建/etc/reslov.conf/etc/resolv.conf是DNS客户机配置文件用于设置DNS服务器的IP地址及DNS域名还包含了主机的域名搜索顺序。该文件是由域名解析器resolver一个根据主机名解析IP地址的库使用的配置文件。如果这个文件为空那板子只能ping IP不能ping网址。DNS Domain Name System域名系统核心作用把好记的域名翻译成计算机能看懂的 IP 地址。nameserver 配置DNS服务器地址顺序来查询且只有当第一个nameserver没有反应时才查询下面的nameserverdomain 声明主机的域名当查询不完全的域名时主机名将被使用(相当于search的默认值)search 它的多个参数指明域名查询顺序。当查询不完全的域名时会使用到domain和search不能共存参考文章https://www.cnblogs.com/fuzidage/p/18019053四、常见报错与解决方案1.SIGILL illegal instruction 非法指令原因交叉工具链为 EABI 标准内核未开启 EABI 支持解决make menuconfig → Kernel Features → 勾选 EABI support重新编译内核。2.提示 cannot load library ld-linux-armhf.so.3原因仅复制 gcc 库缺失 libc 核心动态库解决重新执行拷贝完整libc/lib下的.so*文件。3.串口无输出、乱码bootargs 串口必须写ttymxc0不能使用ttySAC0U-Boot 串口波特率 115200 与 bootargs 保持一致设备树中 24M 晶振clock-frequency 24000000与硬件匹配。对于无设备树 ATAG 架构旧平台例S3C2440步骤一设置machine id步骤二在.c文件中修改外接晶振频率步骤三编译默认配置编译内核步骤四在uboot中指定波特率而对于NXP设备树平台 i.MX6ULL一二步均在设备树文件中修改第三步添加编译设备树文件。对于没有设备树的平台需要去修改对应的.c文件。板子上电启动流程i.MX6ULL 芯片出厂时内部焊死了一段不可擦除的 ROM 固件它不依赖 eMMC 里有没有 U-Boot上电第一时间自动执行。空板完整烧写流程1.拨码切换为网口下载模式2.上电ROM 自动初始化网口等待 TFTP 服务器传输 u-boot.imx3.PC 开 TFTP 服务器上传 u-boot.imxROM 下载到 DDR 并启动 U-Boot4.进入 U-Boot 命令行此时拥有nfs/mmc全部命令5.用 nfs 下载 zImage/dtb/rootfs执行mmc write全部固化到 eMMC6.拨码切回 eMMC 启动后续上电直接跑 eMMC 里的 U-Boot不用再依赖网络。内核启动流程https://www.cnblogs.com/fuzidage/p/17995165bootloader:将内核读入内存设置TAG参数启动R0 0R1 machidR2 TAG参数地址内核根据R1判断能否支持该机器调用机器相关的初始化函数解析TAG参数装载驱动程序挂载根文件系统启动应用程序initcall机制用宏把各个子系统 / 驱动的初始化函数指针按优先级分门别类放进 ELF 不同小节链接时自动拼成有序数组内核启动统一遍历挨个调用。各个模块用宏分散注册各个模块各写一行initcall宏把初始化函数地址注册到指定 ELF 分段链接后变成有序数组内核启动时循环遍历数组批量调用集中执行初始化接口。