Linux内核学习(五):linux kernel源码结构以及makefile分析

简介: Linux内核学习(五):linux kernel源码结构以及makefile分析

Linux内核学习(五):linux kernel源码结构以及makefile分析

前面我们知道了linux内核镜像的生成、加载以及加载工具uboot。

这里我们来看看linux内核的源码的宏观东西,看看这个makefile文件内容。

本文内容全部来自韦神《嵌入式Linux应用开发完全手册》

1、内核源码结构

Linux 内核文件数目将近2万。这些文件的组织结构并不复杂,它们分别位于顶层目录下的17个子目录,各个目录功能独立。表16.2描述了各目录的功能,最后2个目录不包含内核代码。所以多,但是不用怕。

整个栗子看看?

对于ARM 架构的S3C2410、S3C2440,其体系相关的代码在 arch/arm/目录下,在后面进行Linux移植时,开始的工作正是修改这个目录下的文件。如图16.2所示为内核代码的层次结构。所以还可以根据自己的产品在开源的上面形成一些自定义的东西,打造产品的专属内核。

2、Linux Makefile分析

makefile这个文件其实就是解释你的make命令怎么执行的,基于arm的结构比如安卓,还会涉及到一个.mk文件,这个我会写个make、makefile、.mk文件后面好好聊聊。言归正传:

内核中的哪些文件将被编译?

它们是怎样被编译的?

它们连接时的顺序如何确定?

哪个文件在最前面?

哪些文件或函数先执行?

这些都是通过Makefile 来管理的。从最简单的角度来总结 Makefile 的作用,有以下3点。

  • (1) 决定编译哪些文件。
  • (2)怎样编译这些文件?
  • (3)怎样连接这些文件,最重要的是它们的顺序如何?

Linux 内核源码中含有很多个Makefile文件,这些Makefile文件又要包含其他一些文件(比如配置信息、通用的规则等)。这些文件构成了Linux的 Makefile体系,可以分为表16.3中的5类。

内核文档Documentation/kbuild/makefiles.txt对内核中Makefile 的作用、用法讲解得非常透彻,以下根据前面总结的Makefile 的3大作用分析这5类文件。

(1)决定编译哪些文件。

Linux内核的编译过程从顶层Makefile开始,然后递归地进入各级子目录调用它们的Makefile,分为3个步骤。

  • a. 顶层 Makefile决定内核根目录下哪些子目录将被编进内核。
  • b. arch/$(ARCH)Makefile决定arch/S(ARCH)目录下哪些文件、哪些目录将被编进内核。
  • c. 各级子目录下的 Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编成模块(即驱动程序),进入哪些子目录继续调用它们的 Makefile。
先看步骤a,在顶层Makefile 中可以看到如下内容:

可见,顶层Makefile将这13个子目录分为5类: init-y、drivers-y、net-y、libs-y和 core-yo

表16.2中有17个子目录,除去 include目录和后面两个不包含内核代码的目录外,还有一个arch目录没有出现在内核中。

它在 arch/S(ARCH)/Makefile中被包含进内核,在顶层Makefile中直接包含了这个Makefile,如下所示:

对于ARCH变量,可以在执行make命令时传入,比如“make ARCH=arm …”。另外,对于非x86平台,还需要指定交叉编译工具,这也可以在执行make命令时传入,比如“make CROSS_COMPILE=arm-linux- …”。为了方便,常在顶层 Makefile 中进行如下修改。

对于步骤b的 arch/S(ARCH)/Makefile

以 ARM体系为例,在 arch/arm/Makefile中可以看到如下内容:

从第94行可知,除前面的5类子目录外,又出现了另一类: head-y,不过它直接以文件名出现。

MMUEXT在 arch/arm/Makefile前面定义,对于没有MMU的处理器,MMUEXT的值为-nommu,使用文件 head-nommu.S;对于有MMU的处理器,MMUEXT的值为空,使用文件head.S。

arch/arm/Makefile中类似第171、172、173行的代码进一步扩展了core-y的内容,第191行扩展了 libs-y的内容,这些都是体系结构相关的目录。

第173~175行中的CONFIGARCH_S3C2410 在配置内核时定义,它的值有3种: y、m或空。

y表示编进内核,m表示编为模块,空表示不使用。

编译内核时,将依次进入init-y、core-y、libs-y、drivers-y和 net-y所列出的目录中执行它们的Makefile,每个子目录都会生成一个built-in.o (libs-y所列目录下,有可能生成lib.a文件)。最后,head-y所表示的文件将和这些built-in.o、lib.a一起被连接成内核映象文件 vmlinux。

