__define_initcall

来源:互联网 发布:hex 编辑器 c语言 编辑:程序博客网 时间:2024/05/02 01:31

在include/linux/init.h中定义initcall的优先级,例如优先级1有分成1和1s。
#define core_initcall(fn)        __define_initcall(fn, 1)
#define core_initcall_sync(fn)        __define_initcall(fn, 1s)

#define __define_initcall(fn, id) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall" #id ".init"))) = fn;
可见是这些initcall 是统一放在数据段的initcall中
在kernel中,会通过INIT_CALLS 将这些写incall放在__initcall_start 和 __initcall_end之间.
#define INIT_CALLS                            \
        VMLINUX_SYMBOL(__initcall_start) = .;            \
        KEEP(*(.initcallearly.init))                \
        INIT_CALLS_LEVEL(0)                    \
        INIT_CALLS_LEVEL(1)                    \
        INIT_CALLS_LEVEL(2)                    \
        INIT_CALLS_LEVEL(3)                    \
        INIT_CALLS_LEVEL(4)                    \
        INIT_CALLS_LEVEL(5)                    \
        INIT_CALLS_LEVEL(rootfs)                \
        INIT_CALLS_LEVEL(6)                    \
        INIT_CALLS_LEVEL(7)                    \
        VMLINUX_SYMBOL(__initcall_end) = .;

以#define INIT_CALLS_LEVEL(level)                        \
        VMLINUX_SYMBOL(__initcall##level##_start) = .;        \
        KEEP(*(.initcall##level##.init))            \
        KEEP(*(.initcall##level##s.init))            \
为例,可见优先级1先执行,然后才是1s.

在do_initcalls 中会循环调用个个level的回调函数
static void __init do_initcalls(void)
{
    int level;

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}
由于每个level中可能有多个回调函数,例如可能有多个程序call core_initcall(fn)。所以在do_initcall_level 中又会是一个循环
static void __init do_initcall_level(int level)
{
    initcall_t *fn;

    strcpy(initcall_command_line, saved_command_line);
    parse_args(initcall_level_names[level],
           initcall_command_line, __start___param,
           __stop___param - __start___param,
           level, level,
           NULL, &repair_env_string);

    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(*fn);
}
所以在do_one_initcall 中调用单个的回调函数
int __init_or_module do_one_initcall(initcall_t fn)
{
    int count = preempt_count();
    int ret;
    char msgbuf[64];

    if (initcall_blacklisted(fn))
        return -EPERM;

    if (initcall_debug)
        ret = do_one_initcall_debug(fn);
    else
        ret = fn();

    msgbuf[0] = 0;

    if (preempt_count() != count) {
        sprintf(msgbuf, "preemption imbalance ");
        preempt_count_set(count);
    }
    if (irqs_disabled()) {
        strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
        local_irq_enable();
    }
    WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);

    add_latent_entropy();
    return ret;
}
一般如果要debug 这些initcall的话,可以把initcall_debug 变量置为1,这样在do_one_initcall中就会call do_one_initcall_debug。在执行这个函数前后都会打印log
并且会打印这个函数所花的时间.
static int __init_or_module do_one_initcall_debug(initcall_t fn)
{
    ktime_t calltime, delta, rettime;
    unsigned long long duration;
    int ret;

    printk(KERN_DEBUG "calling  %pF @ %i\n", fn, task_pid_nr(current));
    calltime = ktime_get();
    ret = fn();
    rettime = ktime_get();
    delta = ktime_sub(rettime, calltime);
    duration = (unsigned long long) ktime_to_ns(delta) >> 10;
    printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n",
         fn, ret, duration);

    return ret;
}
打印的log 会如下:
printk(KERN_DEBUG "calling  %pF @ %i\n", fn, task_pid_nr(current));
printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n",
         fn, ret, duration);

由于initcall_debug 是按如下方式定义的。

core_param(initcall_debug, initcall_debug, bool, 0644);

因此可以通过下面的方式改变其值

[root@CentOS etc]# cat /sys/module/kernel/parameters/initcall_debug
N
echo Y > /sys/module/kernel/parameters/initcall_debug

0 0
原创粉丝点击