Linux Command Line 解析

0 处理模型

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

1 配置格式

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

2 配置方式

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 }
 
 
3.1.2 .init.setup
 
“.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\n",
                        params[i].set);
                 return params[i].set(val, ¶ms[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()中已经处理的操作,那么这里不再处理;如果是查找到的条目中没有操作函数,那么这表示是过时的数据定义(有些早期的代码,没有定义这个函数);如果不是以上两种情形,那么利用找到的函数对参数进行处理。

Linux Command Line 解析的更多相关文章

  1. 《The Linux Command Line》 读书笔记04 Linux用户以及权限相关命令

    Linux用户以及权限相关命令 查看身份 id:Display user identity. 这个命令的输出会显示uid,gid和用户所属的组. uid即user ID,这是账户创建时被赋予的. gi ...

  2. 《The Linux Command Line》 读书笔记02 关于命令的命令

    <The Linux Command Line> 读书笔记02 关于命令的命令 命令的四种类型 type type—Indicate how a command name is inter ...

  3. 《The Linux Command Line》 读书笔记01 基本命令介绍

    <The Linux Command Line> 读书笔记01 基本命令介绍 1. What is the Shell? The Shell is a program that takes ...

  4. Linux Command Line Basics

    Most of this note comes from the Beginning the Linux Command Line, Second Edition by Sander van Vugt ...

  5. 15 Examples To Master Linux Command Line History

    When you are using Linux command line frequently, using the history effectively can be a major produ ...

  6. 10 Interesting Linux Command Line Tricks and Tips Worth Knowing

    I passionately enjoy working with commands as they offer more control over a Linux system than GUIs( ...

  7. Reso | The Linux Command Line 的中文版

    http://book.haoduoshipin.com/tlcl/book/zh/ 本书是 The Linux Command Line 的中文版, 为大家提供了多种不同的阅读方式. 中英文双语版- ...

  8. [笔记]The Linux command line

    Notes on The Linux Command Line (by W. E. Shotts Jr.) edited by Gopher 感觉博客园是不是搞了什么CSS在里头--在博客园显示效果挺 ...

  9. Linux Command Line(II): Intermediate

    Prerequisite: Linux Command Line(I): Beginner ================================ File I/O $ cat > a ...

随机推荐

  1. QT 按钮(4种样式)

    // 1.正常  btnNormal_ = new QPushButton("Normal Button", this);  // 2.可停驻  btnCheck_ = new Q ...

  2. JAVA类与对象(四)----成员变量与局部变量 、成员方法、构造方法

    类体中的变量分为两部分.变量定义部分定义的变量为类的成员变量,在方法体中定义的变量和方法中涉及的变量称为局部变量. 成员变量和局部变量的区别: (1).成员变量在整个类中都有效,局部变量只在定义它的方 ...

  3. SFI(段文件描述符存在的理由与意义)

    SFI:short file identifier,短文件描述符 SFI的取值必须在0001-001E之间,即0-30 SFI存在的理由:有些COS命令可以不需要事先选择文件而直接通过SFI快速访问文 ...

  4. storm集成kafka

    kafkautil: import java.util.Properties; import kafka.javaapi.producer.Producer; import kafka.produce ...

  5. ThinkDev.Logging-Queue模块介绍

    Queue,ThinkDev.Logging对内存级队列的封装. 主要针对需要简单进程内内存级队列提供支持,应用无需关心存储及线程. 配置例子: <!-- 队列对象 --> <Que ...

  6. spring--注入类型--构造方法(不常用)

    3.3.1.1. Constructor Injection Constructor-based DI is effected by invoking a constructor with a num ...

  7. discuz之搭建

    本篇将介绍IIS+MySQL+DiscuzX3.1+UCenter1.6+Asp.Net+PHP的部署 大部分都是搬运过来的,当然我会注明搬运地点 搭建 首先说明本机基本信息 系统========== ...

  8. Facebook 和 Google 如何激发工程师的创造力

    原文链接:http://kb.cnblogs.com/page/193450/ 今天终于“朝圣”了两个伟大的公司——Facebook和Google,对创造力和驱动力的来源有了更多的理解,尤其是对于典型 ...

  9. EXCEL 跨表比较数据

    Public Sub Compare(fullname As String, sheet As String) Dim conn, sql, rows, i, cellContents ,rowInd ...

  10. nginx+php+flight 构建RESTFul API

    配置: Nginx: conf目录下nginx.conf配置文件. 第44行改为:root   D:/wwwroot/www; 第45行改为:index  index.html index.htm i ...