《Linux内核Makefile分析》之 auto.conf, auto.conf.cmd, autoconf.h【转】

   2023-02-09 学习力0
核心提示:转自: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 行看到:?1234

转自: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 。
 
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 【强转】QEMU+GDB调试linux内核全过程
    【强转】QEMU+GDB调试linux内核全过程
    昨天更新了一篇名为《QEMU+GDB调试linux内核全过程》[link][https://blog.csdn.net/weixin_37867857/article/details/88138432]的博客,发现排版比较混乱,而且思维也比较混乱。咋一看下来简直是惨不忍睹,而且会给读者在安装过程中一种云里雾里的感觉,加上
    03-08
  • Linux下Bochs,NASM安装和使用 linux bom
    Linux下Bochs,NASM安装和使用 linux bom
    以Ubuntu为例,先更新一下:sudo apt-get updatesudo apt-get upgrade然后安装Bochs环境:sudo apt-get install build-essential xorg-dev libgtk2.0-dev安装NASMNASM官网下载,这以nasm-2.14.02.tar.gz为例:用tar zxvf nasm-2.14.02.tar.gz解压后编译安装cd
    03-08
  • 把玩Alpine linux(一):安装
    把玩Alpine linux(一):安装
    导读Alpine Linux是一个面向安全应用的轻量级Linux发行版。它采用了musl libc和busybox以减小系统的体积和运行时资源消耗,同时还提供了自己的包管理工具apk。Alpine 的内核都打了grsecurity/PaX补丁,并且所有的程序都编译为Position Independent Executabl
    03-08
  • 日志审计与分析实验三(rsyslog服务器端和客户端配置)(Linux日志收集)
    日志审计与分析实验三(rsyslog服务器端和客户
     Linux日志收集一、实验目的:1、掌握rsyslog配置方法2、配置rsyslog服务收集其他Linux服务器日志:C/S架构:客户端将其日志上传到服务器端,通过对服务器端日志的查询,来实现对其他客户端的日志进行集中管理;下面实现就是通过两套机器来实现,(server:19
    03-08
  • Linux学习系列--如何在Linux中进行文件的管理
    Linux学习系列--如何在Linux中进行文件的管理
    文件在常见的Linux的文件系统中,经常使用能了解到的文件管理系统是分为多个文件夹进行管理的。如何查看文件路径 pwd ,在文件目录中,会有一个点(.)代表的是当前目录,两个点(..)代表的是当前目录的上层目录在Linux下,所有以点开始的文件都是“隐藏文件
    03-08
  • [JetBrains] 我想在 Linux 上使用 macOS 键绑定!
    [JetBrains] 我想在 Linux 上使用 macOS 键绑定
    很高兴认识你,我的名字是kitakkun。我最近开始实习,是工程界的新手。顺便说一句,这是我的第一篇文章。你最喜欢的操作系统是什么?视窗?苹果系统?还是Linux?我将它们全部用于不同的目的,但感觉就像 macOS ≒ LinuxWindows。一两个月前,我最喜欢 Linux
    03-08
  • linux 配置Socks51
    linux 配置Socks51
    ***大家耳熟能详,但是socks用到的人比较少,那什么是socks呢?请看第二段或者百度百科,socks分别有4和5两个版本,现在5为主流。工作中经常用***访问国外,但是同时国内的速度又慢了,让人很纠结,实际上这个时候可以考虑使用socks。指定某一个程序使用国外s
    02-10
  • linux下如何单独编译设备树? linux设备树是什
    答: make vendor/device_name.dtb  如: make freescale/fsl-1043a-rdb.dtb
    02-10
  • linux下mysql开启远程访问权限及防火墙开放3306端口
    linux下mysql开启远程访问权限及防火墙开放3306
    开启mysql的远程访问权限默认mysql的用户是没有远程访问的权限的,因此当程序跟数据库不在同一台服务器上时,我们需要开启mysql的远程访问权限。主流的有两种方法,改表法和授权法。相对而言,改表法比较容易一点,个人也是比较倾向于使用这种方法,因此,这
    02-10
  • 移植linux3.7到nuc900系列开发板遇到的问题
    通过移植学习linux新版本内核,大概了解一下内核变化。记录一下移植过程中遇到的问题或值得注意的地方。1,添加一款arm9芯片的支持首先修改\arch\arm\tools\mach-types文件添加一行w90p950evbMACH_W90P950EVBW90P950EVB同目录下的脚本文件在编译内核时会根据
    02-10
点击排行