Параметр CmdLine разбор принцип анализа Linux Kernel [включить]

Ядро разбирает CmdLine времени начала и выполнено с возможностью работы в соответствии с этими параметрами, как консоли корень.

Командная_строка загрузчиком передается ядром, такие как UBoot, ядро ​​потребует параметров, передаваемых на таран, чтобы сделать список тегов, адрес для первого ядра, ядро ​​разбирает теги для получения информации, такой CmdLine.

UBoot передать аргументы ядра и ядра, как разобрать теги можно увидеть еще один из моего блога на следующей ссылке:

Сегодня ядро, чтобы проанализировать, как разобрать CmdLine после получения CmdLine.

По моим представлениям (в хронологическом порядке, как начать, как закончить), первый взгляд на два вида параметров , зарегистрированных в соответствии с ядром.
Первый общий параметр является ядром, например, консоли = корень ttyS0,115200 = / rdinit / INIT нравится. Здесь утешать, например.
Второй находится под параметры ядра каждого водителя в случае необходимости, написать драйвер, переменные параметры , если вам нужен некоторый запуск. Может быть добавлен последним в module_param Driver () , чтобы зарегистрировать значение параметра параметра , указанного ядра CmdLine времени начала.

Вот use_acm параметры драйвера / USB / гаджет / serial.c в качестве примера (этот пример немного смещена .. из-за недавнего виртуального последовательного порта отладки USB)

А общие параметры ядра

Для этих общих параметров ядра, оставляя один сегмент данных, называемый .ini.setup сегмент. В арочных / руки / ядро ​​/ vmlinux.lds в:

