《Linux内核Makefile分析》之 auto.conf, auto.conf.cmd, autoconf.h(转载)

来源:互联网 发布:百会软件 编辑:程序博客网 时间:2024/05/01 04:58

转载链接:http://blog.sina.com.cn/s/blog_87c063060101l25y.html
注:还有很多不理解的地方,而且内核版本和目前所看的不同,内容有出入

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

ifeq ($(dot-config),1)# Read in config-include include/config/auto.confifeq ($(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 filesinclude/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 这里执行

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

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

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() 函数中看到:

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() 函数还看到:

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() 函数中会来到:

 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 中的底部:

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 文件中可以看到:

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 个临时文件:

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;    }

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

    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) 这个循环里(是一个宏)里将相关内容分别写入到这几个文件中。

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

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() 实现为:

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

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

include/generated/autoconf.hinclude/config/tristate.confinclude/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 。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 脚上皮肤干燥起皮怎么办 脚趾头冻了很痒怎么办 大脚趾里面有脓怎么办 大脚趾肉肿了怎么办 大脚趾边上肿了怎么办 大母脚趾关节疼怎么办 大脚趾有点歪了怎么办 大脚趾扭伤肿了怎么办 大脚趾外翻怎么办 知乎 颈椎带着胳膊疼怎么办 胳膊酸困无力是怎么办 腰疼引起的腿麻怎么办 手臂到手指麻痛怎么办 拔完智齿特别疼怎么办 躺着胃烧的难受怎么办 喝酒胃烧的难受怎么办 舌头破了特别疼怎么办 舌头又厚又大怎么办 牙齿咬到舌头了怎么办 舌头起了好多泡怎么办 舌头尖起泡很痛怎么办 舌头上起泡怎么办很疼 月经来了二十多天还不干净怎么办 三岁宝宝缺锌怎么办 无舌苔,舌头很红怎么办 嘴唇上肿了一块怎么办 下嘴唇突然肿了怎么办 上嘴唇突然肿厚怎么办 舌尖上火了好疼怎么办 舌头针扎似的疼怎么办 手麻了怎么办小妙招 月子生气奶少了怎么办 颈椎会引起脸麻怎么办 脑梗引起的手麻怎么办 着凉引起的腰疼怎么办 受凉引起的腰疼怎么办 着凉了腰疼厉害怎么办 腰疼压迫神经腿麻木怎么办 运动后小腿变粗怎么办 跑步后小腿变粗怎么办 嘴腮里面肉肿了怎么办