本文主要参考韦东山老师的《嵌入式Linux应用开发完全手册》,基于Linux-2.6.32.2源码。


概要:本文内容包含Linux源码树结构分析Linux Makefile分析Kconfig文件分析Linux内核配置选项分析。这些知识是为了理解内核文件的组织形式,为具体移植内核做知识准备。

1. Linux源码树结构分析

对Linux源码树下个子目录内包含的内容进行列表罗列:

目录 简介
arch 体系结构相关的代码,每一个子目录代表一种架构
block 块设备的通用函数
crypot 常用加密和散列算法、压缩和CRC校核算法
fs Linux支持的文件系统,每一个子目录代表一种文件系统
include 内核头文件:基本头文件(include/linux )、驱动或功能部件头文件(例:include/mtd )、体系相关头文件(linux/asm-arm )
driver 所有的驱动程序,每一个子目录代表一类驱动程序
init 内核的初始化程序,其中main.c中的start_kernel函数是内核引导后执行的第一个函数
ipc 进程间通信代码
kernel 内核管理的核心代码,与体系相关的代码在/arch/$(ARCH)/kernel
lib 内核用到的库函数,与处理器相关的库函数位于/arch/$(ARCH)/lib
mm 内存管理代码,与处理器体系相关的位于/arch/$(ARCH)/mm
net 与网络相关的代码,每一个子目录对应于网络的一个方面
security 安全、密钥相关的代码
sound 音频相关的驱动程序
usr 用来制作一个压缩的cpio归档文件:initrd的镜像,它可以作为内核启动后挂载的第一个文件系统
script 用于配置、编译内核的脚本文件
Document 内核文档

2.Linux Makefile分析

主要从三个方面讲解:编译哪些文件如何编译文件如何连接文件
最权威的参考资料:/Documentation/Kbuild/makefiles.txt

Linux Makefile的分类

名称 描述
顶层Makefile 总体上控制着内核的编译
arch/$(ARCH)/Makefile 决定哪些和体系相关的代码参加编译
.config 配置文件,内核配置时产生,所有的Makefile都根据这个文件编译内核(包括顶层的和各分成的Makefile)
scripts/Makefile.* Makefile公用的通用规则、脚本等
*/Makefile 负责该目录下文件的编译
  • 编译哪些文件

顶层Makefile决定哪些目录中的文件将编译进内核

    init-y      := init/drivers-y   := drivers/ sound/ firmware/net-y       := net/libs-y      := lib/core-y      := usr/...core-y      += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

顶层Makefile将13个子目录分成5个部分:init-ydrivers-ynet-ylibs-ycore-y

顶层通过下列语句包含和体系架构有关的Makefile。仔细观察可以看到/arch子目录的根目录下是没有Makefile文件的,而其它各子目录都是有Makefile。

    include $(srctree)/arch/$(SRCARCH)/Makefile...SRCARCH     := $(ARCH)

所以在编译内核之前先要确定ARCH

    ARCH        ?= $(SUBARCH)CROSS_COMPILE   ?=...SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \-e s/arm.*/arm/ -e s/sa110/arm/ \
                  -e s/s390x/s390/ -e s/parisc64/parisc/ \
                  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
                  -e s/sh[234].*/sh/ )

默认的ARCH不是我们需要的,所以要进行修改

    ARCH        ?= armCROSS_COMPILE   ?=arm-linux-

$$(srctree)/arch/$(SRCARCH)/Makefile对内核的内容进行了扩充

core-y              += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y              += $(machdirs) $(platdirs)
core-$(CONFIG_FPE_NWFPE)   += arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE)  += $(FASTFPE_OBJ)
core-$(CONFIG_VFP)     += arch/arm/vfp/
drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
libs-y              := arch/arm/lib/ $(libs-y)
...
head-y      := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

可以看到一个新元素head-y,它还有一个特殊的地方,它是直接对应着两个文件,而不是目录。之所以分成两个是为了同时支持有无MMU的CPU,它们对应着两个不同的head$(MMUEXT).o 文件,由变量MMUEXT控制,可以在配置时设定。

至此我们知道了编译时将进入哪些文件进行编译。编译时依次进入init-y、core-y、libs-y、drivers-y、net-y中列的目录调用其中的Makefile进行编译,每一个子目录都会生成build-in.o(libs-y所列的目录下有可能生成lib.a)。最后head-y列出的文件和build-in.o、lib.a一起连接成vmlinux.


在配置内核时,将会产生.config文件,Makefile将会在.config文件中添加下面两行。

CONFIG_KERNELVERSION = "2.6.32.2"
CONFIG_ARCH = "arm"

有可能是版本原因,在2.6.32.2版本中并没有上面两个语句,有下面两句。

#Linux kernel version = 2.6.32.2
CONFIG_ARM = y