.init.data : {
  *(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_star
 . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
  __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start =
  __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
  __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
  . = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
 }

__Setup_start могут видеть init.setup запуск сегмента и заканчивая __setup_end.

В сегменте .init.setup хранятся в таблице отображения, соответствующие параметры обработки и общие функции ядра. В включать / Linux / init.h в

Это можно увидеть __setup и early_param макроопределения определяет obs_kernel_param структуры, структуры, и соответствующий сохраненные параметры обработчика, сохраненный в секции .init.setup.

Представьте себе, если Макровызы определения несколько файлов связь для того, ссылки будет основываться на определение obs_kernel_param положить в .init.setup сегмента.

В примере консоли, в /kernel/printk.c следующим образом:

static int __init console_setup(char *str)
{
.......
}
__setup("console=", console_setup);
__setup宏定义展开,如下:

Static struct obs_kernel_param __setup_console_setup 
__used_section(.init.setup) __attribute__((aligned((sizeof(long)))) = {
.name = “console=”,
.setup_func = console_setup,
.early = 0
}

__Setup_console_setup соединит в .init.setup компилировать период времени, в соответствии с именем параметра будет контрастировать с .init.setup CmdLine в пункте obs_kernel_param название ядра во время выполнения.

Матч вызываются для разбора параметров консоли настройки, console_setup значения параметра консоли CmdLine, за которой общий процесс разбора параметров.

Два водителя пользовательских параметров

Для водителя пользовательских параметров, ядро ​​в стороне части rodata сегмента, называемые сегменты __param в арочном / руках / ядре / vmlinux.lds следующим образом:

__param : AT(ADDR(__param) - 0) { __start___param = .; *(__param) __stop___param = .; }

Сегмент помещается в .rodata сегменте.

Сегмент, который хранится в каком виде данных?

Драйвер module_param используется для регистрации параметров, отслеживающий определение макроса, он будет в конечном итоге найти функцию __param операционный сегмент следующим образом:

/* This is the fundamental function for registering boot/module
   parameters. */
#define __module_param_call(prefix, name, ops, arg, perm, level)    \
    /* Default value instead of permissions? */         \
    static int __param_perm_check_##name __attribute__((unused)) =  \
    BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2))  \
    + BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN);   \
    static const char __param_str_##name[] = prefix #name;      \
    static struct kernel_param __moduleparam_const __param_##name   \
    __used                              \
    __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
    = { __param_str_##name, ops, perm, level, { arg } }
........
#define module_param(name, type, perm)              \
    module_param_named(name, name, type, perm)
 
#define module_param_named(name, value, type, perm)            \
    param_check_##type(name, &(value));                \
    module_param_cb(name, ¶m_ops_##type, &value, perm);        \
    __MODULE_PARM_TYPE(name, #type)
 
#define module_param_cb(name, ops, arg, perm)                     \
    __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1)

На водителя / USB / гаджет / serial.c use_acm, например, следующим образом:

static bool use_acm = true;
module_param(use_acm, bool, 0);
Module_param展开到__module_param_call,如下:

Static bool use_acm = true;
Param_check_bool(use_acm, &(use_acm));
__module_param_call(MODULE_PARAM_PREFIX, use_acm, ¶m_ops_bool, &(use_acm, 0, -1));
__MODULE_PARAM_TYPE(use_acm, bool);

Начало __module_param_call, вы можете видеть это определение структуры kernel_param, следующим образом:

Static struct kernel_param __moduleparam_const __param_use_acm 
 __used   __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) = {
.name = MODULE_PARAM_PREFIX#use_acm,
.ops = ¶m_ops_bool,
.Perm=0,
.level = -1.
.arg = &use_acm
}

Очевидно, что, как .init.setup сегмента, будут размещены в соответствии с порядком ссылок kernel_param определены на линии связи ядра сегмента __param.

Kernel_param имеет три переменные-члены принять к сведению:

(1)

ОПС = param_ops_bool, kernel_param_ops структура определена следующим образом:

struct kernel_param_ops param_ops_bool = {
    .set = param_set_bool,
    .get = param_get_bool,
};

Эти две функции-члены для установки и извлечения значений параметров

В ядре / param.c вы можете увидеть ядро ​​поддержки драйвера типов параметров по умолчанию BOOL байт короткого USHORT INT UINT долго ULong строки (символьная строка) в СЬаграх (строка указатель) массив как.

Для поддерживаемых параметров по умолчанию типа на, param.c kernel_param_ops при условии, чтобы обрабатывать соответствующие типы параметров.

(2)

Arg = & use_acm, расширяющее определение макроса, вы можете увидеть адрес ARG сохраненной use_acm. Параметр Функция настройки param_set_bool (Const символ  Вал, Const структура kernel_param  КП)

Установите значению вали KP-> адреса Arg, т.е., чтобы изменить значение use_acm, чтобы достичь цели передачи параметров.

(3)

.name = MODULE_PARAM_PREFIX # use_acm, определяет имя kernel_param в.

MODULE_PARAM_PREFIX очень важен, определенный в включает / Linux / moduleparam.h в:

* You can override this manually, but generally this should match the
   module name. */
#ifdef MODULE
#define MODULE_PARAM_PREFIX /* empty */
#else
#define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
#endif

Если скомпилировать модули (модули делают), то MODULE_PARAM_PREFIX пуст.

Когда модуль параметра передачи, параметр с именем use_acm, так как insmod g_serial.ko use_acm = 0

Нормальное компилируется ядро, MODULE_PARAM_PREFIX для имени модуля + «»

Если мы не знаем, что имя модуля в момент массового участия, вы можете добавить в свой собственный драйвер печати, MODULE_PARAM_PREFIX распечатать имя модуля, чтобы определить свой собственный диск.

Таким образом, здесь включены serial.c ядро, в соответствии с водителем / USB / гаджет / Makefile, следующим образом:

g_serial-y          := serial.o
....
obj-$(CONFIG_USB_G_SERIAL)  += g_serial.o

В конечном счете, генерируя g_serial.o имя модуля g_serial.ko. .name = g_serial.use_acm.

Когда передача параметров ядра, параметр с именем g_serial.use_acm

Такое лечение предотвращает возникновение многих параметров одного и то же имя драйвера в ядре.

Как можно видеть, для регистрации параметров module_param, если она поддерживается тип ядра по умолчанию, параметры ядра будут обеспечивать функции обработки.

Если не ядро ​​типа поддержки, вам нужно иметь, чтобы достичь param_ops ## типа.

Этот параметр можно прокручивать, чтобы увидеть регистрацию водителей / видео / uvesafb.c в (и немного предвзято ... нечаянно нашли).

 

Параметр регистрация осуществляется на компилятор ядра и компоновщика (линкера .init.setup или определение структуры в __param)

Далее вам нужно проанализировать, как анализировать поступающую CmdLine при запуске ядра.

Три ядра синтаксического анализа CmdLine

Согласно тому, что я писал ранее Bowen нашел, start_kernel в setup_arch теги разбора получить CmdLine, скопированный в boot_command_line в. Затем мы рассмотрим start_kernel вниз.

Вызов setup_command_line, 2 части копировального CmdLine размещена saved_command_line static_command_line.

Вызов parse_early_param ниже () следующим образом:

void __init parse_early_options(char *cmdline)
{
    parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param);
}
 
