linux子系統的初始化_subsys_initcall()

概述

       內核選項的解析完成以後,各個子系統的初始化即進入第二部分—入口函數的調用。一般USB、PCI這樣的子系統都會有一個名爲subsys_initcall的入口,若是你選擇它們做爲研究內核的切入點,那麼就請首先找到它。linux

section的聲明

    C 語言中attribute屬性的section是在目標文件連接時能夠用於主動定製代碼的位置,具體能夠WIKI,下面看linux kernel中是如何定義的。 
ide

      如下代碼來自 linux內核源碼中 include/linux/init.h 文件。下面使用相同語法規則的變量名存放了各個初始化函數的地址。函數

        更重要的是其section屬性也是按照必定規則構成的。
post

        關於section見 http://lihuize123123.blog.163.com/blog/static/878290522010420111428109/ui

/* 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.
 */

#define __define_initcall(level,fn,id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" level ".init"))) = fn

/*
 * Early initcalls run before initializing SMP.
 *
 * Only for built-in code, not modules.
 */
#define early_initcall(fn)		__define_initcall("early",fn,early)

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 */
#define pure_initcall(fn)		__define_initcall("0",fn,0)

#define core_initcall(fn)		__define_initcall("1",fn,1)
#define core_initcall_sync(fn)		__define_initcall("1s",fn,1s)
#define postcore_initcall(fn)		__define_initcall("2",fn,2)
#define postcore_initcall_sync(fn)	__define_initcall("2s",fn,2s)
#define arch_initcall(fn)		__define_initcall("3",fn,3)
#define arch_initcall_sync(fn)		__define_initcall("3s",fn,3s)
#define subsys_initcall(fn)		__define_initcall("4",fn,4)
#define subsys_initcall_sync(fn)	__define_initcall("4s",fn,4s)
#define fs_initcall(fn)			__define_initcall("5",fn,5)
#define fs_initcall_sync(fn)		__define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)		__define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)		__define_initcall("6",fn,6)
#define device_initcall_sync(fn)	__define_initcall("6s",fn,6s)
#define late_initcall(fn)		__define_initcall("7",fn,7)
#define late_initcall_sync(fn)		__define_initcall("7s",fn,7s)

#define __initcall(fn) device_initcall(fn)

#define __exitcall(fn) \
	static exitcall_t __exitcall_##fn __exit_call = fn

#define console_initcall(fn) \
	static initcall_t __initcall_##fn \
	__used __section(.con_initcall.init) = fn

#define security_initcall(fn) \
	static initcall_t __initcall_##fn \
	__used __section(.security_initcall.init) = fn

註冊

        這些入口有個共同的特徵,它們都是使用__define_initcall宏定義的。它們的調用也不是隨便的,而是按照必定順序的,這個順序就取決於__define_initcall宏。__define_initcall宏用來將指定的函數指針放到.initcall.init節裏。spa

.initcall.init節debug

        內核可執行文件由許多連接在一塊兒的對象文件組成。對象文件有許多節,如文本、數據、init數據、bass等等。這些對象文件都是由一個稱爲連接器腳本的文件連接並裝入的。這個連接器腳本的功能是將輸入對象文件的各節映射到輸出文件中;換句話說,它將全部輸入對象文件都連接到單一的可執行文件中,將該可執行文件的各節裝入到指定地址處。 vmlinux.lds是存在於arch/<target>/目錄中的內核連接器腳本,它負責連接內核的各個節並將它們裝入內存中特定偏移量處。在vmlinux.lds文件裏查找initcall.init就能夠看到下面的內容指針

#define INITCALLS							\
	*(.initcallearly.init)						\
	VMLINUX_SYMBOL(__early_initcall_end) = .;			\
  	*(.initcall0.init)						\
  	*(.initcall0s.init)						\
  	*(.initcall1.init)						\
  	*(.initcall1s.init)						\
  	*(.initcall2.init)						\
  	*(.initcall2s.init)						\
  	*(.initcall3.init)						\
  	*(.initcall3s.init)						\
  	*(.initcall4.init)						\
  	*(.initcall4s.init)						\
  	*(.initcall5.init)						\
  	*(.initcall5s.init)						\
	*(.initcallrootfs.init)						\
  	*(.initcall6.init)						\
  	*(.initcall6s.init)						\
  	*(.initcall7.init)						\
  	*(.initcall7s.init)

這就告訴咱們.initcall.init節又分紅了7個子節,而xxx_initcall入口函數指針具體放在哪個子節裏邊兒是由xxx_initcall的定義中,__define_initcall宏的參數決定的,好比core_initcall將函數指針放在.initcall1.init子節,device_initcall將函數指針放在了.initcall6.init子節等等。各個子節的順序是肯定的,即先調用.initcall1.init中的函數指針再調用.initcall2.init中的函數指針,等等。不一樣的入口函數被放在不一樣的子節中,所以也就決定了它們的調用順序。code

注意:設備驅動程序中常見的module_init(x)函數,查看init.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);

#define __initcall(fn) device_initcall(fn)

/* Don't use these in modules, but some people do... */
#define early_initcall(fn)		module_init(fn)
#define core_initcall(fn)		module_init(fn)
#define postcore_initcall(fn)		module_init(fn)
#define arch_initcall(fn)		module_init(fn)
#define subsys_initcall(fn)		module_init(fn)
#define fs_initcall(fn)			module_init(fn)
#define device_initcall(fn)		module_init(fn)
#define late_initcall(fn)		module_init(fn)

#define __define_initcall(level,fn) \
	static initcall_t __initcall_##fn __used \
	__attribute__((__section__(".initcall" level ".init"))) = fn

/* Userspace initcalls shouldn't depend on anything in the kernel, so we'll
 * make them run first.
 */
#define __initcall(fn) __define_initcall("1", fn)

#define __exitcall(fn) static exitcall_t __exitcall_##fn __exit_call = fn

#define __init_call	__used __section(.initcall.init)

這樣推斷 module_init 調用優先級爲6低於subsys_initcall調用優先級4

調用

static void __init do_initcalls(void)
{
	initcall_t *fn;

	for (fn = __early_initcall_end; fn < __initcall_end; fn++)
		do_one_initcall(*fn);

	/* Make sure there is no pending stuff from the initcall sequence */
	flush_scheduled_work();
}

int __init_or_module do_one_initcall(initcall_t fn)
{
	int count = preempt_count();
	int ret;

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

	msgbuf[0] = 0;

	if (ret && ret != -ENODEV && initcall_debug)
		sprintf(msgbuf, "error code %d ", ret);

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

	return ret;
}



IN BUILDING

相關文章
相關標籤/搜索