linux 内核 企鹅,Linux 内核 Makefile 体系简单分析

众所周知,Linux内核是使用make命令来配置并编译的,那必然少不了Makefile。在内核目录树中我们可以看到内核编译系统的顶层Makefile文件。但是如此复杂、庞大的内核源码绝不可能使用一个或几个Makefile文件来完成配置编译,而是需要一套同样复杂、庞大,且为Linux内核定制的Makefile系统。她可以说是内核的一个子系统,是内核中比较特殊的一部分,几乎都是应用层的程序和脚本,但又和生成的内核二进制文件息息相关。编译不仅涉及本地编译,还涉及各个平台之间的交叉编译以及二进制文件格式处理等等。她是对Makefile在功能上的扩充,使其在配置编译Linux内核的时候更加灵活、高效和简洁。

尽管她是一个复杂的系统,但对绝大部分内核开发者来说只需要知道如何使用,而无需了解其中的细节。她对绝大部分内核开发者基本上是透明的,隐藏了大部分实现细节,有效地降低了开发者的负担,能使其能专注于内核开发,而不至于花费时间和精力在编译过程上。

以下我们就来简要的了解一下内核Makefile体系。

一、内核Makefile体系概述

其实内核Makefile体系的包含了Kconfig和Kbuild两个系统。她曾经的维护人是Sam

Ravnborg <>,现在的暂时没有查到。参考资料:

Kconfig对应的是内核配置阶段,如你使用命令:make menuconfig,就是在使用Kconfig系统。Kconfig由以下三部分组成:

