1. Linux的介绍
1.1. Unix简介
Unix系统源自贝尔实验室,提供源码。在Unix中几乎所有东西都被当成文件对待。
1.2. Linux简介
芬兰人Linus最早开发,是类Unix系统,但不是Unix,实现了Unix的API(具体实现和Unix可能不同)
而Linux使用GPLv2开源协议
1.3. Linux的发展史
1991
8月25号 : 21岁的芬兰学生Linus Benedict Torvalds 在comp.os.minix 新闻组上宣布了它正在编写一个免费的操作系统。
9月1号 : Linux 0.01在网上发布。
2007
6月6号 : 华硕在2007的台北电脑展上展出了两款“易PC”(Eee PC):701和1001。第1批易PC预装的是Xandros Linux,这是一个基于Debian,轻量级的为适应小屏幕进行过优化的Linux发行版。
8月8号 : 2007年Linux基金会由开源发展实验室(OSDL)和自由标准组织(FSG)联合成立。这个基金会目的是赞助Linux创始人Linus的工作。基金会得到了主要的Linux和开源公司,包括富士通,HP,IBM,Intel,NEC,Oracle,Qualcomm,三星和来自世界各地的开发者的支持。
11月5号 : 与之前大家推测的发布Gphone不同,Google宣布组建开放手机联盟(Open Handset Alliance)和发布Android,它被称为“第一个真正开放的综合移动设备平台”。
2011
5月11号 : 2011年Google I/O大会发布了Chrombook。这是一款运行着所谓云操作系统Chrome OS的笔记本。Chome OS是基于Linux内核的。
6月21号 : Linus Torvalds 发布了Linux3.0版本。
2013
12月13号 : Valve公司发布基于Linux的SteamOS操作系统,这是一个视频游戏控制台系统。
对linux发展史感兴趣的也可以到网上自行搜索linux发展史
1.4. 单内核与微内核区别
单内核:所有内核从整体上作为一个单独大过程实现,运行于内核地址空间,内核通信简单。内核通常以单个静态二进制文件存放于磁盘上。简单性能高,大多数Unix都是单内核。
微内核:微内核功能划分为多个过程,各个过程运行在单独地址空间,需要通过进程间通信IPC处理微内核通信,只有请求强烈的过程才运行在内核态,其他过程在用户态。IPC开销大,且涉及用户态和内核态上下文切换。所以多数的微内核实现(Windows NT、OS X)将所有微内核过程都运行于内核态
1.5. Linux内核
Linux是单内核,但吸取微内核精华:模块化涉及,抢占式内核,支持内核线程,可动态装载内核模块
Kernel即是Linux内核,Linux内核采用宏内核架构,即Linux大部分功能都会在内核中实现,如进程管理、内存管理、设备管理、文件管理以及网络管理等功能,Linux在发展的过程中,引入了内核模块(Loadable Kernel Module,LKM)机制,内核模块全称为动态可加载内核模块,就是在内核运行时可以动态加载一组目标代码来实现某些特定的功能,在这过程中不需要重新编译内核就可以实现动态扩展。
1.6. Linux内核组成
Linux内核主要由5部分组成,分别为:进程管理子系统,内存管理子系统,文件子系统,网络子系统,设备子系统,如下图所示。
1、进程管理
负责进程的创建和销毁,进程的调度。
2、内存管理
负责内存的分配和回收,记录哪些内存被哪些进程使用,管理虚拟内存,将内存的物理地址和逻辑地址做一个映射,主要由MMU进行转换,页表的方式。
3、文件系统
这里的文件系统不仅仅只是硬盘的抽象管理,它也可以是某些io口的抽象;文件系统屏蔽了底层的细节,为上层提供统一的接口;linux中一切皆文件。
4、设备管理
设备管理功能主要由驱动程序提供,主要任务是控制设备完成输入或输出操作;linux把设备看作是特殊的文件,系统通过处理文件的接口(虚拟文件系统VFS)来管理和控制各种设备。
5、网络功能
网络功能值的是除了驱动程序提供的基本硬件操作外,还有系统提供的机制和功能,比如TCP协议,地址解析等。
1.7. Linux官方站点
官方地址为: The Linux Kernel Archives
在这可以下载最新版本的内核文件,但是大部分是没适配相应芯片的,适配芯片是芯片原厂干的事,我们一般不去为一款芯片做适配,我们也没这个能力去适配,我们作为嵌入式开发人员主要职责是进行内核的配置与使用,不要把大量的精力用于去适配一款芯片,这对初学者非常劝退,所以本书主要讲解内核的配置和使用。
1.8. NXP官方镜像与鲁班猫镜像的区别
NXP官方镜像(后简称官方镜像)与鲁班猫镜像(后简称lubancat),我们将kernel,dtb,dtbo打包进了rootfs内,这样更为通用且更换其中一部分只需将文件替换即可。官方镜像则得烧写指定地址需要更新其中一部分则较为困难
2. Linux内核的编译
这里的Linux内核指的是Kernel,或者称内核也是指的Kernel
2.1. 为什么要自己编译Kernel
虽然我们提供的内核已经支持绝大多数功能了,但是对于一些需要定制功能的客户来说不一定有他们需要的功能配置,所以本章将讲解如何配置与编译内核
这里提供一个简单的检测工具查询该内核配置的情况:
sudo wget https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh
sudo chmod 755 ./check-config.sh
./check-config.sh
可以看到配置了的内容
没有启动的到kernel里进行配置启动即可
2.2. 下载安装编译镜像系统
使用平台:Ubuntu 18.04.5 LTS 版本
可以使用我们提供的虚拟机镜像 野火i.MX6ULL Linux开发板 — 野火产品资料下载中心 文档
也可以自己下载ubuntu 18.04.5 LTS官方镜像搭建
2.3. 安装编译工具和依赖
编译内核之前需要安装必要的环境工具。
sudo apt install make gcc-arm-linux-gnueabihf gcc bison flex libssl-dev dpkg-dev lzop vim
2.4. 获取kernel
2.4.1. 下载源代码
有三个方法获取Kernel源码,一个是Kernel官方内核源码,一个是NXP官方的kernel源码,一个是经过我们修改适配我们板子的kernel源码,我们这里使用我们提供的源码为例,对官方源码感兴趣的小伙伴也可以下载来学习配置。我们的kernel是根据NXP官方提供的kernel定制的,NXP的kernel是根据kernel官方某一版本进行芯片适配的。我们作为嵌入式开发者,一般只需要使用芯片厂商适配好的kernel进行开发即可,对于从kernel官方下载新版本来配置这是芯片厂商做的事情,感兴趣的伙伴也可以根据NXP官方提供的芯片手册进行适配新版本的kernel。由于imx6ull这款芯片较为常用所以NXP官方将适配补丁提交给了kernel官方,这样kernel官方基本每一个版本都适配了这款芯片,新版本的kernel官方的kernel也是可以直接编译来使用的。
Kernel官方内核源码:https://git.kernel.org/
NXP内核源码: https://github.com/Freescale/linux-fslc
EBF内核源码:https://github.com/Embedfire/ebf_linux_kernel.git
使用我们提供的
git clone https://github.com/Embedfire/ebf_linux_kernel.git
通常一个内核仓库往往维护着不同分支的内核源码,进入仓库目录下可通过命令查看及切换内核分支
#查看uboot分支
git branch -a
#打印消息如下,默认为master主分支。
* ebf_4.1.19_imx
remotes/origin/HEAD -> origin/ebf_4.1.19_imx
remotes/origin/ebf_4.1.15_imx
remotes/origin/ebf_4.19.35_imx6ul
remotes/origin/ebf_4.19_imx
remotes/origin/ebf_4.19_imx_timeout_15S_PMIC
remotes/origin/ebf_4.19_star
remotes/origin/ebf_5.10.25_rk3328
remotes/origin/ebf_5.4.47_imx8mmini
remotes/origin/master
remotes/origin/test
#切换ebf_4.19.35_imx6ul分支
git checkout ebf_4.19.35_imx6ul
#打印消息如下
Checking out files: 100% (63998/63998), done.
Branch 'ebf_4.19.35_imx6ul' set up to track remote branch 'ebf_4.19.35_imx6ul' from 'origin'.
Switched to a new branch 'ebf_4.19.35_imx6ul'
#重新查看当前分支
qinghui@ebf-dev:~/embedfire/ebf_linux_uboot$ git branch
ebf_4.1.15_imx
* ebf_4.19.35_imx6ul
2.5. kernel工程结构分析
学习一个软件,尤其是开源软件,首先应该从分析软件的工程结构开始。一个好的软件有良好的工程结构,对于读者学习和理解软件的架构以及工作流程都有很好的帮助。
ebf_6ull_linux
内核源码目录如下
arch COPYING drivers init kernel make_deb.sh README sound
block CREDITS firmware ipc lib Makefile samples tools
build_image crypto fs Kbuild LICENSES mm scripts usr
certs Documentation include Kconfig MAINTAINERS net security virt
我们可以看到Linux内核源码目录下是有非常多的目录,且目录下也有非常多的文件, 下面我们简单分析一下这些目录的主要作用。
arch :主要包含和硬件体系结构相关的代码,如arm、x86、MIPS,PPC,每种CPU平台占一个相应的目录,例如我们使用的imx系列CPU就在 arch/arm/mach-imx
目录下,Linux内核目前已经支持30种左右的CPU体系结构。arch中的目录下存放的是各个平台以及各个平台的芯片对Linux内核进程调度、内存管理、 中断等的支持,以及每个具体的SoC和电路板的板级支持代码。
block :在Linux中block表示块设备(以块(多个字节组成的整体,类似于扇区)为单位来整体访问),譬如说SD卡、Nand、硬盘等都是块设备,block目录下放的是一些Linux存储体系中关于块设备管理的代码。
crypto :这个目录下存放的是常用加密和散列算法(如md5、AES、 SHA等),还有一些压缩和CRC校验算法。
Documentation:内核各部分的文档描述。
drivers :设备驱动程序,里面列出了linux内核支持的所有硬件设备的驱动源代码,每个不同的驱动占用一个子目录,如char、block、 net、 mtd、 i2c等。
fs :fs就是file system,里面包含Linux所支持的各种文件系统,如EXT、FAT、 NTFS、 JFFS2等。
include :目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在 include/linux
子目录下,与cpu架构相关的头文件在include目录下对应的子目录中。
init :内核初始化代码,这个目录下的代码就是linux内核启动时初始化内核的代码。
ipc :ipc就是 inter process commuication
,进程间通信,该目录下都是linux进程间通信的代码。
kernel :kernel就是Linux内核,是Linux中最核心的部分,包括进程调度、定时器等,而和平台相关的一部分代码放在arch/*/kernel目录下。
lib :lib是库的意思,lib目录下存放的都是一些公用的有用的库函数,注意这里的库函数和C语言的库函数不一样的,因为在内核编程中是不能用C语言标准库函数的,所以需要使用lib中的库函数,除此之外与处理器结构相关的库函数代码被放在 arch/*/lib/
目录下。
mm : 目录包含了所有独立于 cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等,而与具体硬件体系结构相关的内存管理代码位于 arch/*/mm
目录下,例如 arch/arm/mm/fault.c
。
net : 网络协议栈相关代码,net目录下实现各种常见的网络协议。
scripts :这个目录下全部是脚本文件,这些脚本文件不是linux内核工作时使用的,而是用了配置编译linux内核的。
security :内核安全模型相关的代码,例如最有名的SELINUX。
sound : ALSA、 OSS音频设备的驱动核心代码和常用设备驱动。
usr : 实现用于打包和压缩的cpio等。
此处仅列出一些常见的目录。
2.6. 内核配置选项
Linux内核的配置系统由三个部分组成,分别是:
-
Makefile:分布在 Linux内核源代码根目录及各层目录中,定义 Linux 内核的编译规则;
-
配置文件:给用户提供配置选择的功能,如Kconfig文件定义了配置项, 使用make_deb.sh脚本编译时,使用
arch/arm/configs/npi_v7_defconfig
文件对配置项进行赋值; -
配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释) 和配置用户界面(linux提供基于字符界面、基于Ncurses 图形界面以及 基于 Xwindows 图形界面的用户配置界面,各自对应于make config、make menuconfig 和 make xconfig)。
读者如果想看我们提供的配置文件npi_v7_defconfig中修改了什么地方,可以通过
make menuconfig KCONFIG_CONFIG=arch/arm/configs/npi_v7_defconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
命令来查看我们的配置,make menuconfig是一个基于文本选择的配置界面, 推荐在字符终端下使用, 而这个配置文件为 npi_v7_defconfig
此时就可以看到在npi_v7_defconfig的配置选择, 可以通过键盘的”上”、”下”、”左”、”右”、”回车”、”空格”、”?”、”ESC”等按键进行选择配置,具体见:
#使用图形界面配置需要额外安装 libncurses-dev
sudo apt install libncurses-dev
#执行命令
make menuconfig KCONFIG_CONFIG=arch/arm/configs/npi_v7_defconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
比如我们选择配置我们开发板的ds18b20驱动: ds18b20
, 如果读者找不到这个配置选项在哪里,
可以使用
make menuconfig
中的搜索功能, 在英文输入法状态下按下”/”则可以进行搜索,输入”ds18b20”找到改配置选项的位置, 当输入错误时,可使用 Ctrl+退格键 删除输入。 具体见:
从图中可以很明显看出 ds18b20
配置选项位于 -> Device Drivers
选项下的 -> Embedfire Modules
下的 -> Embedfire Modules (EBF_MODULE [=y])
的选项中, 其实也可以按下 "1"
直接可以定位到对应的选项, 然后选中以下内容即可,具体见图:
可使用 y、n、m
键更改ds18b20驱动的配置时, 其中y表示编译进内核中,m表示编译成模块,n表示不编译。 也可使用空格选择ds18b20驱动的配置选项。
想要配置其他的驱动也是如此。
2.7. kernel编译
编译Kernel有两种方法,一种是编译较为通用的zImage,常用于构建成镜像固件。另一种则是编译成deb安装包,将其下载到板子上安装即可更新Kernel。
2.7.1. 编译内核zImage
#清除之前编译环境
make mrproper
#编译内核
make ARCH=arm npi_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
#编译成功后将打印以下信息。
.......
LD [M] sound/soc/fsl/snd-soc-fsl-esai.ko
LD [M] sound/soc/fsl/snd-soc-fsl-micfil.ko
LD [M] sound/soc/fsl/snd-soc-fsl-rpmsg-i2s.ko
LD [M] sound/soc/fsl/snd-soc-fsl-sai.ko
LD [M] sound/soc/fsl/snd-soc-fsl-ssi.ko
LD [M] sound/soc/fsl/snd-soc-imx-audmux.ko
LD [M] sound/soc/fsl/snd-soc-imx-wm8960.ko
LD [M] sound/soc/fsl/snd-soc-fsl-utils.ko
LD [M] sound/soc/generic/snd-soc-simple-card-utils.ko
LD [M] sound/soc/generic/snd-soc-simple-card.ko
LD [M] sound/soc/generic/snd-soc-simple-scu-card.ko
LD [M] sound/soc/snd-soc-core.ko
LD [M] sound/usb/snd-usbmidi-lib.ko
LD [M] sound/usb/snd-usb-audio.ko
AS arch/arm/boot/compressed/bswapsdi2.o
AS arch/arm/boot/compressed/piggy.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
编译得到的zImage内核在 arch/arm/boot 目录下,设备树在 arch/arm/boot/dts 目录下 设备树分为emmc版本 imx6ull-mmc-npi.dtb 以及 nand版本 imx6ull-nand-npi.dtb 。
zImage生成简述
通过arm-linux-gnueabihf-ld命令将 vmlinux.lds head.o piggy.o misc.o decompress.o string.o hyp-stub.o lib1funcs.o ashldi3.o bswapsdi2.o 链接成vmlinux
再通过arm-linux-gnueabihf-objcopy命令将vmlinux 以bin格式输出到zImage,期间删去了comment等信息
2.7.2. 编译内核deb安装包(推荐)
想要编译内核的deb安装包,以sudo权限运行野火提供的 make_deb.sh 脚本即可, 我们也推荐使用这样的编译方式编译内核,构建出来的deb安装包可直接使用sudo dpkg -i xxx.deb命令 安装在鲁班猫系统上,然后重启使用 cat /proc/version 查看内核是否更新。
ebf_linux_kernel/make_deb.sh内容如下:
deb_distro=bionic
DISTRO=stable
build_opts="-j 16"
build_opts="${build_opts} O=build_image/build"
build_opts="${build_opts} ARCH=arm"
build_opts="${build_opts} KBUILD_DEBARCH=${DEBARCH}"
build_opts="${build_opts} LOCALVERSION=-imx6"
build_opts="${build_opts} KDEB_CHANGELOG_DIST=${deb_distro}"
build_opts="${build_opts} KDEB_PKGVERSION=1.$(date +%g%m)${DISTRO}"
build_opts="${build_opts} CROSS_COMPILE=arm-linux-gnueabihf-"
build_opts="${build_opts} KDEB_SOURCENAME=linux-upstream"
make ${build_opts} npi_v7_defconfig
make ${build_opts}
make ${build_opts} bindeb-pkg
说明如下:
-
deb_distro=bionic: 设置了变量deb_distro,用于指定生成的 Debian 包的目标发行版,这里设置为bionic。
-
DISTRO=stable: 设置了变量DISTRO,用于指定 Linux 内核的目标发行版,这里设置为stable。
-
build_opts=”-j 16”: 设置了编译选项,-j 16表示并行编译时使用16个线程。
-
build_opts=”${build_opts} O=build_image/build”: 添加了编译选项,指定编译输出目录为build_image/build。
-
build_opts=”${build_opts} ARCH=arm”: 添加了编译选项,指定目标架构为ARM。
-
build_opts=”${build_opts} KBUILD_DEBARCH=${DEBARCH}”: 添加了编译选项,指定deb包的架构为${DEBARCH}。
-
build_opts=”${build_opts} LOCALVERSION=-imx6”: 添加了编译选项,设置了内核的本地版本标识为-imx6。
-
build_opts=”${build_opts} KDEB_CHANGELOG_DIST=${deb_distro}”: 添加了编译选项,指定生成的deb包 changelog 的目标发行版为${deb_distro}。
-
build_opts=”${build_opts} KDEB_PKGVERSION=1.$(date +%g%m)${DISTRO}”: 添加了编译选项,指定生成的deb包的版本号为当前年份后两位加上当前月份,后跟${DISTRO}(在这种情况下是stable)。
-
build_opts=”${build_opts} CROSS_COMPILE=arm-linux-gnueabihf-“: 添加了编译选项,指定交叉编译工具链为arm-linux-gnueabihf-。
-
build_opts=”${build_opts} KDEB_SOURCENAME=linux-upstream”: 添加了编译选项,指定生成的deb包的源名称为linux-upstream。
-
make ${build_opts} npi_v7_defconfig: 使用 make 命令加载了名为 npi_v7_defconfig 的预配置文件。
-
make ${build_opts}: 使用 make 命令编译 Linux 内核。
-
make ${build_opts} bindeb-pkg: 使用 make 命令生成内核deb包。
可能需要修改的参数是:
-
build_opts=”-j 16”:默认使用16线程,需要根据实际电脑性能修改。
-
build_opts=”${build_opts} KDEB_PKGVERSION=1.$(date +%g%m)${DISTRO}”:设置了内核deb包的版本号,默认是数字+当前年份后两位加上当前月份后跟stable,如果是大版本更新可修改前面的数字,如果是当月频繁更新,可以将$(date +%g%m)修改为$(date +%g%m%d),添加具体日期,如果是测试版,可修改stable为beta等。
执行以下命令编译内核deb包:
./make_deb.sh
#编译成功打印消息如下
INSTALL debian/headertmp/usr/include/linux/sunrpc/ (1 file)
INSTALL debian/headertmp/usr/include/linux/tc_act/ (15 files)
INSTALL debian/headertmp/usr/include/linux/tc_ematch/ (5 files)
INSTALL debian/headertmp/usr/include/linux/usb/ (13 files)
INSTALL debian/headertmp/usr/include/linux/wimax/ (1 file)
INSTALL debian/headertmp/usr/include/asm/ (38 files)
dpkg-deb: 正在 '../linux-headers-4.19.35-imx6_1stable_armhf.deb' 中构建软件包 'linux-headers-4.19.35-imx6'。
dpkg-deb: 正在 '../linux-libc-dev_1stable_armhf.deb' 中构建软件包 'linux-libc-dev'。
dpkg-deb: 正在 '../linux-image-4.19.35-imx6_1stable_armhf.deb' 中构建软件包 'linux-image-4.19.35-imx6'。
dpkg-genbuildinfo --build=binary
dpkg-genchanges --build=binary >../linux-upstream_1stable_armhf.changes
dpkg-genchanges: 警告: 控制文件声明了包 linux-image-4.19.35-imx6-dbg 但它却不在文件列表中
dpkg-genchanges: info: binary-only upload (no source code included)
dpkg-source --after-build build
dpkg-buildpackage: info: binary-only upload (no source included)
make[1]: 离开目录“/home/qinghui/embedfire/ebf-image-builder/ebf_linux_kernel/build_image/build”
构成生成的deb包在内核源码/build_image目录下。
2.8. 镜像组成
NXP官方镜像(后简称官方镜像)与鲁班猫镜像(后简称lubancat),我们将kernel,dtb,dtbo打包进了rootfs内,这样更为通用且更换其中如何一部分只需将文件替换即可。官方镜像则是烧写指定地址对于不同芯片存储的地址是不同的不利于移植于其他芯片。
3. Linux内核的烧录
3.1. USB烧写Linux内核(EMMC/NAND通用)
3.1.1. 环境搭建
首先下载mfgtool工具 详细可参考 烧写环境的搭建 中的 USB方式烧写的搭建 搭建好环境
3.1.2. 烧写一个镜像
烧写完毕后通过USB OTG连接电脑会存在一个BOOT分区如图
然后将我们在上一章编译的zImage拷贝到BOOT\kernel目录下,将zImage更名为vmlinuz-4.19.35-imx6
然后再建立个软件连接
ln -s /lib/modules/4.19.35-imx6 /lib/modules/4.19.35+
reboot
注意: -4.19.35-imx6 为版本后缀,我这里使用的是4.19.35举例,往后可以更改为相应的版本后缀
3.2. DEB包烧写Linux内核(SD卡/EMMC/NAND通用)
将编译出来的 build_image/linux-image-4.19.35-imx6_1.xxxstable_armhf.deb
内核安装包拷贝到开发板上面,使用 dpkg
命令即可替换板子上的内核,如下所示。
#查看当前的内核
root@npi:~# dpkg -l | grep imx
ii kobs-ng-for-imx6ull 0.1.1 armhf NandFlash Tools
rc linux-image-4.19.35-imx6 2.2LubanCat armhf Linux kernel, version 4.19.35-imx6
#卸载当前内核
sudo dpkg -r linux-image-4.19.35-imx6
#若使用dpkg安装内核deb包提示错误,删除/boot/kernel目录下所有文件
sudo rm /boot/kernel/*
#安装新内核
sudo dpkg -i linux-image-4.19.35-imx6_1stable_armhf.deb
#打印消息如下
root@npi:/home/debian# sudo dpkg -i linux-image-4.19.35-imx6_1stable_armhf.deb
Selecting previously unselected package linux-image-4.19.35-imx6.
(Reading database . 12162 files and directories currently installed.)
Preparing to unpack linux-image-4.19.35-imx6_1stable_armhf.deb ...
Unpacking linux-image-4.19.35-imx6 (1stable) ...
Setting up linux-image-4.19.35-imx6 (1stable) ...
update-initramfs: Generating /boot/initrd.img-4.19.35-imx6
zz-uenv_txt: Updating /boot/uEnv.txt [uname_r=4.19.35-imx6]
通过 cat /proc/version 命令查看内核版本,可通过编译主机以及编译时间判断内核是否替换成功, 如下
root@npi:~ cat /proc/version
Linux version 4.19.35-imx6 (root@zhan) (gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04)) #1stable SMP PREEMPT Tue Apr 20 10:25:18 UTC 2021
若编译失败请尝试使用 sudo make mrproper 清除编译信息再次尝试, 多次编译失败请重新下载内核源码后再试。
3.3. 直接替换Linux内核(SD卡/EMMC/NAND通用)
将编译出来的 ebf_linux_kernel/arch/arm/boot/zImage 的拷到板子上,重命名为vmlinuz-4.19.35-imx6并替换/boot/kernel/vmlinuz-4.19.35-imx6
然后再建立个软件连接,原因是直接编译内核的版本号少了后缀,不能匹配当前/lib/modules/的驱动模块,加了软连接将旧版本的驱动模块软连接到新版本,即可匹配。
ln -s /lib/modules/4.19.35-imx6 /lib/modules/4.19.35+
reboot
3.4. TFTP烧写Linux内核(SD/EMMC/NAND通用,一般用于测试)
3.4.1. 环境搭建
详细可参考 烧写环境的搭建 中的 TFTP网络系统的搭建 搭建好环境
3.4.2. 启动板子进入uboot中
上电快速按回车进入Uboot
setenv gatewayip '192.168.103.254'
setenv netmask '255.255.255.0'
setenv ipaddr '192.168.103.142'
setenv serverip '192.168.103.243'
setenv ethaddr '32:34:46:78:9A:DC'
saveenv
注意
这里的 192.168.103 为局域网IP前缀(局域网的网络地址),要根据你网络情况填写
这里的 192.168.103.243 为TFTP服务器的IP,要根据你网络情况填写
这里的 192.168.103.142 为板子的IP,要设置为一个未被使用的IP,要根据你网络情况填写
这里的 255.255.255.0 为子网掩码,一般为255.255.255.0,如不对请联系你们网络管理员
我们试一下开发板能否ping通到主机 ping 192.168.103.243
然后将我们编译的zImage放到nfs目录下
SD卡和EMMC板的在uboot下运行如下代码
run args_mmc_old;
tftp 0x80800000 zImage;
load mmc 1:2 0x83000000 /usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dtb;
bootz 0x80800000 - 0x83000000;
NAND板的在uboot下运行如下代码
ubi part rootfs;
ubifsmount ubi0;
ubifsload 0x84000000 /lib/firmware/fatboot.img;
tftp 0x80800000 zImage;
ubifsload 0x83000000 /usr/lib/linux-image-4.19.35-imx6/imx6ull-nand-npi.dtb;
load ramblock 0:1 0x88000000 /kernel/initrd.img-4.19.35-imx6;
bootz 0x80800000 0x88000000:${filesize} 0x83000000;
启动后执行 cat /proc/version 查看当前内核信息
4. 添加驱动模块到Linux内核
大部分有修改内核需求的用户基本上都是对于驱动的修改或者是对于设备树的修改, 其中设备树目录在 arch/arm/boot/dts 目录下,设备树插件目录在 arch/arm/boot/dts/overlays 目录下, 驱动目录在ebf_linux_kernel/drivers下, 每个不同的驱动占用一个子目录,如char、block、 net、 mtd、 i2c等。
下面介绍如何往内核中添加一个简单的驱动模块,在drivers目录下存在一个 ebf_module目录,我们将在这个目录下添加新的驱动模块, 在ebf_module目录下新建hello_module目录,并在hello_module目录下新建 hello_module.c 以及Makefile文件,目录结构如下所示
.
├── dht11
│ ├── dht11.c
│ ├── dht11.h
│ ├── Makefile
│ └── test_app.c
├── ds18b20
│ ├── ds18b20.c
│ ├── ds18b20.h
│ ├── ds18b20.sh
│ ├── Makefile
│ └── test_app.c
├── hello_module
│ ├── hello_module.c
│ └── Makefile
├── infrared
│ ├── infra.c
│ ├── infra.h
│ ├── Makefile
│ └── test_app.c
├── Kconfig
└── Makefile
4.1. hello_module.c文件
hello_module.c文件为模块的主要内容,编写一些内容。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_EMERG "[ KERN_EMERG ] Hello Module Init\n");
printk( "[ default ] Hello Module Init\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("[ default ] Hello Module Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL2");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("hello world module");
MODULE_ALIAS("test_module");
4.2. hello_module/Makefile文件
hello_module/Makefile文件内容如下
obj-$(CONFIG_HELLO_MODULE) := hello_module.o
4.3. Makefile文件
除了hello_module/Makefile文件之外还需要修改上一层Makefile的文件的内容, 添加内容如下
#core
obj-y += dht11/
obj-y += ds18b20/
obj-y += infrared/
obj-y += hello_module/
4.4. Kconfig文件
添加kconfig文件内容如下
menu "Embedfire Modules"
menuconfig EBF_MODULE
bool "Embedfire Modules"
if EBF_MODULE
config EBF_DHT11
tristate "dht11"
config DS18B20
tristate "ds18b20"
config INFRARED
tristate "infrared"
config HELLO_MODULE
tristate "hello_module"
endif # EBF_MODULE
endmenu
4.5. 重新配置内核
执行如下命令, 找到hello_module的配置选项, hello_module配置路径为 Device Drivers > Embedfire Modules > Embedfire Modules 选择编译进内核的方式编译。
make menuconfig KCONFIG_CONFIG=arch/arm/configs/npi_v7_defconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
配置如下
arch/arm/configs/npi_v7_defconfig - Linux/arm 4.19.35 Kernel Configuration
> Device Drivers > Embedfire Modules > Embedfire Modules ──────────────────────
┌─────────────────────────── Embedfire Modules ────────────────────────────┐
│ Arrow keys navigate the menu. <Enter> selects submenus ---> (or empty │
│ submenus ----). Highlighted letters are hotkeys. Pressing <Y> │
│ includes, <N> excludes, <M> modularizes features. Press <Esc><Esc> to │
│ exit, <?> for Help, </> for Search. Legend: [*] built-in [ ] excluded │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ --- Embedfire Modules │ │
│ │ <M> dht11 │ │
│ │ <M> ds18b20 │ │
│ │ <M> infrared │ │
│ │ <M> hello_module │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ <Select> < Exit > < Help > < Save > < Load > │
└──────────────────────────────────────────────────────────────────────────┘
选择保存后,重新执行
sudo ./make_deb.sh
编译新的内核deb安装包, 并通过以上介绍过方式将deb安装包重新安装到开发板上。 在开发板子上可看到
/lib/modules/4.19.35-carp-imx6/kernel/drivers/ebf_module/hello_module/hello_module.ko
文件。
5.修改Linux内核启动logo
开发板开机时,若有连接屏幕,会在屏幕上显示内核默认的logo(小企鹅), 若想要修改启动logo,可以按照以下步骤操作。
5.1. 准备一张图片
选择自己想要的修改的logo图片,例如这里选择ubuntu的logo, 将它制作成适合显示屏大小的图片,比如5寸屏幕的分辨率是800*480:
然后将其保存为 256色(即8位色)的bpm格式的图片 ,可以在Windows下或者Linux虚拟机下编辑:
5.2. 转换为ppm格式的图片
1、安装格式转换工具
sudo apt update
sudo apt install netpbm
2、在Linux下使用以下脚本将其转换为ppm格式的文件,为什么是ppm格式呢? 因为这是编译Linux内核必要的文件格式,想要修改logo,就要这种格式的文件, 它必须是 256色(即8位色)的bpm格式的图片 转换而成的。
#!/bin/bash
if [ " $1" == " " ];
then
echo "usage:$0 bmp_file"
exit 0
fi
if [ -f "$1" ]
then
echo $1
else
echo "no find file [$1]"
exit 0
fi
name=${1%%.*}
bmptopnm $1 > $name.pnm
pnmquant 224 $name.pnm > $name.clut224.pnm
pnmtoplainpnm $name.clut224.pnm > $name.ppm
rm $name.pnm $name.clut224.pnm
这是bmp文件转换ppm格式文件的脚本,可以将其写入一个叫 bmp2ppm.sh
脚本文件中, 并且赋予其可执行的权限(使用 chmod +x bmp2ppm.sh
命令即可).
然后将准备好的bmp文件拷贝到制作ppm文件的目录下,使用 bmp2ppm.sh
脚本将其转化为ppm文件,具体操作如下:
zhan@zhan:~$ ls
bmp2ppm.sh ubuntu.bmp ubuntu.ppm
zhan@zhan:~$ ./bmp2ppm.sh ubuntu.bmp
ubuntu.bmp
bmptopnm: Windows BMP, 800x480x8
bmptopnm: WRITING PPM IMAGE
pnmcolormap: making histogram...
pnmcolormap: 29 colors found
pnmcolormap: Image already has few enough colors (<=224). Keeping same colors.
pnmremap: 29 colors found in colormap
zhan@zhan:~$
5.3. 替换原本的logo文件
1、在转换完成后,当前目录将出现对应的ppm文件, 我们将其拷贝到linux内核源码的 ebf_linux_kernel/drivers/video/logo
目录下, 因为我们的logo是存放在此处的,默认使用的logo为 logo_linux_clut224.ppm
2、将我们编译得到的ppm文件重命名为logo_linux_clut224.ppm,替换掉内核原先的logo_linux_clut224.ppm文件。
3、按照上面的编译步骤,重新编译内核,将得到的内核安装到开发板上即可。
5.4. 修改启动脚本和开机背景图
启动开发板,内核启动后会执行文件系统的启动脚本, 而此时文件系统的启动脚本中 /opt/scripts/boot/psplash.sh
会去执行相应的应用程序 /usr/bin/psplash
,显示开机时的背景。
如下图:
若只想显示开机logo可以将这行注释掉,或者将 /lib/fireware/logo.bmp
修改为其他的图片路径 (可以将自己的图片用windows系统自带画图软件另存为24位bmp放/lib/firmware里面)。