zoukankan      html  css  js  c++  java
  • Linux Command Line 详细解析


    全局变量 

    有关内核参数的全局变量一共有三个,分别是command_line,default_command_line和saved_command_line,这三个变量都定义在同一个文件即arch/arm/kernel/子目录下的setup.c文件中: 
    static char command_line[COMMAND_LINE_SIZE]; 
    //用于存放setup_arch()分析后的内核参数 
    //指向它的指针将被返回给start_kernel() 
    char saved_command_line[COMMAND_LINE_SIZE]; 
    //存放setup_arch()解析前的完整的内核参数,供 
    //start_kernel()打印 
    static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; 
    //存放缺省的内核参数,如果没有machine-dep 
    //的代码更改内核参数,它将作为完整的未经解 
    //析参数供解析 

    解析过程 
    内核参数的解析一共有两处,一处是setup_arch()->parse_cmdline()用于解析内核参数中关于内存的部分,另外一处是start_kernel()->parse_option()用于解析其余部分,下面分析一下内核解析内核参数的全过程 

    start_kernel()中对内核参数的解析 
    内核参数的解析在start_kernel()中完成(我是指在start_kernel()之前没有对内核参数做处理,当然某些特定的板子除外),下面是其中涉及到内核参数的变量和操作 
    asmlinkage void __init start_kernel(void) 

    char * command_line; //定义了一个内核参数的指针变量 
    extern char saved_command_line[]; //声明对saved_command_line的引用 
    … 
    setup_arch(&command_line); 
    //将该变量的指针传进setup_arch()用以获得内核参数 
    //并在setup_arch()中对内核参数做体系结构相关的处理 
    printk("Kernel command line: %s ", saved_command_line); 
    //打印完整的内核参数 
    … 
    parse_options(command_line); //对内核参数做体系结构无关的处理 
    … 


    setup_arch()对内核参数的解析 
    setup_arch()函数是体系结构相关的内核初始化过程,这其中对内核参数有涉及的变量和操作如下 
    void __init setup_arch(char **cmdline_p) 

    char *from = default_command_line; 
    //定义了一个指向default_command_line的指针 
    … 
    //这里可能存在一些对from操作的machine-dep的函数 
    … 
    memcpy(saved_command_line, from, COMMAND_LINE_SIZE); 
    //这时的from所指向的就是完整待解析的内核参数,将它复 
    //制到saved_command_line中去(以供start_kernel()打印) 
    //之所以不直接使用default_command_line是因为在此之前 
    //有可能定义一些具体板子相关的对from的操作 
    saved_command_line[COMMAND_LINE_SIZE-1] = ''; 
    //最后一位置为NULL 
    parse_cmdline(&meminfo, cmdline_p, from); 
    //调用parse_cmdline处理from指向的内核参数中关于内存的//部分 


    parse_cmdline对内核参数的解析 
    parse_cmdline做了三件事,首先它解析了from所指向的完整的内核参数,中关于内存的部分,其次它将没有解析的部分复制到command_line中,最后它将start_kernel()传进来的内核参数指针指向command_line 
    内核参数中的“mem=xxxM@xxx”将会被parse_cmdline解析,并根据结果设置meminfo,而其余部分则被复制到command_line中 




    处理模型

    Linux kernel 的启动包括很多组件的初始化和相关配置,这些配置参数一般是通过command line 进行配置的。在进行后续分析之前,先来理解一下command line 的处理模型:
    要处理的对象是一个字符串,其中包含了各种配置信息,通常各个配置之间通过空格进行分离,每个配置的表达形式是如:param=value1,value2或者很简单就是一个rw 。
    那么kernel 就需要提供对这些参数进行处理的处理函数列表。根据参数的作用以及执行期的先后不同,这些处理函数被定义到不同的段中。针对每一个参数,Kernel 都会到相应的段中查找相应的处理函数,最终进行各个组件的配置。

    配置格式

    常见的配置格式如:
    console=ttySAC0,115200 root=nfs nfsroot=192.168.1.9:/source/rootfs initrd=0x10800000,0x14af47

    配置方式

    2.1 Bootloader动态配置

    bootloader 进行参数配置,command line 将做为atag_list 的一个节点传递到Kernel

    2.2 Kernel 静态配置

    通过make menuconfig 进行配置:运行后配置boot options->Default kernel command string 。该配置将被静态编译到Kernel中,通过变量default_command_line 访问。

    3  解析配置

    3.1 相关定义

    根据执行的先后顺序,可以将处理函数分为三个大类,他们分别存在于下面三个段中(参考top/arch/arm/kernel/vmlinux.lds ):
    __setup_start = .; *(.init.setup) __setup_end = .;
    __early_begin = .; *(.early_param.init) __early_end = .;
    __start___param = .; *(__param) __stop___param = .;
    这三个段内存储的不是参数,而是command line 参数所需要的处理函数。

    3.1.1 .early_param.init

    .early_param.init ” 所定义的处理相对靠前一些,它所处理的参数例如:initrd= ,cachepolicy= ,nocache nowb , ecc= , vmalloc= ,mem= ,等等。
    这些处理函数是通过__early_param宏来定义的,例如:

    static void __init early_initrd(char **p)
    { …… }
    __early_param("initrd=", early_initrd);

    对于宏__early_param,可以在top/arch/arm/include/asm/Setup.h 中找到如下定义:
    struct early_params {
        const char *arg;
        void (*fn)(char **p);
    };
    #define __early_param(name,fn)
    static struct early_params __early_##fn __used
    __attribute__((__section__(".early_param.init"))) = { name, fn }
    .init.setup ”定义的处理则要靠后一些,它所处理的参数例如:nfsroot= , ip= ,等等。
    这些处理函数是通过__setup宏来定义的,例如:
    static int __init nfs_root_setup(char *line)
    { …… }
    __setup("nfsroot=", nfs_root_setup);
    对于宏__setup,可以在top/include/linux/Init.h 中看到:
    #define __setup_param(str, unique_id, fn, early)
        static char __setup_str_##unique_id[] __initdata __aligned(1) = str;
        static struct obs_kernel_param __setup_##unique_id
               __used __section(.init.setup)
               __attribute__((aligned((sizeof(long)))))
               = { __setup_str_##unique_id, fn, early }

    #define __setup(str, fn)
        __setup_param(str, fn, fn, 0)

    /* NOTE: fn is as per module_param, not __setup! Emits warning if fn
    * returns non-zero. */
    #define early_param(str, fn)
        __setup_param(str, fn, fn, 1)
    注意看的话,可以看到还有一个宏early_param,它与宏__setup的定义相似,只不过最后一个宏参数是1 而不是0 。1表示需要提前处理的参数。

    3.1.3 __param

    这个段中保存的是build-in 类型module 的配置参数。该宏直接用来修饰需要的变量。

    3.2  解析

    3.2.1 相关变量

    相关的变量包括:
    default_command_line
    保存memuconfig 配置的参数,如果bootloader 传入了命令行参数,那么这个新的配置将被更新到该变量中。
    boot_command_line
    存在于.init.data 段。最初是default_command_line 的拷贝。
    command_line
    存在于.init.data 段。在parse_cmdline() 中被赋值,数据来源是default_command_line 。
    saved_command_line
    用于保存没有处理过的命令行参数,是boot_caommand_line 的拷贝。
    static_command_line
    command_line 的拷贝。

    3.2.2 主要函数

    函数名称:parse_cmdline()
    操作数据:default_command_line
    函数列表: .early_param.init 段(在__early_begin 和__early_end 之间)。
    函数功能: 依据函数列表对default_command_line 中的参数进行处理。
    函数名称:parse_early_param()
    操作数据:boot_command_line
    函数列表: .init.setup 段中(__setup_start 和__setup_end 之间),主要是通过宏early_param定义的部分。
    函数功能: 依据函数列表对boot_command_line 中的参数进行处理。
    注意parse_one() 的第四个入参是0 ,而且第五个参数是NULL 。这里没有给出参数队列,不会对boot_command_line的每个参数在参数队列中进行对比查找,而是直接在do_early_param() 中进行条件判断,如果满足下面的条件,那么对该参数进行对应的操作:
    if ((p->early && strcmp(param, p->str) == 0) ||
                      (strcmp(param, "console") == 0 &&
                       strcmp(p->str, "earlycon") == 0)
                  )
    函数名称:parse_args()
    操作数据:static_command_line
    函数列表: __param 段(__start___param 和__stop___param 之间)。
    函数功能: 该操作将依据函数列表,对static_command_line 中的参数进行相应的操作。这个操作在parse_one() 的第一部分代码完成:
    for (i = 0; i < num_params; i++) {
              if (parameq(param, params[i].name)) {
                     DEBUGP("They are equal! Calling %p ",
                            params[i].set);
                     return params[i].set(val, &params[i]);
              }
       }
    接下来对于不被这个列表所支持的参数,将在unknown_bootoption() 中进行处理。在unknown_bootoption() 中主要是obsolete_checksetup()的操作。
    函数名称:obsolete_checksetup()
    操作数据:static_command_line
    函数列表: .init.setup 段中(__setup_start 和__setup_end 之间),主要是通过宏__setup定义的部分。
    函数功能: 该操作将依据函数列表,对static_command_line 中的参数进行相应的操作。如果是在parse_early_param() 中已经处理的操作,那么这里不再处理;如果是查找到的条目中没有操作函数,那么这表示是过时的数据定义(有些早期的代码,没有定义这个函数);如果不是以上两种情形,那么利用找到的函数对参数进行处理。

    3.2.3图示






     
  • 相关阅读:
    关于连通性问题的Tarjan算法暂结
    【BZOJ 3925】[Zjoi2015]地震后的幻想乡 期望概率dp+状态压缩+图论知识+组合数学
    Kruskal算法及其类似原理的应用——【BZOJ 3654】tree&&【BZOJ 3624】[Apio2008]免费道路
    【NOIP模拟赛】Drink 二维链表+模拟
    【BZOJ 2957】楼房重建&&Codechef COT5 Count on a Treap&&【NOIP模拟赛】Weed 线段树的分治维护
    【BZOJ 4198】[Noi2015]荷马史诗 哈夫曼编码
    【NOIP模拟赛】chess 建图+spfa统计方案数
    【TMD模拟赛】上低音号 链表
    【TMD模拟赛】黄金拼图 Cao
    【BZOJ 4007】[JLOI2015]战争调度 DP+搜索+状压
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744493.html
Copyright © 2011-2022 走看看