必威体育Betway必威体育官网
当前位置:首页 > IT技术

module_init的调用过程

时间:2019-09-09 10:42:12来源:IT技术作者:seo实验室小编阅读:67次「手机版」
 

module_init

不知不觉,其实也已经做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

非常感谢! 本文为学习总结用. 

相关阅读

java调用webservice接口 三种方法

摘自其它:webservice的 发布一般都是使用WSDL(web service descriptive language)文件的样式来发布的,在WSDL文件里面,包含这个webser

module_init机制的理解

https://blog.csdn.net/weixin_37571125/article/details/78665184  我们在学习Linux驱动开发时,首先需要了解Linux的模块化机制(m

PHP中调用filesize后,无法取得文件的真实大小

在处理中,需要每隔一段时间看一下文件的大小。使用filesize后,最初取得的大小是正确的,后来就变的不正确了。一直是一个固定的值。调

C#调用WebKit内核

系统要求 Windows与.NET框架 由于WebKit库和.NET框架的要求,WebKit .NET只能在Windows系统上运行。从版本0.4开始,最低要求包

关于SurfaceHolder.addCallback方法无法调用surfaceCr

我遇到这个问题的场景是:页面一个大的SurfaceView .想要通过相机实时预览捕获到的场景。页面进入的时候动态获取相机权限,如果有权

分享到:

栏目导航

推荐阅读

热门阅读