转自:http://blog.sina.com.cn/s/blog_87c063060101l25y.html

转载:http://blog.csdn.net/lcw_202/article/details/6661364
 
 

在编译构建性目标时(如 make vmlinux),顶层 Makefile 的 $(dot-config) 变量值为 1 。

在顶层 Makefile 的 497-504 行看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf
 
ifeq ($(KBUILD_EXTMOD),)
# Read in dependencies to all Kconfig* files,
make sure to run
# oldconfig if changes are
detected.
-include include/config/auto.conf.cmd
 
# To avoid any implicit rule to kick in, define
an empty command
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
 
# If .config is newer than
include/config/auto.conf, someone tinkered
# with it and forgot to run make
oldconfig.
# if auto.conf.cmd is missing then we are
probably in a cleaned tree so
# we execute the config step to be sure to
catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
        $(Q)$(MAKE)
-f $(srctree)/Makefile silentoldconfig

其中,

引用
-include include/config/auto.conf
-include include/config/auto.conf.cmd

这两行尝试包含 auto.conf 和 auto.conf.cmd 这两个文件。由于使用 -include
进行声明,所以即使这两个文件不存在也不会报错退出,比如在第 1 次编译内核,且已经配置好 .config
文件时,这两个文件还未生成。

假如我们已经编译好了 vmlinux 这个目标,那么我们会在 include/config 这个目录下看到 auto.conf 和
auto.conf.cmd 这两个文件。

从 include/config/%.conf:
$(KCONFIG_CONFIG) include/config/auto.conf.cmd
这条语句可以知道,auto.conf 文件依赖于 $(KCONFIG_CONFIG) 和
include/config/auto.conf.cmd 。其中 $(KCONFIG_CONFIG) 变量的值就是 .config
这个配置文件。那么

 include/config/auto.conf.cmd
这个文件应该在什么时候生成?

现在仍然假设 auto.conf 和 auto.conf.cmd 还没有生成,那么由上面的 $(KCONFIG_CONFIG) include/config/auto.conf.cmd:
; 这条语句知道,该语句中的目标没有依赖,也没有生成它的规则命令,所以可想 GNU Make 本身无法生成
auto.conf.cmd 的。然后该条语句后面的一个分号表明,这两个目标被强制是最新的,所以下面这条命令得以执行:
$(Q)$(MAKE) -f $(srctree)/Makefile
silentoldconfig

这里我们看到要生成一个目标 silentoldconfig ,这个目标定义在 scripts/kconfig/Makefile
中。因为这里使用 -f 选项重新指定了顶层 Makefile,而目标又是 silentoldconfig ,所以该命令最终会在顶层
Makefile 的 462-464 这里执行:

1
2
3
%config: scripts_basic outputmakefile
FORCE
        $(Q)mkdir
-p include/linux include/config
        $(Q)$(MAKE)
$(build)=scripts/kconfig $@

这时,我们来到 scripts/kconfig/Makefile 文件里。在该文件的 32-34 行看到:

1
2
3
silentoldconfig: $(obj)/conf
        $(Q)mkdir
-p include/generated
        $<
-s
$(Kconfig)

从上面看到,silentoldconfig 目标需要依赖 conf 这个程序,该程序也在 scripts/kconfig
目录下生成。
$< -s $(Kconfig) 该条命令相当于
conf -s $(Kconfig) ,这里 $(Kconfig) 是位于不同平台目录下的 Kconfig 文件,比如在 x86
平台就是 arch/x86/Kconfig 。

conf 程序的源代码的主函数在同目录的 conf.c 文件中,在 main() 函数中看到:

