在之前的博文 Linux 之八 完整嵌入式 Linux 环境、(交叉)编译工具链、CPU 体系架构、嵌入式系统构建工具 中说了要一步步搭建整个嵌入式 Linux 运行环境,今天就开始编译 Linux Kernel。
架构 Linux 最初是为基于 32 位 x86 架构的 PC 开发的。如今,它已经可以工作在了 Compaq Alpha AXP、Sun SPARC、Motorola 68000、PowerPC、ARM、Hitachi SuperH、IBM S/390、MIPS、HP PA-RISC,Intel IA-64 等众多平台之上。
Linux Kernel 将功能划分为了各种子系统,子系统之间相对独立。其中,最为核心的应该就是下面的 5 个子系统,这 5 大子系统也是在众多书籍中的常客。但是,随着 Linux Kernel 的不断发展,新的子系统也在不断出现。
- Process Scheduler:也称作进程管理、进程调度。负责管理 CPU 资源,以便让各个进程可以以尽量公平的方式访问 CPU。
- Memory Manager:内存管理。负责管理 Memory(内存)资源,以便让各个进程可以安全地共享机器的内存资源。另外,内存管理会提供虚拟内存的机制,该机制可以让进程使用多于系统可用 Memory 的内存,不用的内存会通过文件系统保存在外部非易失存储器中,需要使用的时候,再取回到内存中。
- VFS(Virtual File System):虚拟文件系统。Linux 内核将不同功能的外部设备,例如 Disk 设备(硬盘、磁盘、NAND Flash、Nor Flash等)、输入输出设备、显示设备等等,抽象为可以通过统一的文件操作接口(open、close、read、write等)来访问。这就是 Linux 系统“一切皆是文件”的体现(其实 Linux 做的并不彻底,因为 CPU、内存、网络等还不是文件,如果真的需要一切皆是文件,还得看贝尔实验室正在开发的 "Plan 9” 的)。
- Network:网络子系统。负责管理系统的网络设备,并实现多种多样的网络标准。
- IPC(Inter-Process Communication):进程间通信。IPC 不管理任何的硬件,它主要负责 Linux 系统中进程之间的通信。
FHS(Filesystem Hierarchy Standard,文件系统层次结构标准)是多数 Linux 发行版采用这种文件组织形式,FHS 定义了系统中每个区域的用途、所需要的最小构成的文件和目录同时还给出了例外处理与矛盾处理。
FHS 是根据以往无数 Linux 用户和开发者的经验总结出来的,FHS 是依据文件系统使用的频繁与否以及是否允许用户随意改动而持续更新的。FHS 由 Linux 基金会维护,最新版本是 2015 年 6 月 3 日发布的 3.0。
大多数 Linux 发行版都遵循 FHS,但对于某些目录可能进行合并。
FHS 定义了两层规范,第一层是 / 下面的各个目录应该要放什么文件数据,例如,/etc应该要放置设置文件,/bin 与 /sbin 则应该要放置可执行文件等等。 第二层则是针对 /usr 及 /var 这两个目录的子目录来定义。例如 /var/log 放置系统登录文件、/usr/share 放置共享数据等等。
-
/bin:Binaries 的缩写,存放一些可以被 root 与一般用户所使用的可执行程序,例如 cat、chmod、chown、date、mv、mkdir、cp、bash 等常用的指令。
Ubuntu 中,它是一个指向 /usr/bin 的硬链接
-
/boot:放置开机会使用到的文件,包括 Linux 核心文件以及开机菜单与开机所需配置文件等等。
操作系统内核必须存在根目录或者 /boot 目录
-
/dev:在 Linux 系统上,任何设备与周边设备都是以文件的型态存在于这个目录当中的。 通过存取这个目录下面的某个文件,就等于存取某个设备了。
-
/etc:系统主要的配置文件几乎都放置在这个目录内,例如人员的帐号密码档、各种服务的启始档等等。一般来说,这个目录下的各文件属性是可以让一般使用者查阅的, 但是只有 root 有权修改。
- /etc/opt:这个目录在放置 /opt 中的第三方协力软件 的相关配置文件
- /etc/sgml:与 SGML 格式有关的各项配置文件
- /etc/X11:与 X Window 有关的各种配置文件都在这里,例如, X Server 的配置文件 xorg.conf。
- /etc/xml:与 XML 格式有关的各项配置文件
-
/lib:系统的函数库非常的多,而 /lib 放置的则是在开机时会用到的函数库, 以及在 /bin 或 /sbin 下面的可执行程序会调用的函数库而已。
- Ubuntu 中,它是一个指向 /usr/bin 的硬链接
- 与不同架构对应,lib 通常分为 lib32、lib64 等
- /lib/modules/:主要放置可抽换式的核心相关模块(驱动程序)。
-
/media:media 是“媒体”的英文,用于放置的就是可移除的设备啦! 包括软盘、光盘、DVD 等等设备都暂时挂载于此。常见的文件名有:/media/floppy、/media/cdrom 等等。
-
/mnt:暂时挂载某些额外的设备一般放置到这个目录中。在古早时候,这个目录的用途与/media相同,有了 /media 之后,这个目录就用来暂时挂载用了。
-
/opt:用于存放那些第三方非系统软件。常见的就是那些 GUI 软件,例如那些基于 KDE 的 GUI 程序。
在以前的 Linux 系统中,经常是放置在 /usr/local 目录下!
-
/run:早期的 FHS 规定系统开机后所产生的各项信息应该要放置到 /var/run 目录下,新版的 FHS 则规范到 /run 下面。 由于 /run 可以使用内存来仿真,因此性能上会好很多!
-
/sbin:Linux 有非常多指令是用来设置系统环境的,这些指令只有 root 才能够利用来“设置”系统,其他使用者最多只能用来“查询”而已。 放在 /sbin 下面的为开机过程中所需要的,里面包括了开机、修复、还原系统所需要的指令。 常见的指令包括:fdisk、fsck、ifconfig、mkfs 等等。
Ubuntu 中,它是一个指向 /usr/sbin 的硬链接
-
/srv:可以视为 service 的缩写,是一些网络服务启动之后,这些服务所需要取用的数据目录。 常见的服务例如 WWW、FTP 等等。例如,WWW 服务器需要的网页数据就可以放置在 /srv/www/ 里面
-
/tmp:这是让一般使用者或者是正在执行的程序暂时放置文件的地方。 这个目录是任何人都能够存取的,重要数据不可放置在此目录!
FHS 甚至建议在开机时,应该要将 /tmp 下的数据都删除。
-
/usr:是 Unix Software Resource 的缩写, 也就是 Unix 操作系统软件资源所放置的目录。FHS 建议所有软件开发者,应该将他们的数据合理的分别放置到这个目录下的次目录,而不要自行创建该软件自己独立的目录。
-
/usr/bin:非必要的命令二进制文件(在单用户模式下不需要);面向所有用户。
-
/usr/include:各种标准的头文件
-
/usr/lib:/usr/bin 和 /usr/sbin 中的可执行程序使用的库文件。
与不同架构对应,lib 通常分为 lib32、lib64 等
-
/usr/local:特定于本主机的本地数据的三级层次结构,通常具有进一步的子目录(例如,bin,lib,Share)。那些使用源码方式安装(make)的软件通常就放到这个目录下。
-
/usr/sbin:非必要的系统二进制文件(例如,各种网络服务的守护进程)。
-
/usr/share:与体系结构无关的(共享)数据。
-
/usr/src:该Linux 发行版使用的 Kernel 源代码
-
-
/var:主要针对常态性变动的文件,包括高速缓存(cache)、登录文件(log file)以及某些软件运行所产生的文件, 包括程序文件(lock file、run file),或者例如 MySQL 数据库的文件等等。
- /var/cache:应用程序缓存数据。此类数据是由于耗时的 I/O 或计算而在本地生成的。应用程序必须能够重新生成或还原数据。可以删除缓存的文件而不会丢失数据。
- /var/lib:状态信息。程序在运行时修改的持久性数据(例如,数据库、打包系统元数据等)
- /var/lock:锁定文件。跟踪当前正在使用的资源的文件。
- /var/log:日志文件。里面有各种日志。
- /var/mail:邮箱文件。在某些发行版中,这些文件可能位于已弃用的 /var/spool/mail 中
- /var/opt:存储在 /opt 中的附加程序包中的变量数据。
- /var/run:运行时变量数据。此目录包含自启动系统以来描述系统的系统信息数据。在 FHS 3.0 中,/var/run 被 /run 取代,但为了向后兼容,系统应该继续提供 /var/run 目录或者提供从 /var/run到 /run 的符号链接。
- /var/tmp:在重新启动之间要保留的临时文件
-
/home:这是系统默认的使用者主文件夹(home directory)。新增一个一般使用者帐号时, 默认的使用者主文件夹都会规范到这里来。
通常使用 ~ 表示。
-
/root:系统管理员(root)的主文件夹。之所以放在这里,是因为如果进入单人维护模式而仅挂载根目录时, 该目录就能够拥有 root 的主文件夹,所以我们会希望 root 的主文件夹与根目录放置在同一个分区中。
-
/lost+found:这个目录是使用标准的 ext2/ext3/ext4 文件系统格式才会产生的一个目录,目的在于当文件系统发生错误时, 将一些遗失的片段放置到这个目录下。如果使用的是 xfs 文件系统的话,就不会存在这个目录了!
-
/proc:这个目录本身是一个虚拟文件系统(virtual filesystem),他放置的数据都是在内存当中, 例如系统核心、行程信息(process)、周边设备的状态及网络状态等等。因为这个目录下的数据都是在内存当中, 所以本身不占任何硬盘空间!比较重要的文件例如:/proc/cpuinfo、/proc/dma、/proc/interrupts、 /proc/ioports、 /proc/netlib/ 目录下。
注意这里的库函数和 C 语言的库函数不一样的。在内核编程中是不能用 C 语言标准库函数,这里的 lib目录下的库函数就是用来替代那些标准库函数的。例如,在内核中要打印信息时不能用 printf,而要用 printk,这个 printk 就在 lib 目录下。 -
LICENSES:该目下存放了 Kernel 所遵循的许可证原始文件。我们都知道,Linux Kernel 是基于 GPLv2 许可证进行开源的。GPLv2 许可证是涵盖整个内核分发的许可证,但是具体到某个源码文件时,可以有不同的许可证。除此之外,个人文件可以在双重许可下提供。但是,一个前提是这些不同的的许可证必须兼容 GPLv2。
一个例外就是系统调用:编写用户空间程序时肯定得包含 Kernel 的源文件,根据 GPLv2 传染性意味着别人的用户程序也得开源,显然不合适。为此,在 LICENSES/exceptions/Linux-syscall-note 中显式描述了这个情况。所以,Linux Kernel 的许可证准确的应该叫 SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note 才更合适。https://docs.kernel.org/process/license-rules.html
在 Kernel 中,源码文件的许可方式是根据文件类型的不同,在文件的开头以注释的形式显示增加 SPDX-License-Identifier: GPL-2.0-only。Linux Kernel 要求在所有源文件中提供精确的 SPDX 标识符且标识的许可证必须是在 LICENSES 目录下已经存在的。
SPDX 许可证标识符是计算机可解析的,是贡献文件内容所依据的许可证的精确简写。SPDX 许可证标识符由 Linux 基金会的 SPDX 工作组管理,并已得到整个行业的合作伙伴、工具供应商和法律团队的同意。有关详细信息参阅 https://spdx.org/。 -
mm: memory management,内存管理,linux 的内存管理代码都在这里
-
net:该目录下是网络相关的代码,譬如TCP/IP协议栈等都在这里。
-
samples:示例
-
scripts:这个目录下是一些开发 Kernel 本身使用的脚本文件,linux 内核工作时是不会使用的。主要是用来辅助对 linux 内核进行开发(配置、编译等)。
-
security:安全相关的代码。
-
sound:音频处理相关的。
-
tools:linux 中用到的一些有用工具
-
usr:目录下是 initramfs 相关的,和 linux 内核的启动有关
-
virt:这个目录下是 KVM (Kernel Virtual Machine) 管理程序的相关代码
-
.clang-format:.clang-format 是 LLVM 提供的针对 C/C++ 代码的格式化工具 clang-format 的配置文件,包含了一些适用于 Kernel 内核编码风格的 clang-format 的选项。当我们在 Kernel 源码根目录下使用 clang-format 时,就会自动加载该配置文件中的配置项。.clang-format 中配置的代码风格如下所示:
.clang-format 中的配置只是尽可能多的去符合 Kernel 的编码风格。当前的 Kernel 的代码中,很多代码格式都不符合 .clang-format 中配置的要求(上图中黄色表示不符合的提示)。并且,某些子系统的代码风格本身就与整体 Kernel 代码风格也存在差异。- Linux kernel coding style
- clang-format 在主流 Linux 发行版中都有提供,可以非常方便的进行安装。例如,Ubuntu 中 sudo apt install clang-format
-
.cocciconfig:.cocciconfig 是 Linux 下的一个可定制程度很高的静态代码分析工具 Coccinelle 的配置文件,包含对 Coccinelle 的配置参数。在 Kernel 源码的 script 目录下提供了一个脚本 script/coccicheck 供开发者使用 Coccinelle 来检测 kernel 代码。此外,根目录的 Makefile 中也专门创建了一个 coccicheck 目标。因此,可以直接使用 make coccicheck。
Coccinelle 运行依赖 Spatch 程序 和 Semantic Patch Language 文件(扩展名 *.cocci 文件)。当我们执行 script/coccicheck 时,就会调用 Spatch 使用 script/coccinelle/xxx/.cocci 语法文件来分析代码,每一个 .cocci 都是一个类型的错误检测。- Linux Kernel 介绍文档:https://docs.kernel.org/dev-tools/coccinelle.html
- 官网:coccinelle.lip6.fr
- 源码:https://github.com/coccinelle/coccinelle
- 主流 Linux 发行版都会提供该工具。例如,Ubuntu:sudo apt install coccinelle
-
.get_maintainer.ignore:用于在使用 scripts/get_maintainer.pl 获取 Kernel 维护者的时候忽略此文件中列出的人员。
-
.gitattributes:.gitattributes 是 Git 的配置文件之一,文件中的每一行定义一个/类文件/路径的若干属性,基本格式:文件 属性1 属性2 ...。其中,文件名支持使用通配符(例如,*.c 表示所有 c 语言源码文件);属性有很多,常用的有 text、diff、eol 等。
git 官方文档:https://git-scm.com/docs/gitattributes
-
.gitignore:.gitignore 文件用来告诉 Git 忽略被记录在 .gitignore 文件里的文件或文件夹的改动。被忽略的文件是不会被放入到远程仓库里的。注意,如果文件已经存在于远程仓库中,是无法通过 .gitignore 文件来忽略的
git 官方文档:https://git-scm.com/docs/gitignore
-
.mailmap:这个文件主要是记录了一些大神维护内核的名字和邮箱。git-shortlog 使用这个列表来修复 git 归档文件中一些糟糕的名称翻译,要么是因为作者的全名被弄乱了,要么是因为作者的书写方式不总是相同,导致同一个人的贡献看起来不是这样或显示得不好。也允许旧的电子邮件地址映射到新的电子邮件地址。
-
COPYING:Kernel 的版权说明,指出了 Kernel 使用的许可证。
-
CREDITS:自 1994 年 3 月 13 日起,CREDITS 文件就开始被包含在了 Kernel 根目录下。该文件中记录了为 Kernel 做过贡献的开发者的主要贡献领域、电子邮件和实际地址以及其他相关数据等基本信息。
-
Kbuild 和 Kconfig:K 指的是 Kernel,Kbuild 和 Kconfig 是 Kernel 的配置及编译构建系统。Kconfig 用来配置内核,它就是各种配置界面的源文件,内核的配置工具读取各个 Kconfig 文件,生成配置界面供开发人员配置内核,最后生成配置文件 .config;Kbuild 则用于编译内核。
- Kernel Build System
- Kbuild 和 Kconfig 就是对 Make 的进一步扩展
-
MAINTAINERS:这个文件列出了 Kernel 中每个子系统的维护者的基本信息。同时也给出了如何正确提交对内核的更改(PATCH)。
-
Makefile:它是 Kernel 配置及构建的入口。Kernel 使用 make 系统来管理构建过程。
-
README:这个文件就是一个简单的对于 Kernel 的自我介绍。由于 Kernel 包含了详细的文档,因此,该文件中并没有啥实质性的内容,就是作为一个入口,导航的其他介绍文档。
Linux Kernel 的构建过程与 U-Boot 构建过程是一模一样的。因为 U-Boot 的构建就是采用的 Linux Kernel 的 Kconfig/Kbuild 构建系统。具体见博文 U-Boot 之一 零基础编译 U-Boot 过程详解 及 编译后的使用说明 。
主机环境 我这里使用主机操作系统是 Ubuntu 22.04 LTS,交叉编译工具链是 ARM 发布的最新版 11.2 版。在编译 Kernel 的过程中,我们可能还需要安装其他一些依赖工具,这个在后面用到的时候缺啥装啥就可以(出现各种错误的时候再安装相应工具即可)。
注意,由于在之前在编译 U-Boot 时,我已经安装了大部分构建过程中需要的工具,因此,这里可能没有太多需要重新安装的工具了。如果出现错误,具体见博文 U-Boot 之一 零基础编译 U-Boot 过程详解 及 编译后的使用说明 应该有解决方法。
在开始编译之前,我们先介绍两个命令:make clean 用于清空编译中间文件 和 make distclean 用于清除所有编译产生的文件。如果我们想要重新编译,可以使用以上两个命令清理环境。这两个命令均定义在 Kernel 源码根目录的 Makefile 中。
- 第一步肯定是获取 Kernel 的源代码,我这里采用的是最新的 LTS 版:5.15.45。注意,最好不要选择最新的版本。还有,解压的时候没有显示进度,解压过程时间较长,耐心等待。
成功下载并解压源代码之后,我们需要进入解压的 Kernel 源码目录下,使用命令:cd linux-5.15.45。此后就在 Kernel 源码目录下进行各种操作。 - 第二步就是生成配置,直接使用命令:make ARCH=arm CROSS_COMPILE=arm-none-eabi- stm32_defconfig。
- 第三步修改配置(裁剪)。直接使用命令:make ARCH=arm CROSS_COMPILE=arm-none-eabi- menuconfig。
需要特殊注意的是,每一个都必须带有 ARCH=arm CROSS_COMPILE=arm-none-eabi- 这俩参数,否则不能正常按照我们指定的架构来显示配置。正常就会进入下面左侧的配置界面了。
这里我们需要更改一些参数: “System Type” > “DRAM Base” = 0xc0000000 and “DRAM Size” = 0x01000000,以及将 “Boot options” -> “Kernel Execute-In_Place From ROM” 前面的型号去掉。最后保存退出即可。
- 第四步就是真正的编译了,直接使用命令:make ARCH=arm CROSS_COMPILE=arm-none-eabi-,然后就是漫长的等待…
- 正常编译完成之后,就会在 Kernel 源码的根目下生成我们真正需要的 arch/arm/boot/zImage 和 arch/arm/boot/dts/stm32f769-disco.dtb 这两个文件了。
-
vmlinux:vm 是 Virtual Memory 的缩写,它是由用户对内核源码编译出来的最原始的内核文件( ELF(Executable and Linkable Format)格式文件)。vmlinuz 是可引导的、可压缩的内核镜像。
当我们编译完 Kernel 后,在源码根目录下就会生成该文件
-
Image:它经过 objcopy 处理的只包含二进制数据的内核代码,但这种格式的内核镜像还没有经过压缩。
编译完 Kernel 后,在对应架构的 boot(例如,archarmboot) 目下就会生成该文件
-
zImage:它是由 vmlinux 加上解压代码经 gzip 压缩而成,是 ARM linux 常用的一种压缩镜像文件。
- 编译完 Kernel 后,在对应架构的 boot(例如,archarmboot) 目下就会生成该文件
- 这种格式的 Linux 镜像文件多存放在 NAND 上
-
bzImage:bz 表示 big zImage,其格式与 zImage 类似,但采用了不同的压缩算法,bzImage 的压缩率更高。
-
uImage:这是 uboot 专用的镜像文件,它是在 zImage 之前加上一个长度为 0x40 的头信息(tag),在头信息内说明了该镜像文件的类型、加载位置、生成时间、大小等信息。换句话说,若直接从 uImage 的 0x40 位置开始执行,则 zImage 和 uImage 没有任何区别。
- 编译完 Kernel 后,在对应架构的 boot(例如,archarmboot) 目下就会生成该文件
- 这种格式的 Linux 镜像文件多存放在 NAND 上
-
xipImage:xip 是 Execute In Place 的缩写,这种格式的 Linux 镜像文件多存放在 NorFlash 上,且运行时不需要拷贝到内存 SDRAM中,可以直接在 NorFlash 中运行。
- https://firstmonday.org/ojs/index.php/fm/article/view/1151/1071
- https://www.csdn.net/tags/MtzaIg0sNDc2MTEtYmxvZwO0O0OO0O0O.html
- https://www.shuzhiduo.com/A/ke5j1RByJr/
- https://linux-kernel-labs.github.io/refs/heads/master/lectures/intro.html
- https://lwn.net/Articles/507794/
- https://lwn.net/Articles/507832/
- https://james-hui.com/2021/07/02/building-a-small-uboot-linux-and-rootfs-for-arm-cortex-m7/
- https://blog.csdn.net/King_Rose/article/details/121512359
- https://blog.csdn.net/m0_47799526/article/details/106203517
10.https://blog.csdn.net/lpwsw/article/details/122098847



