通过对过渡篇的学习,相信你已经具有了相当的知识储备,接下来就来继续学习单个.o文件的编译过程

以/drivers/char/mem.c的编译为例

  1. make /drivers/char/mem.o

一. 找到目标及其构建规则*
在顶层目录的Makefile中:

  1. %.o: %.c prepare scripts FORCE
  2. $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)

这格式其实和我们平时自己写的规则是差不多的。.o的文件依赖于同名的.c文件,然后执行了一个命令来生成。不过呢,确实还多了些东西,包括一些特殊的依赖条件。

为了更直接的看懂这条规则,在这段规则前面添加一个“#”号

  1. #$(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变量

  1. # scripts/Kbuild.include
  2. build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
  3. # 其中$(KBUILD_SRC)常规情况下为空,所以变量的定义可简化为
  4. build := -f scripts/Makefile.build obj

2. build-dir 和 target-dir变量

  1. #Single targets
  2. #---------------------------------------------------------------------------
  3. #Single targets are compatible with:
  4. #- build with mixed source and output
  5. #- build with separate output dir 'make O=...'
  6. #- external modules
  7. #
  8. #target-dir => where to store outputfile
  9. #build-dir => directory in kernel source tree to use
  10. ifeq ($(KBUILD_EXTMOD),)
  11. build-dir = $(patsubst %/,%,$(dir $@))
  12. target-dir = $(dir $@)
  13. else
  14. zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@)))
  15. build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash))
  16. target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@))
  17. endif

通过注释可以了解到,这两个变量一个是做编译的路径,一个是目标存放的路径。

定义本身分为两种情况

  • 当KBUILD_EXTMOD为空,表示不是编译内核模块
  • 当KBUILD_EXTMOD不为空,表示是在编译内核模块

我们这里并没有编译内核模块,所以KBUILD_EXTMOD为空,采用的是第一种情况的定义。从显示出的命令来看build-dir和target-dir分别定义为drivers/char 和drivers/char/。只是相差了最后的一个/。

3. 规则展开的最终形式

  1. @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目标

  1. # Built-in and composite module parts
  2. $(obj)/%.o: $(src)/%.c FORCE
  3. $(call cmd,force_checksrc)
  4. $(call if_changed_rule,cc_o_c)

依赖条件暂不关注,看多了会头痛,哈哈。这个规则中的两条命令:
第一条是做代码检查的,暂不关注。
第二条看着名字就有点像,cc_o_c,把c代码通过cc制作成.o文件。这个正是我们要关注的,就分析它了。

2. if_changed_rule函数
上一节,我们把关注的焦点定在了这条语句。

  1. $(call if_changed_rule,cc_o_c)

其中if_changed_rule函数定义在Kbuild.include中,因为在顶层Makefile中包含了Kbuild.include这个文件

  1. include $(srctree)/scripts/Kbuild.include

