不知不覺,其實也已經做bsp快三年了. 有些知識點,開始大概知道一點,但是還是比較抽象的. 經過這麼長時間的使用,還是覺得有必要好好整理一下的.比如module_init.
下面的code是在Android p,kernel/msm-4.14.
首先是module_init(x)函數.在kernel/msm-4.14/include/linux/module.h中:
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module. If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);
註釋中說,如果是常駐的driver,那麼會在do_initcalls的時候調到module_init添加的函數.那麼先看看do_initcalls怎麼調過來的吧. 在kernel/msm-4.14/init/main.c中有do_initcalls(). 實際上前面還有這樣的調用過程:start_kernel->rest_init->kernel_init->kernel_init_freeable->do_basic_setup->do_initcalls.
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
好,這裏是遍歷所有level,從0開始.往後看do_initcall_level().還是在main.c中:
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = {
"early",
"core",
"postcore",
"arch",
"subsys",
"fs",
"device",
"late",
};
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(*fn),還在main.c中.
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;
ret = do_one_initcall_debug(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;
}
在函數do_one_initcall_debug中,終於看到有fn()的運行.
static int __init_or_module do_one_initcall_debug(initcall_t fn)
{
ktime_t calltime, delta, rettime;
unsigned long long duration;
int ret;
if (initcall_debug)
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;
if (initcall_debug)
printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n",
fn, ret, duration);
if (initcall_debug == 0) {
if (duration > 100000)
printk(KERN_WARNING "[debuginit] initcall %pF returned %d after %lld usecs\n", fn,
ret, duration);
}
return ret;
}
那麼fn到底是什麼呢?
從for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)中可以看到fn實際上就是數組initcall_levels[]的一個元素.也就是說,如果level爲6,那麼fn=__initcall6_start,並且fn的類型是initcall_t.
大致理解爲__initcall6_start是函數的入口(地址),fn++就是到下一個同級別的函數入口,一直到下一個level的函數.那麼這些函數又是怎麼放過來的呢?
前面後說到,#define module_init(x) __initcall(x);那麼看看__initcall(x). 還是在init.h中有:
#define __initcall(fn) device_initcall(fn)
再看看device_initcall(fn),還是在init.h中:
#define device_initcall(fn) __define_initcall(fn, 6)
這裏的6代表module_init的level是6,好像地位不是很高呀. 繼續看__define_initcall(fn, 6),還在init.h中:
#ifdef CONFIG_LTO_CLANG
/* prepend the variable name with __COUNTER__ to ensure correct ordering */
#define ___initcall_name2(c, fn, id) __initcall_##c##_##fn##id
#define ___initcall_name1(c, fn, id) ___initcall_name2(c, fn, id)
#define __initcall_name(fn, id) ___initcall_name1(__COUNTER__, fn, id)
#else
#define __initcall_name(fn, id) __initcall_##fn##id
#endif
/*
* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*
* Initcalls are run by placing pointers in initcall sections that the
* kernel iterates at runtime. The linker can do dead code / data elimination
* and remove that completely, so the initcall sections have to be marked
* as KEEP() in the linker script.
*/
#define __define_initcall(fn, id) \
static initcall_t __initcall_name(fn, id) __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
上面這一段內容和下面的msm-4.14/include/asm-generic/vmlinux.lds.h文件中的INIT_CALLS_LEVEL(level)裏面的內容相符的.並且如同註釋中說到的使用了KEEP(). 也可以簡單理解爲通過module_init(x)最終是把我的init函數x添加到__initcall6_start那一塊去的.
#define INIT_CALLS_LEVEL(level) \
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
KEEP(*(.initcall##level##.init)) \
KEEP(*(.initcall##level##s.init)) \
#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) = .;
到這裏爲止,也就基本把module_init的調用分析完了. 邁小步,不停步.
參考博文:https://blog.csdn.net/richard_liujh/article/details/45669207
https://blog.csdn.net/richard_liujh/article/details/46758073
非常感謝! 本文爲學習總結用.