/* Arch code calls this early on, or if not, just before other parsing. */
void __init parse_early_param(void)
{
    static __initdata int done = 0;
    static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
 
    if (done)
        return;
 
    /* All fall through to do_early_param. */
    strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
    parse_early_options(tmp_cmdline);
    done = 1;
}

Parse_early_param скопировать CmdLine в tmp_cmdline в окончательном parse_args вызова следующим образом:

/* Args looks like "foo=bar,bar2 baz=fuz wiz". */
int parse_args(const char *name,
           char *args,
           const struct kernel_param *params,
           unsigned num,
           s16 min_level,
           s16 max_level,
           int (*unknown)(char *param, char *val))
{
    char *param, *val;
 
    pr_debug("Parsing ARGS: %s\n", args);
 
    /* Chew leading spaces */
    args = skip_spaces(args);
 
    while (*args) {
        int ret;
        int irq_was_disabled;
 
        args = next_arg(args, ¶m, &val);
        irq_was_disabled = irqs_disabled();
        ret = parse_one(param, val, params, num,
                min_level, max_level, unknown);
        if (irq_was_disabled && !irqs_disabled()) {
            printk(KERN_WARNING "parse_args(): option '%s' enabled "
                    "irq's!\n", param);
        }
        switch (ret) {
        case -ENOENT:
            printk(KERN_ERR "%s: Unknown parameter `%s'\n",
                   name, param);
            return ret;
        case -ENOSPC:
            printk(KERN_ERR
                   "%s: `%s' too large for parameter `%s'\n",
                   name, val ?: "", param);
            return ret;
        case 0:
            break;
        default:
            printk(KERN_ERR
                   "%s: `%s' invalid for parameter `%s'\n",
                   name, val ?: "", param);
            return ret;
        }
    }
 
    /* All parsed OK. */
    return 0;
}
.....
void __init parse_early_options(char *cmdline)
{
    parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param);
}

Parse_args траверса командная_строка, получение параметров в качестве пространства режется, параметры сбора данных имени next_arg Param и значение параметров для всех параметров значей вызовов. Консоль = ttyS0,115200, то пары = консольная, Вэлы = ttyS0,115200. Вызов parse_one. Как следует:

