• 【从零到一的Raspberry】数莓派踩坑实录(二) 内核编译配置和模块安装


    写在前面

      本次作业具有挑战性,不过不管哪一环节出错了,你都要知道如何把它还原到初始状态,这样你就不是在危险地操作,而有还原的保障.因此在第0节我会介绍一种还原数莓派系统的方法,这样你就可以在内核无法运行时还原到默认系统!
      后面从第一章开始,带序号的小节会指引你完成本次"内核裁剪"作业.加油!


      【声明】:本文章核心内容主要参考 数莓派官方网站 ,一切以官方网站上的指令为准,敬请访问

    https://www.raspberrypi.com/documentation/computers/linux_kernel.html

      【NOTE】:如果你想要着手快速开始,请直接到序号索引的小节。没有序号的小节可以帮助你获取全方位的理解。
      【系统支持】:本篇暂时未支持Windows系统,请使用 Linux 系统,推荐使用双系统
      【硬件平台】:数莓派3或4系列,支持64位Linux系统,当然,非以上系列也可以用32位进行代替.只是对于本章而言,软件对于硬件具有较好的屏蔽性,数莓派3和4系列的操作代码完全一致.


    项目内容

    ◼首先用默认配置重新编译已安装到开发板的内核,将新的内核替换现有内核,检查是否通过!
    ◼ 在原始版本基础上,重新配置Linux内核,构建一个较小的嵌入式Linux内核;在保留所需的基本功能前提下,内核越小越好;保留必要的模块加载,剩余部分(占多数)取消
    ◼ 编译安装重新配置后的内核、模块及设备树
    ◼ 用新的内核取代旧内核,在开发板上运行测试;
    ◼ 选择模块加载与卸载,检查是否加载卸载成功;
    ◼ 构建并安装两款不同于根文件系统(ext4)、用于应用开发的文件系统,基于所选文件系统特性说明其用途。


    项目分工

    ◼<>首先用默认配置重新编译已安装到开发板的内核,将新的内核替换现有内核,检查是否通过!
    ◼<>内核裁剪流程探索与介绍
    ◼<2人>内核配置manuconfig详细选项阐述部分功能,并根据配置编译安装内核,查看内核大小,并观察是否启动成功
    PPT整合
    ◼<1人>模块加载与卸载实验,基于原始配置来做,检查是否卸载成功,结合老师上课ppt
    ◼<2人>根文件系统探究,构建并安装不同的根文件系统,并说明其用途.结合老师上课ppt
    报告整合

    每个人做好自己的部分对应PPT和报告,最好做之前来找我演示一下,这样看文档会更容易理解.
    大家有任何问题直接联系我,我和大家一起探究.



    系统还原方法

      当我们配置内核,然后把配置好的内核移植到数莓派上时,可能无法工作,无法启动,无法连接等等.当你发现无法恢复需要重新配置时,请先把系统还原.以保证移植替换内核之前数莓派上运行着正常的系统.下面的方法,亲测有效一次,当然需要大量尝试来确保其在不同版本数莓派上的有效性.
      复位文件linux64_defconfig
    在这里插入图片描述

      这个复位文件是什么呢?这个复位文件本质上是一个按照默认配置编译好的内核文件,我们只需要挂载TF卡然后进行安装就可以了,这也就可以使系统快速还原至一个正确的内核.

    #64位内核名
    KERNEL=kernel8
    #挂载tf卡
    sudo mount /dev/sdb1 mnt/fat32
    sudo mount /dev/sdb2 mnt/ext4
    #安装模块
    sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=mnt/ext4 modules_install
    #备份原镜像
    sudo cp mnt/fat32/$KERNEL.img mnt/fat32/$KERNEL-backup.img
    #移动编译好的镜像文件到boot中
    sudo cp arch/arm64/boot/Image mnt/fat32/$KERNEL.img
    sudo cp arch/arm64/boot/dts/broadcom/*.dtb mnt/fat32/
    sudo cp arch/arm64/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
    sudo cp arch/arm64/boot/dts/overlays/README mnt/fat32/overlays/
    #卸载tf卡
    sudo umount mnt/fat32
    sudo umount mnt/ext4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      这段代码我已经写成了recovery.sh,放入linux64_defconfig文件夹中.你要做的只是

    0.lsblk查看tf卡在你的电脑上是不是sdb1,sdb2,如果是sdc的话,把recovery.sh文件中的sdb改成sdc
    1.插上tf卡到电脑
    2.解压linux64_defconfig文件
    3.cd到linux64_defconfig文件夹中,输入sh recovery.sh等待一定时间,就可以恢复成功.
    4.拔下tf卡,插到数莓派上,等待片刻,黄灯不闪,系统恢复成功.


      其中运行完脚本recovery.sh如下所示.
    在这里插入图片描述

    manuconfig配置内核

      配置内核总共有3方式,见本文:树莓派Linux内核源码配置、编译、挂载(boot/kernal/根文件)、开启新内核
      其中我们还原系统采用的是defconfig方法,即使用厂家提供的参考配置,而下面配置内核采用manuconfig方法
      下面仍然以64位系统为例,32位系统请参考开头给的官网参考来做

    make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- KERNEL=kernel8 menuconfig
    
    • 1

      这里指明了make参数,分别是架构名,交叉编译工具链,内核镜像名(64位叫kernel8,32位较kernel7或者kernel7l)
      稍等几秒后弹出如下画面,就可以开始手动配置了.
    在这里插入图片描述
      第一次我们首先按照manuconfig的默认配置来(Note:这里manuconfig的默认配置和之前所述的还原系统时用的厂家配置,这两者配置并不相同,编译的内核镜像大小是有区别的,但最后执行完毕都会生成 .config 文件,用来作为配置文件指导内核的编译)
      直接连续按下两次ESC,这时会有退出询问,询问是不是要保存配置
      点击确定
    在这里插入图片描述
      然后可以看到提示信息告诉我们配置以及写入.config文件了
    在这里插入图片描述

    编译内核(Build with configs)

      无论你使用的是defconfig还是manuconfig,亦不论你的manuconfig配置如何,在上一步中你已经完成了 .config 文件的生成,这个文件将会指导我们这一节中的内核编译.

    make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- KERNEL=kernel8 Image modules dtbs -j 10
    
    • 1

      指明架构,交叉编译工具链,内核镜像名,生成镜像,模块和dtb,注意64位生成Image而不是zImage(32位),同时使用-j 10为其开启多线程加速.j后面的数字最好是cpu核心的1.5倍为最好.
      根据电脑性能和配置内核大小不同,这一步将会花费数分钟到数十分钟不等的时间,耐心等待.
    在这里插入图片描述
      编译结束,接下来将编译好的内核挂载到tf卡上

    TF卡挂载新内核

      在当前编译好的文件夹下编写脚本文件loadkernel.sh,该shell脚本可以帮助自动化挂载新内核到TF卡.
    在这里插入图片描述

      插入tf卡,运行脚本

    sh loadkernel.sh
    
    • 1

      运行完毕后拔下卡,插到数莓派上,发现绿灯很快就灭了,甚至都没有与网线成功连接(网线灯都没有亮).当然,我们其余的步骤都是没有问题的,因为defconfig就是按照如上步骤进行下来的,而manuconfig不行.因此这里推断是manuconfig中需要勾选一些重要启动项目才能让数莓派启动成功.那么下面就开始研究manuconfig配置.
      研究的过程在这里省略了,总之就是发现配置有某些数莓派官方提供的配置文件 bcm2711_defconfig 中的某些选项很重要.而我们使用 menuconfig启动的默认配置 和bcm2711_defconfig中的存在差距.默认配置编译完成是不能正常在数莓派上启动的.于是我考虑对比这两者的配置项的区别.


      1.对比两种配置生成的.config文件,由于每个文件各有数千行,使用python进行文件处理

    import os
    os.remove('diff.txt')
    outf=open('diff.txt','w')
    for line1 in open("bcm2711_defconfig.txt"):
        for line2 in open("defconfig.txt"):
            if(line1==line2):
                break
        else:#正常循环结束,表示没有匹配到line1
            outf.write(line1)
    outf.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      但是输出的结果还是有数千行的差距.此方法行不通.


      2.使用manuconfig导入.config,在UI中进行查看,查看 bcm2711_defconfigmenuconfig启动的默认配置 其配置不同之处,发现其中配置项也有几千项,不能确定到底是什么配置对数莓派启动造成了关键影响.


      3.直接在 bcm2711_defconfig 的基础上进行删改,这被证明是切实有效的.这也是目前我认为最可行高效的方法之一.

      下面就详细阐释基于方法3如何来进行Linux内核配置,裁剪以及装载


    1 工具链配置与内核下载

      本节主要参考了

    https://www.raspberrypi.com/documentation/computers/linux_kernel.html#cross-compiling-the-kernel

      交叉编译的配置工具链和获取源码部分。我们以64位内核为例,以下语句分别完成了交叉编译链安装、linux内核获取

    sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev
    
    sudo apt install crossbuild-essential-arm64
    
    git clone --depth=1 https://github.com/raspberrypi/linux
    
    • 1
    • 2
    • 3
    • 4
    • 5

      我们为linux文件集创建一份备份linux-bkp文件夹,可以留着防止编译配置出错时不用再git

    2 内核配置

    2.1 生成RP官方.config

      我们接下来先根据树莓派官方配置 bcm2711_defconfig 生成.config文件
      指明内核名称,架构,交叉工具链名还有官方的配置名

    make KERNEL=kernel8 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
    
    • 1
    2.2 导入RP官方.config进行修改,并覆盖(不是内核配置的组员跳过此步骤)

      导入.config需要使用manuconfig,于是我们编译manuconfig,在此之前,先安装manuconfig的依赖环境

     sudo apt install libncurses5-dev
    
    • 1

      编译manuconfig

    make KERNEL=kernel8 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
    
    • 1

      稍等片刻,会启动manuconfig的图形界面。我们使用方向键和回车选择LOAD按钮,加载的配置文件名即为我们前一步用官方配置bcm2711_defconfig生成的.config,因此这里按照默认的.config,然后点击确定就可以。
      这时候实质上就是基于树莓派官方提供的配置,在图形界面上进行新的删改配置了。这种配置的好处就是可以保证我们对内核的配置增删是基于能启动的基础上的,最大程度上保证了裁剪后内核的可靠性。
      在图形界面中适当修改一些选项以完成内核剪裁。
      这里需要详细介绍图形界面中具体的配置项,这占据了本次工作的主体内容!
      更改结束后,双击ESC,直到退出提示询问是否要保存配置文件,选择是。这时我们裁剪过的.config配置会覆盖之前bcm2711_defconfig生成的.config

    3 内核编译

      已经配置完毕存在.config文件之后,我们编译时make就会按照 .config 文件所指示配置的进行编译

    make KERNEL=kernel8 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs -j 12
    
    • 1

      注意,j后面的参数推荐处理器数*1.5
      根据配置的内核大小和电脑处理速度的关系,大概等待10~30分钟不等即可完成配置。

    4 内核挂载与模块安装

      这里采用脚本的方法进行快速执行.
      首先将带有TF卡的读卡器插入到你的电脑上
      <注意:这一章节保持tf卡插在电脑上>
      输入

    lsblk
    
    • 1

      来查看该tf卡挂载到了哪里,一般来说可能是sdb,sdc等等.在我的电脑上如下图所示,
    在这里插入图片描述
      你需要通过查看它有bootrootfs这两个分区来确定tf卡挂载的设备名,这里不一定是sdb,在你的电脑上也许是sdc或者其他名字,总之,记住这个tf卡挂载名.
      如果没有出现以上步骤的两个分区,比如说新的tf卡是没有分区的,需要对其先进行分区,具体方法这里不再介绍.你可以使用一些分区软件来完成.
      接着,cd到刚刚完成编译的文件夹,在这里用touch创建一个脚本 loadkernel.sh ,用于自动化地运行一系列命令,需要注意的是,shell脚本注释行是以 # 开头的,不是#开头的行都会被作为语句直接执行.

    touch loadkernel.sh
    
    • 1

      然后对其进行编辑,写入如下脚本,并保存
      <注意:脚本中这里用到的是sdb,实际情况根据前述tf卡挂载名进行修改>

    #指定内核名称
    KERNEL=kernel8
    #创建挂载tf卡的文件夹
    mkdir mnt
    mkdir mnt/fat32
    mkdir mnt/ext4
    #将tf卡对应分区挂载到两个文件夹上
    sudo mount /dev/sdb1 mnt/fat32
    sudo mount /dev/sdb2 mnt/ext4
    #安装模块至ext4分区
    sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=mnt/ext4 modules_install
    #备份原来的镜像
    sudo cp mnt/fat32/$KERNEL.img mnt/fat32/$KERNEL-backup.img
    #将编译生成的新的镜像放入FAT32分区
    sudo cp arch/arm64/boot/Image mnt/fat32/$KERNEL.img
    #将编译生成的dts文件放入FAT32分区
    sudo cp arch/arm64/boot/dts/broadcom/*.dtb mnt/fat32/
    sudo cp arch/arm64/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
    sudo cp arch/arm64/boot/dts/overlays/README mnt/fat32/overlays/
    #取消tf卡的挂载
    sudo umount mnt/fat32
    sudo umount mnt/ext4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      注意,这里模块的安装,是把前面配置为M的模块都放在/lib/module/uname -r/,而启动系统后如果需要用到相关模块,就可以使用相关指令进行加载和卸载.如果我们对某个模块使用非常频繁或者非常重要,就可以在编译时将其选择Y编译进入内核,这也就会启动自加载,而不需要手动加载. [模块加载与卸载] 详细内容见第五节
      至此,完成了内核的编译裁剪安装全流程,我们进入boot文件夹,右键kernel8.img查看裁剪后内核的大小
    在这里插入图片描述

      拔下TF卡,插入到数莓派上.等待数秒,绿灯不闪时,如果能够正常启动,我们就可以通过putty对其进行ssh连接.
      当然,内核的替换并不会修改我们之前配置的一些启动选项,比如说登录名pi和密码123456,同时,ext4中的一些用户文件也依然存在.
      登录后,输入uname -a就可以查看该内核的版本号信息
      这是替换内核之前的信息

    在这里插入图片描述

      
      这是替换内核之后的信息

    在这里插入图片描述

    5 模块的加载与卸载

    本节参考
    http://t.zoukankan.com/jjzd-p-6438641.html
    https://blog.csdn.net/weixin_44502943/article/details/121402205

      在上级节中,我们选择了把一些项目作为模块编译,并将其安装在了数莓派/lib/module/uname -r/文件夹下,当数莓派系统需要使用时,我们再通过一些指令把这些模块加载进来.当不需要时,我们通过特定指令对其进行卸载.
    在这里插入图片描述
      如上图,这些.ko名称结尾的都是之前安装的模块.

      lsmod命令可以查看当前正在运行的模块.
    在这里插入图片描述
      modinfo 查看模块的信息,包括描述,依赖关系,证书,版本等
    在这里插入图片描述
      其中很多模块是依赖于其他模块的,模块的依赖关系都靠modules.dep文件指示,该文件位置如下图所示
      
    在这里插入图片描述
      
      模块的加载有两种指令,分别是
      insmode
      modprobe
      后者的好处是会自动处理依赖关系,加载示例如下
      
    在这里插入图片描述
      
      rmmod 进行模块卸载
      
    在这里插入图片描述

       任务: 实现模块加载卸载,对比几个指令的区别.详细介绍模块究竟是什么,其运行详细机制.
       拓展:尝试自己编写一个简单模块,加载之.

      

    6 根文件系统安装

       数莓派的启动流程可以参考

    https://blog.csdn.net/qq_45172832/article/details/126040945

       boot分区创建用户配置文件可以参考下面文章第三节

    https://blog.csdn.net/fanbinjiim/article/details/124529243

       从本质上来说,需要研究给你一张空的tf卡,如何为其分区,安装bootloader,了解数莓派启动全流程,安装根文件系统,再编译内核,再下载模块并启动这样的全流程太过繁杂,因此我想到的办法是可以在现有的基础上使用fdisk命令删除分区.
       本节重要参考

    https://blog.csdn.net/yuhangfeng/article/details/124845483
    https://blog.csdn.net/yuhangfeng/article/details/124888832?spm=1001.2014.3001.5502

       在之前我们以及构建好的数莓派系统的基础上,把tf卡插入到电脑上.然后用fdisk删除ext4分区,并创建一个新的分区代替ext4,比方说ext3或者别的,参考老师ppt.然后新建好分区后,在这个新的分区上使用busybox创建根文件系统,其中包括了shell命令和库的编译.最后安装模块.
       当然也可以使用数莓派镜像安装器来进行boot分区安装.

    存在的问题:无法启动
    1.交叉编译器正确安装
    2.分区删除干净
    3.内核配置时选择支持该种类的文件系统FS
    4.内核配置的启动段配置Boot options
    关于内核启动bootargs设置
    https://blog.csdn.net/weixin_43793181/article/details/115461081

  • 相关阅读:
    数据结构学习笔记(二)----线性表(上)
    213. 打家劫舍 II
    从驾考科目二到自动驾驶,聊聊GPU为什么对自动驾驶很重要
    虹科分享 | 谷歌Vertex AI平台使用Redis搭建大语言模型
    eBPF汇编指令你还不知道?看这一篇文就够了
    剑指 Offer 05. 替换空格 Java
    SpringBoot环境MongoDB分页+去重+获取去重后的原始数据
    Dataworks实现接口调用
    vue3将自定义组件插入指定dom
    聊聊支付流程的设计与实现逻辑
  • 原文地址:https://blog.csdn.net/SuperiorEE/article/details/127781129