内核源码中单个.o文件的编译过程(六)
通过对过渡篇的学习,相信你已经具有了相当的知识储备,接下来就来继续学习单个.o文件的编译过程
以/drivers/char/mem.c的编译为例
make /drivers/char/mem.o
一. 找到目标及其构建规则*
在顶层目录的Makefile中:
%.o: %.c prepare scripts FORCE
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
这格式其实和我们平时自己写的规则是差不多的。.o的文件依赖于同名的.c文件,然后执行了一个命令来生成。不过呢,确实还多了些东西,包括一些特殊的依赖条件。
为了更直接的看懂这条规则,在这段规则前面添加一个“#”号
#$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
运行一次make /drivers/char/mem.o 命令
从打印的命令来看,生成drivers/char/mem.o这个目标文件是重新又调用了一次make,而这次使用的是script/Makefile.build这个规则文件,传入的参数是drivers/char,目标还是drivers/char/mem.o。
三. 解析这条构建规则
1. build变量
# scripts/Kbuild.include
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
# 其中$(KBUILD_SRC)常规情况下为空,所以变量的定义可简化为
build := -f scripts/Makefile.build obj
2. build-dir 和 target-dir变量
#Single targets
#---------------------------------------------------------------------------
#Single targets are compatible with:
#- build with mixed source and output
#- build with separate output dir 'make O=...'
#- external modules
#
#target-dir => where to store outputfile
#build-dir => directory in kernel source tree to use
ifeq ($(KBUILD_EXTMOD),)
build-dir = $(patsubst %/,%,$(dir $@))
target-dir = $(dir $@)
else
zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@)))
build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash))
target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@))
endif
通过注释可以了解到,这两个变量一个是做编译的路径,一个是目标存放的路径。
定义本身分为两种情况
- 当KBUILD_EXTMOD为空,表示不是编译内核模块
- 当KBUILD_EXTMOD不为空,表示是在编译内核模块
我们这里并没有编译内核模块,所以KBUILD_EXTMOD为空,采用的是第一种情况的定义。从显示出的命令来看build-dir和target-dir分别定义为drivers/char 和drivers/char/。只是相差了最后的一个/。
3. 规则展开的最终形式
@make -f scripts/Makefile.build obj=drivers/char drivers/char/mem.o
四. scripts/Makefile.build是如何编译出drivers/char/mem.o的?
1. 找到目标drivers/char/mem.o的生成规则
注意不是使用顶层Makefile生成.o的规则,因为已经通过-f 指定了scripts/Makefile.build这个文件。在scripts/Makefile.build文件中找到*.o目标
# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
依赖条件暂不关注,看多了会头痛,哈哈。这个规则中的两条命令:
第一条是做代码检查的,暂不关注。
第二条看着名字就有点像,cc_o_c,把c代码通过cc制作成.o文件。这个正是我们要关注的,就分析它了。
2. if_changed_rule函数
上一节,我们把关注的焦点定在了这条语句。
$(call if_changed_rule,cc_o_c)
其中if_changed_rule函数定义在Kbuild.include中,因为在顶层Makefile中包含了Kbuild.include这个文件
include $(srctree)/scripts/Kbuild.include
if_changed_rule函数的定义如下:
# Usage: $(call if_changed_rule,foo)
# Will check if $(cmd_foo) or any of the prerequisites changed,
# and if so will execute $(rule_foo).
if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ), \
@set -e; \
$(rule_$(1)))
根据本节前面的基础知识储备小节,你就会发现,这其实就是一个if语句。if_changed 函数在当发现规则的依赖有更新,或者是对应目标的命令行参数发生改变时($ (strip $ (any-prereq) $ (arg-check)) 语句结果不为空),执行后面的语句。set -e 表示如果命令执行有错那么命令停止执行并退出(指的是rule_ $ (1)这个命令)。
我们把注意力仍然集中在rule_$(1)。根据前面的基础知识储备篇,可以知道if_changed_rule函数对应的传入参数是cc_o_c。在这里展开后就是 rule_cc_o_c。
这里有点像回调函数的感觉,if_changed_rule负责判断有没有前置条件是新的,是否需要重新生成目标。如果需要,if_changed_rule就会调用所要求的函数再去生成目标。
3. rule_cc_o_c
经过对if_changed_rule函数的分析,我们的目标又变成了rule_cc_o_c。现在的任务就是找到它。
rule_cc_o_c定义在scripts/Makefile.build中:
define rule_cc_o_c
$(call echo-cmd,checksrc) $(cmd_checksrc) \
$(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \
$(cmd_modversions) \
$(call echo-cmd,record_mcount) \
$(cmd_record_mcount) \
scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \
$(dot-target).tmp; \
rm -f $(depfile); \
mv -f $(dot-target).tmp $(dot-target).cmd
endef
又是一长串…。不过相信你经过前面的基础知识的学习,能够轻松应对它。经过对无用命令的剥离,剩下一个与我们相关的命令行:
$(call echo-cmd,cc_o_c) $(cmd_cc_o_c);
把$(call echo-cmd,cc_o_c) $(cmd_cc_o_c)展开得到:
# $(call echo-cmd,cc_o_c)展开:
echo 'CC $@' ; CC $@
执行make /drivers/char/mem.o命令时打印如下:
总结
linux内核中编译单个.o的执行顺序:
-----------------------------------------------------------------------------------
# Makefile(顶层)
%.o: %.c
make -f scripts/Makefile.build obj=drivers/char drivers/char/mem.o
-----------------------------------------------------------------------------------
# scripts/Makefile.build
$(obj)/%.o: $(src)/%.c
$(call if_changed_rule,cc_o_c)
-----------------------------------------------------------------------------------
#scripts/Makefile.build 打印这条命令,并且执行命令
rule_cc_o_c
$(call echo-cmd,cc_o_c) $(cmd_cc_o_c)
-----------------------------------------------------------------------------------
#scripts/Makefile.build 执行命令
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
内核源码中单个.o文件的编译过程(六)的更多相关文章
- Android系统篇之—-编写简单的驱动程序并且将其编译到内核源码中【转】
本文转载自:大神 通过之前的一篇文章,我们了解了 Android中的Binder机制和远程服务调用 在这篇文章中主要介绍了Android中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一 ...
- Caffe源码中caffe.proto文件分析
Caffe源码(caffe version:09868ac , date: 2015.08.15)中有一些重要文件,这里介绍下caffe.proto文件. 在src/caffe/proto目录下有一个 ...
- linux内核源码中两个重要的宏
转载:http://www.cnblogs.com/skywang12345/p/3562146.html 倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 containe ...
- Linux 内核源码中likely()和unlikely()
ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首先明确: if (likely(value))等价于if (value)if (likely( ...
- Linux内核源码中的likely和unlikely释疑【转】
本文转载自:https://my.oschina.net/armsky/blog/15320 ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首 ...
- Linux 内核源码中likely()和unlikely()【转】
本文转载自:http://blog.csdn.net/tigerjibo/article/details/8279183 ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们 ...
- live555源码研究(十)------在编译过程中遇到的问题及解决方法
一.编译testOnDemandRTSPServer.cpp. 在testProgs项目中,加入testOnDemandRTSPServer.cpp进行编译,编译类型是编译成exe文件,在编译过程中会 ...
- linux 内核源代码情景分析——linux 内核源码中的汇编语言代码
1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...
- 如何单独编译Linux内核源码中的驱动为可加载模块?
答: 分为两步: 1. 配置某个驱动为模块(如: CONFIG_RTC_XXX=m) 2. 指定路径并编译, 如编译drivers/rtc中的驱动 make SUBDIRS=drivers/rtc m ...
- linux内核源码中常见宏定义
http://blog.csdn.net/yangdelong/article/details/5508057
随机推荐
- Visual Studio Code 常见的配置、常用好用插件以及【vsCode 开发相应项目推荐安装的插件】
一.VsCode 常见的配置 1.取消更新 把插件的更新也一起取消了 2.设置编码为utf-8:默认就是了,不用设置了 3.设置常用的开发字体:Consolas, 默认就是了,不用设置了 字体对开发也 ...
- php+mysql实现微信公众号回复关键词新闻列表
非常抱歉,我之前理解有误.如果您想要实现在公众号发送关键词,返回新闻列表的功能,可以按照以下步骤进行操作: 1. 创建一个数据库表,用于存储新闻的标题.链接和内容等信息.例如,可以创建一个名为news ...
- 发现Mysql的主从数据库没有同步,差点凉凉了
摘要:今天发现Mysql的主从数据库没有同步,瞬间整个人头皮发麻. 本文分享自华为云社区<糟了,生产环境数据竟然不一致,人麻了!>,作者:冰 河 . 今天发现Mysql的主从数据库没有同步 ...
- 自学C#,要懂得善用MSDN
很多初学者学习编程,都会通过看别人写的教程.或者录制的视频,来学习. 这是一个非常好的途径,因为这个是非常高效的. 但是这样,存在两个问题: 1.教程不够全面:任何再好的教程,都无法囊括所有的知识点, ...
- 简单记录下RestTemplate使用方法
1.设置get方法 ResponseEntity<JSONObject> responseEntity= restTemplate.getForEntity(url,JSONObject. ...
- RTSP&IGMP详解
RTSP协议 一.概述 1)RTSP(Real ...
- CF1037G A Game on Strings Sol
有趣题. 首先"分成若干个互不相干的子串"是子游戏的定义,可以用 SG 函数处理. 然而接下来试着打了半个多小时的表,没有找到任何规律. 但是发现 SG 函数的状态转移是很简单的. ...
- Node + Express 后台开发 —— 登录标识
登录标识 系统通常只有登录成功后才能访问,而 http 是无状态的.倘若直接请求需要登录才可访问的接口,假如后端反复查询数据库,而且每个请求还得带上用户名和密码,这都是不很好. 作为前端,我们听过 c ...
- 2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。
2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧. 答案2023-04-05: 使用github/moonf ...
- 2020-09-23:TCP头部信息有哪些?
福哥答案2020-09-23:#福大大架构师每日一题# 福哥口诀法:T源目序缺首保 紧确推和复同终 窗校紧选数(TCP格式:源端口,目的端口,序号,确认号,首部长度,保留,紧急位URG,确认位ACK, ...