static int parse_one(char *param,
             char *val,
             const struct kernel_param *params,
             unsigned num_params,
             s16 min_level,
             s16 max_level,
             int (*handle_unknown)(char *param, char *val))
{
    unsigned int i;
    int err;
 
    /* Find parameter */
    for (i = 0; i < num_params; i++) {
        if (parameq(param, params[i].name)) {
            if (params[i].level < min_level
                || params[i].level > max_level)
                return 0;
            /* No one handled NULL, so do it here. */
            if (!val && params[i].ops->set != param_set_bool
                && params[i].ops->set != param_set_bint)
                return -EINVAL;
            pr_debug("They are equal!  Calling %p\n",
                   params[i].ops->set);
            mutex_lock(¶m_lock);
            err = params[i].ops->set(val, ¶ms[i]);
            mutex_unlock(¶m_lock);
            return err;
        }
    }
 
    if (handle_unknown) {
        pr_debug("Unknown argument: calling %p\n", handle_unknown);
        return handle_unknown(param, val);
    }
 
    pr_debug("Unknown argument `%s'\n", param);
    return -ENOENT;
}

Потому что от parse_early_options входящих num_params = 0, так что идти непосредственно parse_one последнюю функцию handle_unknown. Эта функция передается по do_early_param разбора early_options. Как следует:

static int __init do_early_param(char *param, char *val)
{
    const struct obs_kernel_param *p;
 
    for (p = __setup_start; p < __setup_end; p++) {
        if ((p->early && parameq(param, p->str)) ||
            (strcmp(param, "console") == 0 &&
             strcmp(p->str, "earlycon") == 0)
        ) {
            if (p->setup_func(val) != 0)
                printk(KERN_WARNING
                       "Malformed early option '%s'\n", param);
        }
    }
    /* We accept everything at this stage. */
    return 0;
}

Do_early_param .init.setup поперечной секция, если есть возможный ранний obs_kernel_param, есть консоль или командная_строка параметры и obs_kernel_param Have earlycon параметры obs_kernel_param подпрограмма настройки будет призвана проанализировать аргументы.

Do_early_param имеет более высокий приоритет в командная_строке параметров для разрешения. Я перевернул следующий исходный код ядра, чтобы найти примером является арка / рука / ядро ​​/ early_printk.c, параметры использования командная_строки earlyprintk зарегистрировать один из самых ранних консоли, мы заинтересованы может ссылаться.

Если вы хотите, чтобы начать как можно раньше распечатку ядра, удобная отладка, вы можете зарегистрироваться как obs_kernel_param earlycon на ул.

В его обработчике параметров настройки register_console, регистрируя раннюю консоль, так что информация printk нормальной печати, я буду суммировать это в задней части печатающего механизма ядра для этого вопроса.

do_early_param делает, разбор функции ядра командная_строки (например, earlyprintk earlycon) должна быть настроено как можно скорее.

Do_early_param он говорит, то функция не касается общих параметров ядра и драйвера часто используют пользовательские параметры. Читайте дальше. Код выглядит следующим образом:

setup_arch(&command_line);
    mm_init_owner(&init_mm, &init_task);
    mm_init_cpumask(&init_mm);
    setup_command_line(command_line);
    setup_nr_cpu_ids();
    setup_per_cpu_areas();
    smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
 
    build_all_zonelists(NULL);
    page_alloc_init();
 
    printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
    parse_early_param();
    parse_args("Booting kernel", static_command_line, __start___param,
           __stop___param - __start___param,
           -1, -1, &unknown_bootoption);

После Parse_early_param, start_kernel называется parse_args. Вызов, в отличии от parse_args parse_early_param вызова в качестве указателя kernel_param является NULL, а сегмент .__ паров.

Назад выше функция parse_args, Params параметр .__ начального адреса сегмента пар, Num этого числа kernel_param.

Min_level, max_level 都 为 -1.unknown = unknown_bootoption

Parse_args еще до того, как, проходя командная_строка, раскол и получить Param VAL каждый параметр, вызовите parse_one для каждого параметра.

Оглядываясь на функции Parse_one Источник:

(1) parse_one первая секция проходит через все .__ пары kernel_param, переменная kernel_param_ops контраст имя паров и параметры их, метод с тем же именем устанавливаются члены kernel_param вызовов, чтобы установить значение параметра.