scripts/kconfig/*

Kconfig文件解析程序

kconfig

各个内核源代码目录中的kconfig文件

arch/$(ARCH)/configs/*_defconfig

各个平台的缺省配置文件

当Kconfig系统生成.config后,Kbuild会依据.config编译指定的目标。后面我会简单地对make %config的流程进行情景分析,这里不必赘述。

Kbuild是内核Makefile体系重点,对应内核编译阶段,由5个部分组成:

顶层Makefile

根据不同的平台,对各类target分类并调用相应的规则Makefile生成目标

.config

内核配置文件

arch/$(ARCH)/Makefile

具体平台相关的Makefile

scripts/Makefile.*

通用规则文件,面向所有的Kbuild Makefiles,所起的作用可以从后缀名中得知。

各子目录下的Makefile文件

由其上层目录的Makefile调用,执行其上层传递下来的命令

而其中scripts目录下的编译规则文件和其目录下的C程序在整个编译过程起着重要的作用。列举如下:

文件名

作用

Kbuild.include

共用的定义文件,被许多独立的Makefile.*规则文件和顶层Makefile包含

Makefile.build

提供编译built-in.o, lib.a等的规则

Makefile.lib

负责归类分析obj-y、obj-m和其中的目录subdir-ym所使用的规则

Makefile.host

本机编译工具(hostprog-y)的编译规则

Makefile.clean

内核源码目录清理规则

Makefile.headerinst

内核头文件安装时使用的规则

Makefile.modinst

内核模块安装规则

Makefile.modpost

模块编译的第二阶段,由.o和.mod生成.ko时使用的规则

顶层Makefile主要是负责完成vmlinux(内核文件)与*.ko(内核模块文件)的编译。顶层Makefile读取.config文件,并根据.config文件确定访问哪些子目录,并通过递归向下访问子目录的形式完成。顶层Makefile同时根据.config文件原封不动的包含一个具体架构的Makefile,其名字类似于arch/$(ARCH)/Makefile。该架构Makefile向顶层Makefile提供其架构的特别信息。

每一个子目录都有一个Makefile文件,用来执行从其上层目录传递下来的命令。子目录的Makefile也从.config文件中提取信息,生成内核编译所需的文件列表。

二、内核Makefile导读与情景分析

1、概述

上面简要介绍了内核Makefile的总体结构,但当我们打开顶层Makefile文件时还是因为她的复杂而觉得无从下手。但是内核Makefile就是Makefile,和最简单的Makefile遵循着同样的规则。所以只要我们静下心来分析,还是可以理解的。当然,在阅读内核的Makefile前,你必须对Makefile和shell脚本有一定的基础。

推荐参考资料:

《》 翻译整理:徐海兵  PDF文档

《》翻译:杨春敏 黄毅

根据Makefile的执行规则,在分析Makefile时,首先必须确定一个目标,然后才能确定所有的依赖关系,最后根据更新情况决定是否执行相应的命令。所以要看懂内核Makefile的大致框架,我们首先要了解她里面所定义的目标。而内核Makefile所定义的目标基本上可以通过make help打印出来(因为help本身就是顶层Makefile的一个目标,里面是打印帮助信息的“echo”命令)。

这些目标可以分为以下几个大类:

目标

常用目标举例

作用

配置

%config

config

启动Kconfig,以不同界面来配置内核。

menuconfig

xconfig

编译

all

编译vmlinux内核映像和内核模块

vmlinux

编译vmlinux内核映像

modules

编译内核模块

安装

headers_install

安装内核头文件/模块

modules_install

源码浏览

tags

生成代码浏览工具所需要的文件

TAGS

cscope

静态分析

checkstack

检查并分析内核代码

namespacecheck

headers_check

内核打包

%pkg

以不同的安装格式编译内核

文档转换

%doc

把kernel文档转成不同格式

构架相关

(以arm为例)

zImage

生成压缩的内核映像

uImage

生成压缩的u-boot可引导的内核映像

install

安装内核映像

其中的构架相关目标在顶层Makefile上并未出现,而是被包含在平台相关的Makefile(arch/$(ARCH)/Makefile)中。

2、情景分析

以下我们就来分析一个简单的目标(menuconfig),作为情景分析范例来演示一下内核Makefile的分析方法。

首先当我们在内核源码的根目录下执行make menuconfig命令时,根据规则,make程序读取顶层Makefile文件及其包含的Makefile文件,内建所有的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表。make程序最终会调用规则:

config %config: scripts_basic

outputmakefile FORCE

$(Q)mkdir

-p include/linux include/config

$(Q)$(MAKE)

$(build)=scripts/kconfig $@

调用的原因是我们指定的目标“menuconfig”匹配了“%config”。

她的依赖目标是scripts_basic和outputmakefile,以及FORCE。也就是说在完成了这3个依赖目标后,下面的两个命令才会执行以完成我们指定的目标“menuconfig”。

所以我们来看看这三个依赖目标实现的简要过程:

(1)scripts_basic

make程序会调用规则:

scripts_basic:

$(Q)$(MAKE)

$(build)=scripts/basic

他没有依赖目标,所以直接执行了以下的指令,只要将指令展开,我们就知道make做了什么操作。其中比较不好展开的是$(build),她的定义在scripts/Kbuild.include中:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build

obj

所以展开后是:

make -f scripts/Makefile.build obj= scripts/basic

也就是make解析执行scripts/Makefile.build文件,且参数obj=

scripts/basic。而在解析执行scripts/Makefile.build文件的时候,scripts/Makefile.build又会通过解析传入参数来包含对应文件夹下的Makefile文件(scripts/basic/Makefile),从中获得需要编译的目标。

在确定这个目标以后,通过目标的类别来继续包含一些scripts/Makefile.*文件。例如scripts/basic/Makefile中内容如下:

hostprogs-y := fixdep docproc hash

always      := $(hostprogs-y)

# fixdep is needed to

compile other host programs

$(addprefix

$(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep

所以scripts/Makefile.build会包含scripts/Makefile.host。相应的语句如下:

# Do not include host

rules unless needed

ifneq

($(hostprogs-y)$(hostprogs-m),)

include

scripts/Makefile.host

endif

此外scripts/Makefile.build会包含include

scripts/Makefile.lib等必须的规则定义文件,在这些文件的共同作用下完成对scripts/basic/Makefile中指定的程序编译。

由于Makefile.build的解析执行牵涉了多个Makefile.*文件,过程较为复杂,碍于篇幅无法一条一条指令的分析,兴趣的读者可以自行分析。

推荐两篇经典的分析文档:

《Kbuild系统原理分析》  作者未知,网上有PDF文档

(2)outputmakefile

make程序会调用规则:

PHONY += outputmakefile

# outputmakefile

generates a Makefile in the output directory, if using a

# separate output

directory. This allows convenient use of make in the

# output directory.

outputmakefile:

ifneq ($(KBUILD_SRC),)

$(Q)ln -fsn $(srctree) source

$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \

$(srctree) $(objtree)

$(VERSION) $(PATCHLEVEL)

endif

从这里我们可以看出:outputmakefile是当KBUILD_SRC不为空(指定O=dir,编译输出目录和源代码目录分开)时,在输出目录建立Makefile时才执行命令的,

如果我们在源码根目录下执行make menuconfig命令时,这个目标是空的,什么都不做。

如果我们指定了O=dir时,就会执行源码目录下的scripts/mkmakefile,用于在指定的目录下产生一个Makefile,并可以在指定的目录下开始编译。

(3)FORCE

这是一个在内核Makefile中随处可见的伪目标,她的定义在顶层Makefile的最后:

PHONY

+= FORCE

FORCE:

是个完全的空目标,但是为什么要定义一个这样的空目标,并让许多目标将其作为依赖目标呢?原因如下:

正因为FORCE是一个没有命令或者依赖目标,不可能生成相应文件的伪目标。当make执行此规则时,总会认为FORCE不存在,必须完成这个目标,所以她是一个强制目标。也就是说:规则一旦被执行,make就认为它的目标已经被执行并更新过了。当她作为一个规则的依赖时,由于依赖总被认为被更新过的,因此作为依赖所在的规则中定义的命令总会被执行。所以可以这么说:只要执行依赖包含FORCE的目标,其目标下的命令必被执行。

在make完成了以上3个目标之后,就开始执行下面的命令的,首先是

$(Q)mkdir -p

include/linux include/config

这个很好理解,就是建立两个必须的文件夹。然后

$(Q)$(MAKE)

$(build)=scripts/kconfig $@

这和我们上面分析的$(Q)$(MAKE)

$(build)=结构相同,将其展开得到:

make -f

scripts/Makefile.build obj=scripts/kconfigmenuconfig

所以这个指令的效果是使make解析执行scripts/Makefile.build文件,且参数obj=scripts/kconfig

menuconfig。这样Makefile.build会包含对应文件夹下的Makefile文件(scripts/kconfig /Makefile),并完成scripts/kconfig /Makefile下的目标:

menuconfig:

$(obj)/mconf

$< $(Kconfig)

这个目标的依赖条件是$(obj)/mconf,通过分析可知她其实是对应以下规则:

mconf-objs  := mconf.o zconf.tab.o $(lxdialog)

……

ifeq

($(MAKECMDGOALS),menuconfig)

hostprogs-y += mconf

endif

也就是编译生成本机使用的mconf程序。完成依赖目标后,通过scripts/kconfig/Makefile中对Kconfig的定义可知,最后执行:

mconfarch/$(SRCARCH)/Kconfig

而对于conf和xconf等都有类似的过程,所以总结起来:当make %config时,内核根目录的顶层Makefile会临时编译出scripts/kconfig中的工具程序conf/mconf/qconf等负责对arch/$(SRCARCH)/Kconfig文件进行解析。这个Kconfig又通过source标记调用各个目录下的Kconfig文件构建出一个Kconfig树,使得工具程序构建出整个内核的配置界面。在配置结束后,工具程序就会生成我们常见的.config文件。

三、在内核中添加自己的模块

虽然内核Makefile体系很是复杂,但是这种复杂带来的确是开发时的便利。其实内核Makefile体系之所以复杂,其中的一个原因就是为了方便扩展。对于一个开发者来要在内核中添加自己的一个驱动代码是非常简单的事情。

一般来说,对于一个新驱动代码的添加,驱动工程师只需要在内核源码的drivers目录的相应子目录下添加新设备驱动源码,并增加或修改该目录下的Kconfig和Makefile文件即可。

比如你已经写好了一个针对TI 的AM33XX芯片的LED的驱动程序,名为am33xx_led.c。

(1)将驱动源码am33xx_led.c等文件复制到linux-X.Y.Z/drivers/char目录。

(2)在该目录下的Kconfig文件中添加LED驱动的配置选项:

config AM33XX_LED

bool "Support for am33xx led

drivers"

depends on  SOC_OMAPAM33XX

default n

---help---

Say Y here if you want to support for AM33XX LED drivers.

(3)在该目录下的Makefile文件中添加对LED驱动的编译:

obj-$(CONFIG_AM33XX_LED)   +=  am33xx_led.o

这样你就可以在make menuconfig的时候看到这个配置选项,并进行配置了。

当然,上面的例子只是一个意思,对于Kconfig文件和Makefile的详细语法,请参考内核文档:Documentation/kbuild/makefile.txt

四 、在内核Makefile上对读者的建议

这个复杂的Makefile体系体现了很多优秀程序共有的设计思想,对于我们今后的程序设计上有很多值得借鉴的地方。比如:模块化设计、简化编程接口,使得自行添加模块更加的简洁。阅读分析这样复杂的Makefile对于学习和编写Makefile和shell脚本有很好的参考价值。如果你正在学习Makefile的编写和阅读,那你可以耐心的分析一下内核的Makefile体系,只要你认真分析了一两个目标的实现,你会发现当你在阅读一些小软件的Makefile时已经是轻车熟路了。特别是现在很多芯片的开发包都是以SDK包的形式发布的,而这些软件包都是通过Makefile体系来实现自动编译和配置的,所以熟悉Makefile是每个Linux开发者都需要做到的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432352.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UWidgetBlueprintLibrary

使用UWidgetBlueprintLibrary类的绘制函数&#xff0c;可以在UMG里绘制二维图像。 做成静态函数后就可以在蓝图中直接调用 void AAGraphics::DrawLine(FPaintContext Context, FVector2D PositionA, FVector2D PositionB, FLinearColor Tint, bool bAntiAlias) {UWidgetBluepri…

UDP模式与TCP模式的区别

1、TCP有连接状态&#xff0c;而UDP没有。 2、TCP应用层使用无需考虑包的大小及发送情况&#xff0c;而UDP需要。 3、TCP中IP包大小的决定者是Socket&#xff0c;而UDP为应用层。转载于:https://www.cnblogs.com/xinxinboke/p/3944619.html

UProceduralMeshComponent

使用UProceduralMeshComponent可以自由的用三角星来绘制三维图形 1. 在actor的构造函数中添加UProceduralMeshComponent组件 mesh CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("mesh")); Material LoadObject<UMaterial>(NULL, TEXT("…

[LeetCode] Search for a Range [34]

题目 Given a sorted array of integers, find the starting and ending position of a given target value. Your algorithms runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]. For example, Given [5, 7, 7…

linux ssh注册码,linux ssh -l 命令运用

ssh是远程登录命令&#xff0c;-l选项是最常用的选项&#xff0c;下面是我的一些总结远程登录&#xff1a;ssh -l userName ip# 远程登录到 10.175.23.9ssh -l root2 10.175.23.9执行远程命令(不登录)&#xff1a;ssh -l userName ip command# 不远程登录到 10.175.23.9…

【OpenGL】向Shader中传递数据

传递顶点属性信息之前讲过&#xff0c;vertex shader会被每个顶点调用&#xff0c;通常一个顶点会包含很多信息&#xff0c;例如顶点坐标、顶点法向量、纹理坐标等等&#xff0c;我们称这些信息为顶点的属性。在之前的OpenGL版本里&#xff0c;每个属性都对应了一个特定的通道&…

常用正则表达式大全

匹配中文字符的正则表达式&#xff1a; [u4e00-u9fa5]   评注&#xff1a;匹配中文还真是个头疼的事&#xff0c;有了这个表达式就好办了   匹配双字节字符(包括汉字在内)&#xff1a;[^x00-xff]   评注&#xff1a;可以用来计算字符串的长度&#xff08;一个双字节字符…

linux+qt+定时精度,Qt QTimer测试定时精度

调试信息输出窗口可以查看超时误差dialog.h文件#ifndef DIALOG_H#define DIALOG_H#include #include namespace Ui {class Dialog;}class QTimer;class Dialog : public QDialog{Q_OBJECTpublic:explicit Dialog(QWidget *parent 0);~Dialog();public slots:void slotOneSec()…

初始JavaScript详解【精选】

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍初始JavaScript以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题可以在评论区留言 目录 ⭐…

最简单的基于FFmpeg的AVDevice例子(屏幕录制)

最简单的基于FFmpeg的AVDevice例子文章列表&#xff1a;最简单的基于FFmpeg的AVDevice例子&#xff08;读取摄像头&#xff09;最简单的基于FFmpeg的AVDevice例子&#xff08;屏幕录制&#xff09;&#xfeff;&#xfeff;FFmpeg中有一个和多媒体设备交互的类库&#xff1a;Li…

linux 挂载32T文件系统,linux Centos下磁盘分区及文件系统创建与挂载(示例代码)

linux Centos下磁盘分区及文件系统创建与挂载MBR(Master Boot Record)是传统的分区机制&#xff0c;应用于绝大多数使用BIOS的PC设备。1.MBR支持32bit和64bit系统2.MBR支持分区数量有限3.MBR只支持不超过2T的硬盘&#xff0c;超过2T的硬盘只能使用2T空间(使用其他方法)1.主分区…

C语言的数组初始化

http://blog.csdn.net/sibylle/article/details/2026915 一直以为 int a[256]{0};是把a的所有元素初始化为0&#xff0c;int a[256]{1};是把a所有的元素初始化为1.调试的时查看内存发现不是那么一回事.5.2.1 数组初始化 数组可以用一个列值来初始化&#xff0c;例如 …

【OpenGL】GLSL中的函数和子程序(subroutines)

这篇文章里讲一下在GLSL如何使用函数和子程序&#xff08;subroutines&#xff09;。在GLSL中使用函数 GLSL支持函数&#xff0c;它们的语法结构和C很相似。但是调用约定会有所不同。下面&#xff0c;我们以一个普通的ADS&#xff08;ambient&#xff0c;diffuse&#xff0c;sp…

linux7 共享盘创建,使用CentOS7建立samba文件共享服务器

1、安装samba检查samba是否已经安装[rootbak1 ~]# rpm -qa | grep samba没有任何回显则表示未安装软件包 sambayum安装samba[rootbak1 ~]# yum -y install samba samba-client samba-common再次检查安装结果[rootbak1 var]# rpm -qa | grep sambasamba-common-libs-4.6.2-12.el…

批量造数据的一种方法

实现目的&#xff1a;一张表Table中有A、B、C三个字段&#xff0c;并有很多值&#xff0c;开发测试需要&#xff0c;可以执行这样的语句批量复制插入数据&#xff1a; insert into Table(A,B,C)select A,B,C from Table 转载于:https://www.cnblogs.com/tianqy/p/3965292.html

【Modern OpenGL】第一个三角形

>说明&#xff1a;跟着learnopengl的内容学习&#xff0c;不是纯翻译&#xff0c;只是自己整理记录。>强烈推荐原文&#xff0c;无论是内容还是排版。 [原文链接](http://learnopengl.com/#!Getting-started/OpenGL)本文地址&#xff1a;http://blog.csdn.net/aganlengzi…

13c语言中的文件是一种流式文件,读写时均以字符为单位.,C语言判断题部分.doc...

C语言判断题部分?第一章C语言中&#xff0c;字符数据用格式说明"%c"来输出&#xff0c;字符串数据用格式说明"%s"来输出。TC语言中&#xff0c;关键字及编译预处理命令用小写字母书写。T二元运算符均可以和赋值运算符构成复合的赋值运算符。TC语言中&…

UI Automation 简介

转载&#xff0c;源地址&#xff1a; http://blog.csdn.net/ffeiffei/article/details/6637418 MS UI Automation&#xff08;Microsoft User Interface Automation&#xff1a;UIA&#xff09;是随.net framework3.0一起发布的&#xff0c;虽然在如今这个几乎每天都有各种新名…

【OpenGL4.0】GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形

以前都是用Cg的&#xff0c;现在改用GLSL&#xff0c;又要重新学&#xff0c;不过两种语言很多都是相通的。下面的例子是实现绘制一个三角形的简单程序。采用了VBO&#xff08;veretx buffer object&#xff09;、VAO&#xff08;vertex array object&#xff09;等OpenGL的一些…

catia linux下载64位,CATIA V5 CATSysDemon.exe缓冲区溢出漏洞

发布日期&#xff1a;2014-02-24更新日期&#xff1a;2014-02-25受影响系统&#xff1a;3ds catia-v5描述&#xff1a;--------------------------------------------------------------------------------CATIA是数字产品定义及生命周期管理使用的CAD、CAE、CAM应用集成软件包…