最后,看一下步骤c是怎么进行的。

在配置内核时,生成配置文件.config(具体过程后续会将)。

内核顶层Makefile使用如下语句间接包含.config 文件,以后就根据.config 中定义的各个变量决定编译哪些文件。

之所以说是“间接”包含,是因为包含的是include/config/auto.conf文件,而它只是将.config 文件中的注释去掉,并根据顶层 Makefile 中定义的变量增加了一些变量而已。

include/config/auto.conf 文件的生成过程不再描述,它与.config 的格式相同,摘选部分内容如下(注意,下面以“#”开头的行是本书加的注释):

在include/config/auto.conf 文件中,变量的值主要有两类:“y”和“m”。

各级子目录的Makefile使用这些变量来决定哪些文件被编进内核中,哪些文件被编成模块(即驱动程序);

要进入哪些下一级子目录继续编译,这通过以下4种方法来确定( obj-y、obj-m、lib-y是Makefile中的变量)。

(这里前两天看源码正在纳闷这个是什么标志?)

obj-y 用来定义哪些文件被编进( built-in)内核。

obj-y中定义的.o文件由当前目录下的.c或.S文件编译生成,它们连同下级子目录的built-in.o文件一起被组合成(使用“$(LD)-r”命令)当前目录下的 built-in.o文件。这个built-in.o文件将被它的上一层 Makefile 使用。

obj-y 中各个.o文件的顺序是有意义的,因为内核中用module_init()或._initcall定义的函数将按照它们的连接顺序被调用。

是不是我自己可以定义一个宏,然后加功能进去诶?

obj-m用来定义哪些文件被编译成可加载模块( Loadable module)。

obj-m 中定义的.o文件由当前目录下的.c或.S文件编译生成,它们不会被编进 built-in.o中,而是被编成可加载模块。

一个模块可以由一个或几个.o文件组成。对于只有一个源文件的模块,在 obj-m中直接增加它的.o文件即可。对于有多个源文件的模块,除在 obj-m中增加一个.o文件外,还要定义一个<module_name>-objs变量来告诉Makefile这个.o文件由哪些文件组成。

lib-y用来定义哪些文件被编成库文件。

lib-y中定义的.o文件由当前目录下的.c或.S文件编译生成,它们被打包成当前目录下的-个库文件: lib.a。

同时出现在: obj-y、lib-y 中的.o文件,不会被包含进lib.a 中。

要把这个lib.a编进内核中,需要在顶层 Makefile中 libs-y变量中列出当前目录。

要编成库文件的内核代码一般都在这两个目录下: lib/、arch/$(ARCH)/lib/。

obj-y、obj-m还可以用来指定要进入的下一层子目录。

Linux中一个Makefile 文件只负责生成当前目录下的目标文件,子目录下的目标文件由子目录的 Makefile 生成。

Linux 的编译系统会自动进入这些子目录调用它们的 Makefile,只是在这之前指定这些子目录。这要用到obj-y、obj-m,只要在其中增加这些子目录名即可。

上面知道了怎么看哪些文件需要编译,现在再接着来看看这些文件是怎么编译的。

(2)怎样编译这些文件

编译文件,那么肯定会有相应的编译选项、连接选项,这都是什么?这些选项分3类:

  • 全局的,适用于整个内核代码树;
  • 局部的,仅适用于某个Makefile 中的所有文件;
  • 个体的,仅适用于某个文件。

全局选项在顶层Makefile和 arch/$(ARCH)Makefile中定义,这些选项的名称为:

CFLAGS:编译C文件的选项

AFLAGS:编译汇编文件的选项

LDFLAGS:连接文件的选项

ARFLAGS:制作库文件的选项

需要使用局部选项时,它们在各个子目录中定义,名称为:

EXTRA_CFLAGS 、EXTRA_AFLAGS、EXTRA_LDFLAGS、EXTRA_ARFLAGS,它们的用途与前述选项相同只是适用范围比较小,它们针对当前Makefile中的所有文件。

另外,如果想针对某个文件定义它的编译选项,可以使用CFLAGS_KaTeX parse error: Expected group after '_' at position 9: @,AFLAGS_̲@。

前者用于编译某个C文件,后者用于编译某个汇编文件。

$@表示某个目标文件名,比如以下代码表示编译aha152x.c时,选项中要额外加上“-DAHA152X_STAT -DAUTOCONF"。

需要注意的是,这3类选项是一起使用的,在 scripts/Makefile.lib 中可以看到。

知道了这些编译选项,这些文件是怎么链接的,顺序是什么样的呢?

(3)怎样连接这些文件,它们的顺序如何。

前面分析有哪些文件要编进内核时,顶层 Makefile和 arch/$(ARCH)/Makefile定义了6类目录(或文件): head-y、init-y、drivers-y、net-y、libs-y和 core-y。

它们的初始值如下((以ARM体系为例)。

可见,除head-y 外,其余的init-y、drivers-y等都是目录名。

在顶层Makefile 中,这些目录名的后面直接加上: built-in.o 或lib.a,表示要连接进内核的文件,如下所示:

上面的patsubst是个字符串处理函数,它的用法如下:

表示寻找“text”中符合格式“pattern”的字,用“replacement”替换它们。比如上面的init-y初值为“init/”,经过第567行的交换后,“init-y”变为“init/built-in.o”。

顶层 Makefile 中,再往下看。

第604行的 vmlinux-all表示所有构成内核映象文件vmlinux的目标文件,

从第602~604行可知这些目标文件的顺序为: head-y、init-y、core-y、libs-y、drivers-y、net-y,即arch/arm/kernel/head.o(假设有MMU,否则为head-nommu.o )、arch/arm/kernel/ init_task.o、init/built-in.o、 usr/built-in.o等。

第605行表示连接脚本为arch/$(ARCH)/kernel/vmlinux.lds。对于ARM体系,连接脚本就是arch/arm/kernel/vmlinux.lds,它由arch/arm/kernel/vmlinux.lds.S文件生成,规则在scripts/Makefile.build 中,如下所示:

现将生成的arch/arm/kernel/vmlinux.lds摘录如下:

以上就是关于makefile文件的内容,下面对本节分析Makefile 的结果作一下总结。

  • ( 1)配置文件.config 中定义了一系列的变量,Makefile将结合它们来决定哪些文件被编进内核、哪些文件被编成模块、涉及哪些子目录。
  • (2)顶层Makefile和 arch/S(ARCH)Makefile决定根目录下哪些子目录、arch/S(ARCH)目录下哪些文件和目录将被编进内核。
  • (3)最后,各级子目录下的 Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编成模块(即驱动程序),进入哪些子目录继续调用它们的 Makefile。
  • (4)顶层 Makefilc和 arch/$(ARCH)/Makefile设置了可以影响所有文件的编译、连接选项:CFLAGS、AFLAGS、LDFLAGS、ARFLAGS。
  • (5)各级子目录下的 Makefile 中可以设置能够影响当前目录下所有文件的编译、连接选项:EXTRA_CFLAGS、EXTRA_AFLAGS、EXTRA_LDFLAGS、EXTRA_ARFLAGS;还可以设置可以影响某个文件的编译选项:CFLAGS_S@,AFLAGS_S@。
  • (6)顶层Makefile按照一定的顺序组织文件,根据连接脚本arch/$(ARCH) kernelvmlinux.lds 生成内核映象文件vmlinux。

以上就是整个流程,配置好编译生成了镜像,不过在第一步那里那个config文件是咋个工作和来的呢?下一篇就来看看生成.config的文件–Kcofig

目录
相关文章
|
16天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
57 4
|
5天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
23 1
|
10天前
|
算法 Linux 开发者
Linux内核中的锁机制:保障并发控制的艺术####
本文深入探讨了Linux操作系统内核中实现的多种锁机制,包括自旋锁、互斥锁、读写锁等,旨在揭示这些同步原语如何高效地解决资源竞争问题,保证系统的稳定性和性能。通过分析不同锁机制的工作原理及应用场景,本文为开发者提供了在高并发环境下进行有效并发控制的实用指南。 ####
|
17天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
38 6
|
18天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
36 2
|
网络协议 NoSQL Linux
阿里云 Linux 内核优化实战(sysctl.conf 和 ulimits )
一、sysctl.conf优化Linux系统内核参数的配置文件为 /etc/sysctl.conf 和 /etc/sysctl.d/ 目录。其读取顺序为: /etc/sysctl.d/ 下面的文件按照字母排序;然后读取 /etc/sysctl.conf 。
8643 1
|
19天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
49 9
|
19天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
21天前
|
机器学习/深度学习 负载均衡 算法
深入探索Linux内核调度机制的优化策略###
本文旨在为读者揭开Linux操作系统中至关重要的一环——CPU调度机制的神秘面纱。通过深入浅出地解析其工作原理,并探讨一系列创新优化策略,本文不仅增强了技术爱好者的理论知识,更为系统管理员和软件开发者提供了实用的性能调优指南,旨在促进系统的高效运行与资源利用最大化。 ###
|
21天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####