1
2
3
4
5
6
7
8
9
10
while ((opt =
getopt(ac, av,
"osdD:nmyrh")) != -1)
{
        switch
(opt) {
        case
'o':
            input_mode
= ask_silent;
            break;
        case
's':
            input_mode
= ask_silent;
            sync_kconfig
= 1;
            break;
...
...

所以,在使用 s 参数时,sync_kconfig 这个变量会为 1 。同样在 main() 函数还看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
    if
(sync_kconfig) {
        name
= conf_get_configname();
        if
(stat(name, &tmpstat)) {
            fprintf(stderr,
_(
"***\n"
                "***
You have not yet configured your kernel!\n"
                "***
(missing kernel config file \"%s\")\n"
                "***\n"
                "***
Please run some configurator (e.g. \"make oldconfig\"
or\n"
                "***
\"make menuconfig\" or \"make xconfig\").\n"
                "***\n"),
name);
            exit(1);
        }
    }

上面代码中,如果我们从未配置过内核,那么就会打印出错误信息,然后退出。这里假设已经配置过内核,并生成了 .config 文件,那么在
main() 函数中会来到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    switch
(input_mode) {
    case
set_default:
        if
(!defconfig_file)
            defconfig_file
= conf_get_default_confname();
        if
(conf_read(defconfig_file)) {
            printf(_("***\n"
                "***
Can't find default configuration \"%s\"!\n"
                "***\n"),
defconfig_file);
            exit(1);
        }
        break;
    case
ask_silent:
    case
ask_all:
    case
ask_new:
        conf_read(NULL);
        break;
...
...

由于使用 s 选项,则 input_mode 为 ask_silent,所以这里会执行 conf_read(NULL);
函数。
conf_read(NULL); 函数用来读取 .config 文件。读取的各种相关内容主要存放在一个 struct symbol
结构链表里,而各个结构的相关指针则放在一个 symbol_hash[] 的数组中,对于数组中元素的寻找通过 fnv32
哈希算法进行定位。

最后会来到 conf.c 中的底部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    if
(sync_kconfig) {
        
        if
(conf_get_changed() &&
conf_write(NULL)) {
            fprintf(stderr,
_(
"\n*** Error during writing of
the kernel configuration.\n\n"
));
            exit(1);
        }
        if
(conf_write_autoconf()) {
            fprintf(stderr,
_(
"\n*** Error during update of the
kernel configuration.\n\n"
));
            return
1;
        }
    }
else {
        if
(conf_write(NULL)) {
            fprintf(stderr,
_(
"\n*** Error during writing of
the kernel configuration.\n\n"
));
            exit(1);
        }
    }

实际上也只有当处理 silentoldconfig 目标是 sync_kconfig 变量才会为 1 。上面代码中的
conf_write_autoconf() 函数就用来生成 auto.conf, auto.conf.cmd 以及
autoconf.h 这 3 个文件。

在 if (conf_get_changed() && conf_write(NULL))
这个判断里,conf_get_changed() 函数判断 .config 文件是否做过变动,如果是,那么会调用
conf_write(NULL) 来重新写 .config 文件。实际上,对于 defconfig, oldconfig,
menuconfig 等目标来说,conf 程序最终也是调用 conf_write() 函数将配置结果写入 .config
文件中(最后那个 else 里的内容便是)。

确保了 .config 已经最新后,那么调用 conf_write_autoconf() 生成
auto.conf,auto.conf.cmd 以及 autoconf.h 这 3 个文件。

来到 conf_write_autoconf() 函数:

在 conf_write_autoconf() 里,调用
file_write_dep("include/config/auto.conf.cmd"); 函数将相关内容写入
auto.conf.cmd 文件。在生成的 auto.conf.cmd 文件中可以看到:

1
2
include/config/auto.conf: \
        $(deps_config)

可以看到 auto.conf 文件中的内容依赖于 $(deps_config) 变量里定义的东西,这些东西基本上是各个目录下的
Kconfig 以及其它一些相关文件。

auto.config 和 .config 的差别是在 auto.config 里去掉了 .config
中的注释项目以及空格行,其它的都一样。

仍然在 conf_write_autoconf() 里,分别建立了 .tmpconfig,.tmpconfig_tristate 和
.tmpconfig.h 这 3 个临时文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
out
=
fopen(".tmpconfig",
"w");
    if
(!out)
        return
1;
 
    tristate
=
fopen(".tmpconfig_tristate", "w");
    if
(!tristate) {
        fclose(out);
        return
1;
    }
 
    out_h
=
fopen(".tmpconfig.h",
"w");
    if
(!out_h) {
        fclose(out);
        fclose(tristate);
        return
1;
    }

然后将文件头的注释部分分别写入到这几个临时文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    sym
= sym_lookup(
"KERNELVERSION",
0);
    sym_calc_value(sym);
    time(&now);
    fprintf(out,
"#\n"
             "#
Automatically generated make config: don't edit\n"
             "#
Linux kernel version: %s\n"
             "#
%s"
             "#\n",
             sym_get_string_value(sym),
ctime(&now));
    fprintf(tristate,
"#\n"
              "#
Automatically generated - do not edit\n"
              "\n");
    fprintf(out_h,
"\n"
               "#define
AUTOCONF_INCLUDED\n"
,
               sym_get_string_value(sym),
ctime(&now));

接着在 for_all_symbols(i, sym) 这个循环里(是一个宏)里将相关内容分别写入到这几个文件中。

在最后一段代码中,将这几个临时文件进行改名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name
=
getenv("KCONFIG_AUTOHEADER");
    if
(!name)
        name
=
"include/generated/autoconf.h";
    if
(rename(".tmpconfig.h",
name))
        return
1;
    name
=
getenv("KCONFIG_TRISTATE");
    if
(!name)
        name
=
"include/config/tristate.conf";
    if
(rename(".tmpconfig_tristate",
name))
        return
1;
    name
= conf_get_autoconfig_name();
    
    if
(rename(".tmpconfig",
name))
        return
1;

上面代码中的 conf_get_autoconfig_name() 实现为:

1
2
3
4
5
6
const char *conf_get_autoconfig_name(void)
{
    char
*name = getenv("KCONFIG_AUTOCONFIG");
 
    return
name ? name : "include/config/auto.conf";
}

从上面可以看到,分别生成了以下几个文件:

引用
include/generated/autoconf.h
include/config/tristate.conf
include/config/auto.conf

其中 include/generated/autoconf.h 头文件由内核本身使用,主要用来预处理 C 代码。比如在 .config
或 auto.conf 中定义要编译为模块的项,如:
CONFIG_DEBUG_NX_TEST=m
在 autoconf.h 中会被定义为:
#define CONFIG_DEBUG_NX_TEST_MODULE 1

在 .config 或 auto.conf 后接字符串值的项,如:
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
在 autoconfig.h 中会被定义为:
#define CONFIG_DEFCONFIG_LIST
"/lib/modules/$UNAME_RELEASE/.config"

同样对应于 int 型的项如 CONFIG_HZ=1000 在 autoconf.h 中被定义为 #define CONFIG_HZ
1000 。

 

《Linux内核Makefile分析》之 auto.conf, auto.conf.cmd, autoconf.h【转】的更多相关文章

  1. linux内核Makefile整体分析

    转自:http://www.cnblogs.com/amanlikethis/p/3675486.html <请阅读原文> 一.概述 1.本文的意义 众多的资料(<嵌入式Linux应 ...

  2. Linux内核源代码分析方法

    Linux内核源代码分析方法   一.内核源代码之我见 Linux内核代码的庞大令不少人"望而生畏",也正由于如此,使得人们对Linux的了解仅处于泛泛的层次.假设想透析Linux ...

  3. 2019-2020-1 20199303 《Linux内核原理分析》 第一周作业

    2019-2020-1 20199303 <Linux内核原理分析> 第一周作业 1. 环境准备 在众多的Linux发行版中,Ubuntu,小红帽还有类Unix系统的BSD系统,我选择了目 ...

  4. linux内核makefile概览

    linux内核makefile概览 本博客参照内核官方英文文档 linux的内核makefile主要用于编译整个内核源码,按照用户的需求生成各种目标文件,对于用户来说,编译内核时非常简单的,只需要几个 ...

  5. Linux内核Makefile文件(翻译自内核手册)

    --译自Linux3.9.5 Kernel Makefiles(内核目录documention/kbuild/makefiles.txt) kbuild(kernel build) 内核编译器 Thi ...

  6. Linux内核(3) - 分析内核源码如何入手(下)

    下面的分析,米卢教练说了,内容不重要,重要的是态度.就像韩局长对待日记的态度那样,严谨而细致. 只要你使用这样的态度开始分析内核,那么无论你选择内核的哪个部分作为切入点,比如USB,比如进程管理,在花 ...

  7. Linux内核(2) - 分析内核源码如何入手(上)

    透过现象看本质,兽兽们无非就是一些人体艺术展示.同样往本质里看过去,学习内核,就是学习内核的源代码,任何内核有关的书籍都是基于内核,而又不高于内核的. 既然要学习内核源码,就要经常对内核代码进行分析, ...

  8. 《Linux内核与分析》第七周

    by 21035130王川东 Linux内核如何装载和启动一个可执行程序 一. EIF文件格式: 1.ELF头部在文件的开始,描述文件的总体格式,保存了路线图,描述该文件的组织情况,即生成该文件系统的 ...

  9. Linux内核启动分析过程-《Linux内核分析》week3作业

    环境搭建 环境的搭建参考课件,主要就是编译内核源码和生成镜像 start_kernel 从start_kernel开始,才真正进入了Linux内核的启动过程.我们可以把start_kernel看做平时 ...

随机推荐

  1. 五 python并发编程之IO模型

    一 IO模型介绍 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问 ...

  2. vue 组件的书写

    简单的来说是 vue组件最核心的就是props和自定义函数,来实现组件的开发 最简单的一个组件 子组件如下: <template> <div class="bgClass& ...

  3. HTML5实现获取地理位置信息并定位功能

    HTML5提供了地理位置定位功能(Geolocation API),能确定用户位置,我们可以借助HTML5的该特性开发基于地理位置信息的应用.本文结合实例给大家分享如何使用HTML5,借助百度.谷歌地 ...

  4. 千万不要错过这几道Python面试题,Python面试题No16

    第1题: python下多线程的限制以及多进程中传递参数的方式? python多线程有个全局解释器锁(global interpreter lock),简称GIL,这个GIL并不是python的特性, ...

  5. ArcGis API for JavaScript学习——离线部署API

    ArcGis API for JavaScript开发笔记——离线部署API 以3.18版API为例: 在加载图图前引用GIS服务是必须的.有两种方法,一是在线引用,而是离线部署引用. 在线引用: & ...

  6. Android引导页过多导致OOM内存泄漏

    摘要:前几天推广我们APP的时候,有些手机加载引导页的时候会闪退或崩溃,在Bugly显示是OOM异常.    然后Bugly上面显示的解决方案是: 该异常表示未能成功分配字节内存,通常是因为内存不足导 ...

  7. Unable to execute dex: Multiple dex files define Lcom/myapp/R$array;

    Unable to execute dex: Multiple dex files define Lcom/myapp/R$array; 我这个问题最后解决方式是,吧工程里面用同一个v4包. 很明显, ...

  8. 倍增 - 强制在线的LCA

    LCA 描述 给一棵有根树,以及一些询问,每次询问树上的 2 个节点 A.B,求它们的最近公共祖先. !强制在线! 输入 第一行一个整数 N. 接下来 N 个数,第 i 个数 F i 表示 i 的父亲 ...

  9. day05_06 continue语句、while循环

    输入满3次跳出,然后留一句话 for i in range(3): username = input("Username:") password = input("Pas ...

  10. Your branch is ahead of 'origin/master' by 21 commits.

    当切换到主分支后,准备 git pull 拉取远程 master 分支时,提示本地主分支显示有 21 个commits 问题原因: 因为你修改了 local master 本地的主分支,可以选择以下方 ...