树莓派Linux内核编译

前言

树莓派运行linux系统,内核代码开源,我们可以自己修改内核代码、编写驱动。

本文介绍树莓派内核理解知识,如何获取linux内核代码,并完成编译、内核替换。

基于原文,搭配老师笔记作了改动:http://nicekwell.net/blog/20171108/shu-mei-pai-nei-he-kai-fa-shuo-ming-xia-zai-dai-ma-bian-yi-ti-huan-nei-he.html


树莓派的github主页:https://github.com/raspberrypi,里面包含了linux源码、交叉编译工具链等内容。

对于我们要用到的有两个仓库:

https://github.com/raspberrypi/linux 内核源码

https://github.com/raspberrypi/tools 交叉编译工具链(仅在交叉编译时用到)

注:
1、树莓派里安装的系统镜像版本要和kernel代码对应。因为树莓派系统是在不断开发和升级的,如果你的树莓派使用的是某个时间的系统镜像,那么最好也使用当时的kernel代码。
2、关于内核编译方法,官网有很详细的介绍:https://www.raspberrypi.org/documentation/linux/kernel/building.md,这里算是翻译和补充。
3、以下编译过程在树莓派1和树莓派3B上测试ok。


芯片启动过程

  1. C51、STM32(裸机)是C直接操控底层寄存器实现相关业务,属于业务流程型的裸机代码。比如:
    遥控灯: while(1)
    垃圾桶:WemosD1 LOOP
    恩智浦智能车: stm32

  2. X86、Intel、windows
    启动过程: 电源——> BIOS——>windows内核——>C,D盘——> 程序启动(QQ)

  3. 嵌入式产品: 树莓派,mini2440, mini6410,nanopi,海思,RK(瑞芯微)
    启动过程: 电源——>BootLoader(引导操作系统启动)——>Linux内核——>文件系统(根据功能性来组织文件夹,带访问权限)——>KTV点歌机、人脸识别打卡器、智能家居主控。

  4. 安卓
    启动过程: 电源——>fastBoot/Bootloader——>linux内核——>文件系统——>虚拟机——>HOME应用程序——>点某图标打开某APP

BootLoader
Bootloader启动大多数都分为两个阶段。第一阶段主要包含依赖于CPU的体系结构硬件初始化的代码,通常都用汇编语言来实现。第二阶段通常用C语言完成,以便实现更复杂的功能,也使程序有更好的可读性和可移植性。

一阶段:让CPU 跟内存、FLASH、串口、IIC、IIS、数据段打交道,驱动这些设备(汇编和C结合)

二阶段:引导Linux内核启动 (纯C)


树莓派Linux源码目录树分析

arch:包含和硬件体系结构相关的代码,每种平台占一个相应的目录。和32位PC相关的代码存放在i386目录下,其中比较重要的包括kernel(内核核心部分)、mm(内存管理)、math-emu(浮点单元仿真)、lib(硬件相关工具函数)、boot(引导程序)、pci(PCI总线)和power(CPU相关状态)。

block:部分块设备驱动程序。

crypto:常用加密和散列算法(如AES、SHA等),还有一些压缩和CRC校验算法。

Documentation:关于内核各部分的通用解释和注释。

drivers:设备驱动程序,每个不同的驱动占用一个子目录。

fs:各种支持的文件系统,如ext、fat、ntfs等。

include:头文件。其中,和系统相关的头文件被放置在linux子目录下。

init:内核初始化代码(注意不是系统引导代码)。

ipc:进程间通信的代码。

kernel:内核的最核心部分,包括进程调度、定时器等,和平台相关的一部分代码放在arch/*/kernel目录下。

lib:库文件代码。

mm:内存管理代码,和平台相关的一部分代码放在arch/*/mm目录下。

net:网络相关代码,实现了各种常见的网络协议。

scripts:用于配置内核文件的脚本文件。

security:主要是一个SELinux的模块。

sound:常用音频设备的驱动程序等。

usr:实现了一个cpio。

在i386体系下,系统引导将从arch/i386/kernel/head.s开始执行,并进而转移到init/main.c中的main()函数初始化内核。


以上来自:https://www.cnblogs.com/senior-engineer/p/4929703.html


Linux内核大约1.3w个C文件,1100w行代码,Linux是开源免费的、由LInux开源社区工作者共同维护的、支持多架构多平台的、可移植性非常高的类Unix操作系统。

通常Linux内核编译出来就几个M(4M)

前面谈到1.3w个文件,那为什么会这么小呢?

因为Linux支持多平台,多架构,所以编译之前要配置,配置成适合的目标平台来用。比如我们常见的平台架构:

ARM:海思 友善之臂 RK 树莓派 nanoPi
X86
PowerPC
MIPS


在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载

静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和下载内核,效率较低。若采用静态加载的驱动较多,会导致内核容量很大,浪费存储空间。

动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko),在不需要的时候用rmmod命令卸载模块,采用这种动态加载的方式便于驱动程序的调试,同时可以针对产品的功能需求,进行内核的裁剪,将不需要的驱动去除,大大减小了内核的存储容量

在台式机上,一般采用动态加载的方式;在嵌入式产品里,可以先采用动态加载的方式进行调试,调试成功后再编译进内核。


树莓派Linux源码配置

驱动代码的编译需要一个提前编译好的内核,而编译内核就必须配置,配置最终会生成 .config文件,该文件指导Makefile去把有用的东西组织成内核。