if_changed_rule函数的定义如下:

  1. # Usage: $(call if_changed_rule,foo)
  2. # Will check if $(cmd_foo) or any of the prerequisites changed,
  3. # and if so will execute $(rule_foo).
  4. if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ), \
  5. @set -e; \
  6. $(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中:

  1. define rule_cc_o_c
  2. $(call echo-cmd,checksrc) $(cmd_checksrc) \
  3. $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \
  4. $(cmd_modversions) \
  5. $(call echo-cmd,record_mcount) \
  6. $(cmd_record_mcount) \
  7. scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \
  8. $(dot-target).tmp; \
  9. rm -f $(depfile); \
  10. mv -f $(dot-target).tmp $(dot-target).cmd
  11. endef

又是一长串…。不过相信你经过前面的基础知识的学习,能够轻松应对它。经过对无用命令的剥离,剩下一个与我们相关的命令行:

  1. $(call echo-cmd,cc_o_c) $(cmd_cc_o_c);

把$(call echo-cmd,cc_o_c) $(cmd_cc_o_c)展开得到:

  1. # $(call echo-cmd,cc_o_c)展开:
  2. echo 'CC $@' ; CC $@

执行make /drivers/char/mem.o命令时打印如下:

总结
linux内核中编译单个.o的执行顺序:

  1. -----------------------------------------------------------------------------------
  2. # Makefile(顶层)
  3. %.o: %.c
  4. make -f scripts/Makefile.build obj=drivers/char drivers/char/mem.o
  5. -----------------------------------------------------------------------------------
  6. # scripts/Makefile.build
  7. $(obj)/%.o: $(src)/%.c
  8. $(call if_changed_rule,cc_o_c)
  9. -----------------------------------------------------------------------------------
  10. #scripts/Makefile.build 打印这条命令,并且执行命令
  11. rule_cc_o_c
  12. $(call echo-cmd,cc_o_c) $(cmd_cc_o_c)
  13. -----------------------------------------------------------------------------------
  14. #scripts/Makefile.build 执行命令
  15. cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<

内核源码中单个.o文件的编译过程(六)的更多相关文章

  1. Android系统篇之—-编写简单的驱动程序并且将其编译到内核源码中【转】

    本文转载自:大神 通过之前的一篇文章,我们了解了 Android中的Binder机制和远程服务调用 在这篇文章中主要介绍了Android中的应用在调用一些系统服务的时候的原理,那么接下来就继续来介绍一 ...

  2. Caffe源码中caffe.proto文件分析

    Caffe源码(caffe version:09868ac , date: 2015.08.15)中有一些重要文件,这里介绍下caffe.proto文件. 在src/caffe/proto目录下有一个 ...

  3. linux内核源码中两个重要的宏

    转载:http://www.cnblogs.com/skywang12345/p/3562146.html 倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 containe ...

  4. Linux 内核源码中likely()和unlikely()

    ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首先明确: if (likely(value))等价于if (value)if (likely( ...

  5. Linux内核源码中的likely和unlikely释疑【转】

    本文转载自:https://my.oschina.net/armsky/blog/15320 ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首 ...

  6. Linux 内核源码中likely()和unlikely()【转】

    本文转载自:http://blog.csdn.net/tigerjibo/article/details/8279183 ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们 ...

  7. live555源码研究(十)------在编译过程中遇到的问题及解决方法

    一.编译testOnDemandRTSPServer.cpp. 在testProgs项目中,加入testOnDemandRTSPServer.cpp进行编译,编译类型是编译成exe文件,在编译过程中会 ...

  8. linux 内核源代码情景分析——linux 内核源码中的汇编语言代码

    1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...

  9. 如何单独编译Linux内核源码中的驱动为可加载模块?

    答: 分为两步: 1. 配置某个驱动为模块(如: CONFIG_RTC_XXX=m) 2. 指定路径并编译, 如编译drivers/rtc中的驱动 make SUBDIRS=drivers/rtc m ...

  10. linux内核源码中常见宏定义

    http://blog.csdn.net/yangdelong/article/details/5508057

随机推荐

  1. Linux 根据名称自动kill掉当前相关进程

    ps aux | grep app | grep -v "grep" | awk '{print $2}' | xargs -r kill

  2. 深度学习03-(图像梯度处理、图像轮廓、图像预处理在AI中的应用)

    深度学习03-计算机视觉基本理论2 深度学习03-(计算机视觉基本理论2) 图像梯度处理 什么是图像梯度 模板运算 均值滤波 高斯滤波 中值滤波 边沿检测 锐化 图像轮廓 什么是图像轮廓 查找和绘制轮 ...

  3. [C++基础入门] 2、数据类型

    文章目录 2 数据类型 2.1 整型 2.2 sizeof关键字 2.3 实型(浮点型) 2.4 字符型 2.5 转义字符 2.6 字符串型 2.7 布尔类型 bool 2.8 数据的输入 2 数据类 ...

  4. 2022年5月5日模拟赛题解与总结(ABC237)

    总结 初一第一,竞赛班第二 还可以,为了照顾提高班来的四个同学放了四个水题,可惜他们做的不是很理想,希望他们下次可以获得满意的成绩 这次做的其实是 AtCoder ABC237 A.Not Overf ...

  5. Prism Sample 9 ChangeConvention

    上个例子跳过了ViewModelLocator,因是采用约定的方式最为方便. 如果有人要修改约定,自定义view和viewModel的默认自动定位方式,怎么办呢? 在app.xaml.cs重写以下方法 ...

  6. dede网站flash中图片不能正常显示解决办法

    专业微信开发 网站制作  就在西安格创网络  联系电话:18009249661  原因:因为服务器或虚拟主机用了CDN加速,导致图片是调用远程缓存,只能显示FLASH控件,但图片不能正常显示,常见于西 ...

  7. pytest插件开发

    插件的加载方式 外部插件: pip install 安装的插件 本地插件: pytest 自动模块发现机制(conftest.py存放) 内置插件: 代码内部的_pytest目录加载 什么是hook ...

  8. 2023-01-01:remix-ide是浏览器的ide,官方已经提供地址,但是需要连接外网。如果是内网,需要自己在服务器里搭建remix-ide;另一种方式是用remix-ide的桌面版。这里只讨论

    2023-01-01:remix-ide是浏览器的ide,官方已经提供地址,但是需要连接外网.如果是内网,需要自己在服务器里搭建remix-ide:另一种方式是用remix-ide的桌面版.这里只讨论 ...

  9. 2020-11-06:go中,谈一下调度器。

    福哥答案2020-11-06:·MPG模型:goroutine的并发模型可以归纳为MPG模型:·MPG概念:线程(machine,系统线程,物理线程)-内核(processor)-协程(gorouti ...

  10. drf-spectacular

    介绍 drf-spectacular是为Django REST Framework生成合理灵活的OpenAPI 3.0模式.它可以自动帮我们提取接口中的信息,从而形成接口文档,而且内容十分详细,再也不 ...