观察.config文件会发现变量的值主要有两种ym,各级的Makefile将会根据这些变量的值来决定编译哪些文件,同时是编译进内核,还是作为内核模块存在。

obj-y中定义的.o文件将由当前目录下的.c、.S文件及子目录下的build-in.o文件编译连接得到的。
注意:obj-y中定义的.o文件的顺序是由意义的。
下面是一段取自子目录中的Makefile文件内容,在该目录下有ioat和ipu子目录

    obj-$(CONFIG_DMA_ENGINE) += dmaengine.oobj-$(CONFIG_NET_DMA) += iovlock.oobj-$(CONFIG_DMATEST) += dmatest.oobj-$(CONFIG_INTEL_IOATDMA) += ioat/obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.oobj-$(CONFIG_FSL_DMA) += fsldma.oobj-$(CONFIG_MV_XOR) += mv_xor.oobj-$(CONFIG_DW_DMAC) += dw_dmac.oobj-$(CONFIG_AT_HDMAC) += at_hdmac.oobj-$(CONFIG_MX3_IPU) += ipu/obj-$(CONFIG_TXX9_DMAC) += txx9dmac.oobj-$(CONFIG_SH_DMAE) += shdma.o

obj-m中定义的.o文件是由的当前目录下的.c、.S文件编译生成,它们不会与build-in.o一起编译进入内核。而是被编译成.ko文件,作为模块存在。
当.o文件由单文件编译而成时,用下面的语句:

obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

当.o文件由多文件编译而成时,用下面的语句:

obj-$(CONFIG_ISDN) +=isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_commen.o 

编写驱动程序时,也是以这种方式编写Makefile。

lib-y中定义的.o文件是由的当前目录下的.c、.S文件编译生成,他们被打包成当前目录下的lib.a文件。同时出现在lib-y和obj-y中的文件,不会被包含进lib.a文件。

obj-y和obj-m可以用来指定进入下一级目录。

  • 怎么编译这些文件

怎么编译文件就是意味着编译选项连接选项是什么。
这些选项分成3类:全局的(适用整个代码树)局部的(适用单个Makefile)个体的(适用单个文件)

全局选项是在顶层Makefile和arch/$(ARCH)/Makefile中定义的,这些选项是CFLAGS、AFLAGS、LDFLAGS、ARFLAGS,它们分别是编译C文件的选项,编译汇编文件的选项,连接文件的选项,制作库文件的选项。

局部选项在各自子目录中定义,名称为:EXTRA_CFLAGS、EXTRA_AFALGS、EXTRA_LDFALGS、EXTRA_ARFLAGS.

对单文件设定编译选项,可以用CLFAGS_$@、AFLAGS_$@,前者对C文件,后者对汇编文件。

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

_c_flags = $(CFLAGS) $(EXTRA_CFLGAS) $(CFALGS_$(baseterget.o))
  • 如何连接文件

在顶层Makefile文件中有如下语句:

init-y      := $(patsubst %/, %/built-in.o, $(init-y))
core-y      := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y   := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y       := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1     := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2     := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y      := $(libs-y1) $(libs-y2)

可以看出以后的连接是相当于着五种built-in.o文件和head-o文件的连接。

之后对这些文件再次进行合并

vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds

可以看出初始化代码由两部分组成head-y和init-y两部分组成,而且head-y是在init-y的前面。所以总的代码顺序是arch/arm/kernel/head.o(假设有MMU,没有的话是head_nommu.o)、arch/arm/kernel/init_task.o、init/build-in.o。

连接脚本是arch/$(SRCARCH)/kernel/vmlinux.lds,它由arch/$(SRCARCH)/kernel/vmlinux.lds.S生成。
具体连接细节可以查看上面的文件内容。


3.内核的Kconfig分析

内核配置工具读取各个Kconfig文件,生成配置界面共开放人员配置内核,最后生成配置文件.config。

关于Kconfig的最权威资料在/Documentations/Kbuild/kconfig-language.txt

Kconfig语法分析:

  • Kconfig的基本要素:config
    config经常被其它条目包含,用来生成菜单和多项选择。
config JFFS2_FS_WBUF_VERIFYbool "Verify JFFS2 write-buffer reads"depends on JFFS2_FS_WRITEBUFFERdefault nhelpThis causes JFFS2 to read back every page written through thewrite-buffer, and check for errors.

上述代码是config的常用方式:

config JFFS2_FS_WBUF_VERIFY

在配置界面中配置了该选项后,会在.config中出现 CONFIG_JFFS2_FS_WBUF_VERIFY = y或者m.

bool "Verify JFFS2 write-buffer reads"

在配置界面中将会显示Verify JFFS2 write-buffer reads选项,bool是变量的类型,一共有5种变量类型:booltristatestringhexint,bool变量有两种取值y,m;tristate变量有三种取值y,m,n;string可以取字符串;hex取十六进制数;int取十进制数。