linux源码中有很多工程:
树莓派1的工程是bcmrpi_defconfig;
树莓派2、3的工程是bcm2709_defconfig。

厂家配给linux内核源码,比如说买了树莓派,配给树莓派linux内核源码,一般我们有三种方法配置。

  • 第一种方式:
    cp 厂家给的.config .config
    将我们厂家给的.config完全copy到自己的.config上。

  • 第二种方式:
    make menuconfig 一项项配置,通常是基于厂家的config来配置

  • 第三种方式:
    完全自己来

tips:find -name *_defconfig查看config

如何配置树莓派的Linux内核

驱动两种加载方式:
* -----编译进内核 zImage包含了驱动
M-----模块方式生成驱动文件xxx.ko 系统启动后,通过命令inmosd xxx.ko 加载

内核配置:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make bcm2709_defconfig
其中参数:
1、ARCH=arm指定ARM架构
2、CROSS_COMPILE=arm-linux-gnueabihf-指定编译器工具链
3、KERNEL=kernel7树莓派
4、make bcm2709_defconfig主要核心指令

树莓派Linux内核编译

安装必要的库:
sudo apt-get install bc
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install zlib1g:i386
sudo apt-get install libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5

1、编译:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 zImage modules dtbs
指定用多少电脑资源进行编译
其中参数:
zImage生成内核镜像
modules要生成驱动模块
dtbs生成配置文件

2、编译成功后,看到源码树目录多了vmlinux,失败则无此文件
成功后,目标zImage镜像arch/arm/boot底下

3、打包zImage成树莓派可用的xxx.img
./scripts/mkknlimg arch/arm/boot/zImage ./kernel_new.img

树莓派Linux内核编译_第1张图片

挂载树莓派sd卡,并安装编译出的DIRECTLY 到sd卡
4、数据拷贝
dmesg命令查看底层硬件数据
4.1、mkdir data1 data2创建两个文件夹用于挂载
4.2、挂载U盘

tips : lsblk查看磁盘分区情况

sudo mount /dev/sdb1 data1 一个fat分区,是boot相关的内容,kernel的img
sudo mount /dev/sdb2 data2 一个是ext4分区,也就是系统的根目录分区。

4.3、安装modules, 设备驱动文件: hdmi usb wifi io …
sudo ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make INSTALL_MOD_PATH=/home/sxh/data2 modules_install

树莓派Linux内核编译_第2张图片

4.4、安装更新 kernel.img 文件,注意镜像名字是kernel7.img
先备份
cd /home/sxh/data1
cp kernel7.img kernel7Old.img
再把编译新生成的拷贝到data1,起名kernel7.img
cp kernel_new.img /home/sxh/data1/kernel7.img

树莓派Linux内核编译_第3张图片

4.5、拷贝配置文件

cp arch/arm/boot/dts/.*dtb* /home/sxh/data1
cp arch/arm/boot/dts/overlays/.*dtb* /home/sxh/data1/overlays/
cp arch/arm/boot/dts/overlays/README /home/sxh/data1/overlays/

更新完成后插回树莓派即可开机,开机后可以用 uname -r 命令查看kernel信息已经改变。
在这里插入图片描述


树莓派本地编译

1、配置编译环境
在ubuntu里交叉编译时需要配置的环境变量有:

  • PATH: 添加交叉工具链的目录
  • ARCH: 配置成arm
  • CROSS_COMPILE: 配置成ubuntu上使用的交叉工具链arm-linux-gnueabihf- KERNEL=kernel7
  • KERNEL: 配置成kernel7

而在树莓派本地编译:
关于交叉工具链,本身的编译工具就可以编译给自己使用,所以不用配置;
只需要配置 KERNEL=kernel7 即可。

和上面相同,可以用 export KERNEL=kernel7,一次设置之后此终端里所有命令都带有此环境变量。
也可以更进一步写成脚本,不过这里这一行命令很简单,不写脚本也可以。

2、配置config
和上面一样,
树莓派1使用的是 bcmrpi_defconfig,
树莓派2、3使用的是 bcm2709_defconfig。
例:KERNEL=kernel7 make bcm2709_defconfig

如果要使用树莓派自带的config的话:
sudo modprobe configs # 加载模块
zcat config.gz > .config # 获取配置

3、编译
安装必要的库:
sudo apt-get install bc
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install zlib1g
sudo apt-get install libc6

3.1、执行menuconfig
KERNEL=kernel7 make menuconfig
没什么要改的话就不用执行这一步。

3.2、编译
KERNEL=kernel7 make -j4 zImage modules dtbs 2>&1 | tee build.log
以n进程编译。不指明几进程的话则默认以单进程编译。

3.3、打包zImage文件
直接用linux源码包里的工具:
./scripts/mkknlimg arch/arm/boot/zImage ./kernel_new.img
在本目录生成一个kernel_new.img文件,这个文件就是要放到sd卡中的文件。

4、更新系统
4.1、安装模块
sudo make modules_install

4.2、复制dtb文件
sudo cp arch/arm/boot/dts/.dtb /boot/ sudo cp arch/arm/boot/dts/overlays/.dtb* /boot/overlays/ sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/

4.3、更新kernel.img文件
sudo cp arch/arm/boot/zImage /boot/$KERNEL.img

你可能感兴趣的:(Linux,linux,运维,服务器)