Драйвер Lenovo переднего говорит пример специального параметра use_acm, параметры командная_строка имеют g_serial.use_acm = 0, то совпадение обхода parse_one __param_use_acm зарегистрирован в serial.c, вызовите функцию param_ops_bool установить, установив тем самым use_acm = 0.

(2) Если ядро ​​parse_args передать parse_one общих параметров, таких как консоли = ttyS0,115200. Parse_one передней траверсы .__ сегмента паров не найти соответствующий kernel_param. Я вернулся, чтобы позвонить handle_unknown. Parse_args приходит unknown_bootoption, код выглядит следующим образом: 

/*
 * Unknown boot options get handed to init, unless they look like
 * unused parameters (modprobe will find them in /proc/cmdline).
 */
static int __init unknown_bootoption(char *param, char *val)
{
    repair_env_string(param, val);
 
    /* Handle obsolete-style parameters */
    if (obsolete_checksetup(param))
        return 0;
 
    /* Unused module parameter. */
    if (strchr(param, '.') && (!val || strchr(param, '.') < val))
        return 0;
 
    if (panic_later)
        return 0;
 
    if (val) {
        /* Environment option */
        unsigned int i;
        for (i = 0; envp_init[i]; i++) {
            if (i == MAX_INIT_ENVS) {
                panic_later = "Too many boot env vars at `%s'";
                panic_param = param;
            }
            if (!strncmp(param, envp_init[i], val - param))
                break;
        }
        envp_init[i] = param;
    } else {</span>
<span style="font-size:14px;">        /* Command line option */
        unsigned int i;
        for (i = 0; argv_init[i]; i++) {
            if (i == MAX_INIT_ARGS) {
                panic_later = "Too many boot init vars at `%s'";
                panic_param = param;
            }
        }
        argv_init[i] = param;
    }
    return 0;
}

Первый repair_env_string будет Param Val = собирает пары VAL форму.

Пересекая все сегменты Obsolete_checksetup -init_setup obs_kernel_param, если парамет-> ул парам спичкой, в param_ вызова> значения параметров конфигурации.

Одна вещи, чтобы отметить здесь repair_env_string будет повторно формировать себя в парах паров = форму валь. Пройденный после согласования имеет соответствие «параметры =» вместо «паров».

Общий параметр ядра до примеров анализа с учетом __ установки ( «консоль =», console_setup).

Консоль = ttyS0,115200, obsolete_checksetup соответствия передней консоли =, если матч пропускается консоль =, ttyS0,115200 приобрела значение, определенную функцию установки вызова для синтаксического анализа значений набора параметров.

Мыслимый, parse_one передается для каждого parse_args CmdLine .__ параметров и параметры -init.setup пересекают секцию, согласование с тем же именем или ул, затем вызвать или установить соответствующую функцию установки или установки значения параметра разрешение.

Start_kernel в Parse_args конце концов, ядро ​​CmdLine на разобранный!

 

Параметры Краткое описание ядра анализа:

(. 1) ядро ​​собрано и связано с использованием секции ядра .__ Param .init.setup желаемые параметры отображения (драйвер и общие) и соответствующую функцию обработки (obs_kernel_param kernel_param структуру), хранящуюся вместе.

(2) Ядро запуска, do_early_param параметры процесса ядра (например, earlyprintk earlycon) с использованием раннего

(3) parse_args для каждого параметра CmdLine траверса __param .init.setup согласования, согласование успешно, то соответствующий обработчик вызывается для разбора и установить значение параметров.

Также стоит учесть, для таких отображения таблицы обработчиков Есть много способов использования следующего ядра. До таких UBoot Bowen передать параметры ядру, обработчик ядра для различных тегов таким образом, также отображается.

Обратный вызов обработчик частная структура под драйвером ядра также эта мысль вау!

рекомендация

отwww.cnblogs.com/linhaostudy/p/11716931.html