depends on JFFS2_FS_WRITEBUFFER

代表只有在JFFS2_FS_WRITEBUFFER被配置时,才会进行该选项的配置。

default n

代表默认的情况下是选择n

select FS_POSIX_ACL

代表在该选项被选种时,会将FS_POSIX_ACL也选种。

    helpThis causes JFFS2 to read back every page written through thewrite-buffer, and check for errors.

当在配置时按H时会显示该信息。


  • menu条目
    配置界面的主界面是由根目录下Makefile中ARCH配置决定的,当选择arm时,/arch/arm中的Kconfig文件将会用来生成主目录。
    下面的内容摘自/arch/arm/Kconfig
mainmenu "Linux Kernel Configuration"

设定主目录的名称

menu "System Type"

将会创建System Type子目录

  • choice条目
    choice将多个类似的配置选项组合在一起,供用户多选和单选
choiceprompt "Memory split"default VMSPLIT_3GhelpSelect the desired split between kernel and user memory.If you are not absolutely sure what you are doing, leave thisoption alone!config VMSPLIT_3Gbool "3G/1G user/kernel split"config VMSPLIT_2Gbool "2G/2G user/kernel split"config VMSPLIT_1Gbool "1G/3G user/kernel split"
endchoice
    prompt "Memory split"

上述代码给出提示信息,选中之后就可以进行选择配置
choice条目中定义的变量类型只能是bool和tristate,当配置的代码编译入内核时为bool,只能有一个条目选择为y;当编译成模块时为tristate或bool,为bool时,也只能是一个为y,当为tristate时,可以有多个m。

  • comment条目
    comment条目用于提供帮助信息,出现在配置界面的第一行。
comment "At least one emulation must be selected"
  • source条目
    用于包含其他Kconfig文件
source "drivers/cpuidle/Kconfig"
  • 菜单形式的配置界面的操作方法
    配置界面中[*]、< M >、[ ]分别表示相应的文件被编译进内核、编译成模块、没有被编译。
Load an Alertnate Configuration File
Save an Alertnate Configuration File

当执行第一条语句时,将.config外的config文件加载,当执行第二条时,表示存储成处.config 外的config文件。


4.Linux内核配置选项

与移植密切相关的内容是System TypeDevice Driver

内核配置主界面内容

配置界面主菜单 描述
code maturity level options 代码成熟度选项,包含一些正在开发的或者不成熟的代码和驱动程序,一般不用设置
General setup 常规设置,比如增加附加的内核版本号、支持内存页交换功能、System V进程间通信等。除非很熟悉其中的内容,否则一般使用默认配置
Loadable module support 可加载模块支持:一般都会打开可加载模块支持(Enable loadable module support )、允许卸载已经加载的模块(Module unloading)、让内核通过运行modprobe来自动加载模块(Automatic kernel module loading)
block layer 块设备层:用于设置块设备的一些总体参数,比如是否支持大于2TB的块设备、是否支持大于2TB的文件、设置IO调度器,使用默认值即可
System Type 系统类型:选择CPU的架构、开发板类型等于开发板相关的配置选项
Bus support PCMCIA 、CardBus总线的支持,对于ARM开发板不需要设置
Kernel Feature 用于设置内核的一些参数,比如是否支持内核抢占,是否支持动态修改系统时钟
Boot option 启动参数:比如设置默认的命令行参数等,一般不用理会
Floating point emulation 浮点运算仿真功能:因为Linux内核不支持硬件浮点运算,所以要选择一个浮点仿真器,一般选择”NWFPE math emulaiton”
Userspace binary formats 可执行文件格式:一般都选择ELF、a.out格式
Power management options 电源管理选项
Networking 网络协议选项:一般都选择”Networking support“以支持网络功能。通常可以在选择”Networking support“后,使用默认配置
Device Driver 设备驱动程序:几乎包含了Linux的所有的驱动程序
File systems 文件系统:选择支持的文件系统
Profiling support 对系统的或顶进行分析,仅供内核开发者使用
Kernel hacking 调试内核时的各种选项:Linux设备驱动程序中有详细描述
security options 安全选项:一般使用默认选项
Cryptographic options 加密选项
Library routines 库子程序:比如CRC32检验函数、zlib压缩函数等。不包含在内核源码中的第三方内核模块可能需要这些库,可以全不全,内核中若有其他部分依赖它,会自动选项

在配置内核的时候按照顺序进行,因为前面的配置会影响后面的。

