linux子系统的初始化_subsys_initcall():那些入口函数

来源:互联网 发布:淘宝关键字搜索量 编辑:程序博客网 时间:2024/05/21 11:04

内核选项的解析完成之后,各个子系统的初始化即进入第二部分—入口函数

的调用。通常USB、PCI这样的子系统都会有一个名为subsys_initcall

的入口,如果你选择它们作为研究内核的切入点,那么就请首先找到它。

朱德庸在《关于上班这件事》里说,要花前半生找入口,花后半生找出口。

可见寻找入口对于咱们这一生,对于看内核代码这件事儿都是无比重要的。

但是很多时候,入口并不仅仅只有subsys_initcall一个,比如PCI。


以下代码来自 linux内核源码中 include/linux/init.h 文件 

#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节里。

.initcall.init节

内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,

如文本、数据、init数据、bass等等。这些对象文件都是由一个称为链接器

脚本的文件链接并装入的。这个链接器脚本的功能是将输入对象文件的各节映射

到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,

将该可执行文件的各节装入到指定地址处。 vmlinux.lds是存在于

arch/<target>/目录中的内核链接器脚本,它负责链接内核的各个节并将

它们装入内存中特定偏移量处。在vmlinux.lds文件里查找initcall.init

就可以看到下面的内容

__inicall_start = .;.initcall.init : AT(ADDR(.initcall.init) – 0xC0000000) {*(.initcall1.init)*(.initcall2.init)*(.initcall3.init)*(.initcall4.init)*(.initcall5.init)*(.initcall6.init)*(.initcall7.init)}__initcall_end = .;

这就告诉我们.initcall.init节又分成了7个子节,而xxx_initcall入口函数指针

具体放在哪一个子节里边儿是由xxx_initcall的定义中,__define_initcall宏的

参数决定的,比如core_initcall将函数指针放在.initcall1.init子节,

device_initcall将函数指针放在了.initcall6.init子节等等。各个子节的

顺序是确定的,即先调用.initcall1.init中的函数指针再调用.initcall2.init

中的函数指针,等等。不同的入口函数被放在不同的子节中,因此也就决定了它们的调用顺序。

注意:设备驱动程序中常见的module_init(x)函数,查看init.h文件发现,

#define module_init(x)__initcall(x);

#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn) __define_initcall("6",fn,6)

这样推断 module_init 调用优先级为6低于subsys_initcall调用优先级4.


do_initcalls()函数

那些入口函数的调用由do_initcalls函数来完成。

do_initcall函数通过for循环,由__initcall_start开始,直到

__initcall_end结束,依次调用识别到的初始化函数。而位于__initcall_start

和__initcall_end之间的区域组成了.initcall.init节,其中保存了由

xxx_initcall形式的宏标记的函数地址,do_initcall函数可以很轻松的取得函数

地址并执行其指向的函数。

.initcall.init节所保存的函数地址有一定的优先级,越前面的函数优先级越高,

也会比位于后面的函数先被调用。

由do_initcalls函数调用的函数不应该改变其优先级状态和禁止中断。因此,每个

函数执行后,do_initcalls会检查该函数是否做了任何变化,如果有必要,它会校

正优先级和中断状态。

另外,这些被执行的函数有可以完成一些需要异步执行的任务,

flush_scheduled_work函数则用于确保do_initcalls函数在返回前等待

这些异步任务结束。

666 static void __init do_initcalls(void)667 {668 initcall_t *call;669 int count = preempt_count();670 671 for (call = __initcall_start; call < __initcall_end; call++) {672 ktime_t t0, t1, delta;673 char *msg = NULL;674 char msgbuf[40];675 int result;676 677 if (initcall_debug) {678 printk("Calling initcall 0x%p", *call);679 print_fn_descriptor_symbol(": %s()",680 (unsigned long) *call);681 printk("/n");682 t0 = ktime_get();683 }684 685 result = (*call)();686 687 if (initcall_debug) {688 t1 = ktime_get();689 delta = ktime_sub(t1, t0);690 691 printk("initcall 0x%p", *call);692 print_fn_descriptor_symbol(": %s()",693 (unsigned long) *call);694 printk(" returned %d./n", result);695 696 printk("initcall 0x%p ran for %Ld msecs: ",697 *call, (unsigned long long)delta.tv64 >> 20);698 print_fn_descriptor_symbol("%s()/n",699 (unsigned long) *call);700 }701 702 if (result && result != -ENODEV && initcall_debug) {703 sprintf(msgbuf, "error code %d", result);704 msg = msgbuf;705 }706 if (preempt_count() != count) {707 msg = "preemption imbalance";708 preempt_count() = count;709 }710 if (irqs_disabled()) {711 msg = "disabled interrupts";712 local_irq_enable();713 }714 if (msg) {715 printk(KERN_WARNING "initcall at 0x%p", *call);716 print_fn_descriptor_symbol(": %s()",717 (unsigned long) *call);718 printk(": returned with %s/n", msg);719 }720 }721 722 /* Make sure there is no pending stuff from the initcall sequence */723 flush_scheduled_work();724 }

本文来自:http://blog.csdn.net/yimiyangguang1314/article/details/7312209



原创粉丝点击