Linux内核源码组织结构相关推荐

  1. 腾讯首发Linux内核源码《嵌入式开发进阶笔记》差距差的不止一点点哦

    一,前言 Linux内核是一个操作系统(OS)内核,本质上定义为类Unix.它用于不同的操作系统,主要是以不同的Linux发行版的形式.Linux内核是第一个真正完整且突出的免费和开源软件示例.Lin ...

  2. 【Linux内核源码剖析】内核源码的组织结构

    文章目录 前言 一.Linux内核的特征 二.为什么要学Linux内核 三.Linux操作系统结构 1. Linux内核在系统中的位置 2. Linux内核子系统之间的关系 3. 系统主要的数据结构 ...

  3. Linux内核源码分析《进程管理》

    Linux内核源码分析<进程管理> 前言 1. Linux 内核源码分析架构 2. 进程原理分析 2.1 进程基础知识 2.2 Linux进程四要素 2.3 进程描述符 task_stru ...

  4. Linux内核源码中使用宏定义的若干技巧

    在C中,宏定义的概念虽然简单,但是真要用好却并不那么容易,下面从Linux源码中抽取一些宏定义的使用方法,希望能从中得到点启发: 1. 类型检查 比如module_init的宏定义: 点击(此处)折叠 ...

  5. 深入分析Linux内核源码oss.org.cn/kernel-book/

    本html页面地址:http://oss.org.cn/kernel-book/ 深入分析Linux内核源码 前言         第一章 走进linux 1.1 GNU与Linux的成长 1.2 L ...

  6. Redhat7.2上编译Linux内核源码

    下载linux源码包:https://git.kernel.org/pub/scm/virt/kvm/kvm.git/snapshot/kvm-4.17-1.tar.gz (这是包含kvm开发版本的l ...

  7. 【Linux 内核】Linux 内核源码目录说明 ① ( arch 目录 | block 目录 | certs 目录 | crypto 目录 | Documentation 目录 )

    文章目录 一.arch 目录 二.block 目录 三.certs 目录 四.crypto 目录 五.Documentation 目录 在上一篇博客 [Linux 内核]Linux 内核源码结构 ( ...

  8. 【Linux 内核】编译 Linux 内核 ② ( 解压内核源码 | 查询当前 Linux 内核版本号 | 进入并查看 linux 内核源码目录 )

    文章目录 一.解压内核源码 二.查询当前 Linux 内核版本号 三.进入并查看 linux 内核源码目录 一.解压内核源码 将 下载的 Linux 内核源码 linux-5.6.14.tar.gz ...

  9. 【Linux 内核】编译 Linux 内核 ① ( 下载指定版本的 Linux 内核源码 | Linux 内核版本号含义 | 主版本号 | 次版本号 | 小版本号 | 稳定版本 )

    文章目录 一.下载 Linux 内核 1.下载最新版本 Linux 内核 2.下载指定版本 Linux 内核 二.Linux 内核版本号含义 一.下载 Linux 内核 1.下载最新版本 Linux ...

最新文章

  1. Centos 6.4 python 2.6 升级到 2.7一起的MySQLdb不能使用的解决问题
  2. ArcGis 10+Oracle发布WFS-T服务,无法更新Feature的解决方法
  3. springboot读取自定义properties文件
  4. 详解Java反射机制
  5. 计算机技术博客博客知乎,我的技术博客的选择:CSDN、博客园、简书、知乎专栏仍是Github Page?...
  6. ElasticSearch 未授权访问记录(端口:9200)
  7. 【转】win7 虚拟机virtualbox中ubuntu12.04安装samba实现文件共享
  8. react-native无法在react-native-gesture-handler中解析符号android.support.v4.util.Pools解决方案...
  9. 树莓派boot分区cmdline.txt
  10. java 访问器_网络之美 JavaScript中Get和Set访问器的实现代码
  11. R语言入门1:安装R和RStudio
  12. Socket服务端与网络调试助手模拟串口服务器通讯示例
  13. 韩顺平java基础学习笔记
  14. 【面试分享】奇安信渗透测试工程师,通过!
  15. 泊松分布(一种离散分布)
  16. python股票编程_Python爬虫回测股票的实例讲解
  17. WINRAR密码去除/破解工具
  18. 网络0323和网络2303分类过程的比较
  19. 【开发日常】【Makefile】编译时如何将警告(warning)视为错误(error)?
  20. 正宇丨青松气质 红梅品格

热门文章

  1. SQL数据分析淘宝用户分析实操
  2. 为什么建议大家使用 Linux 开发?有那么爽吗?
  3. 如何写好一个产品需求文档PRD
  4. ORA-01422: exact fetch returns more than requested number of rows
  5. 华人学者再创佳绩!包揽CoRL2021最佳论文奖项
  6. python做erp系统教程_“python2.7教程廖雪峰“刚开始学openERP Python,如何快速入门?...
  7. 全球人口密度大致分布
  8. 通俗易懂说网络之基础名词及简要介绍(1)
  9. 我的世界mod整合包java_[1.7.10]我的世界多MOD整合包-糖果领域
  10. element框架el-aside高度100%显示