來源: http://blog.csdn.net/eroswang/archive/2008/04/23/2317771.aspx

在kernel中有很多__init,這個東東到底是何方神聖捏?且聽小生我一一道來。
下面是其定義:
file:/include/linux/init.h
 43 #define __init      __attribute__ ((__section__ (".init.text"))) __cold
 44 #define __initdata  __attribute__ ((__section__ (".init.data")))
 45 #define __exitdata  __attribute__ ((__section__(".exit.data")))
 46 #define __exit_call __attribute_used__ __attribute__ ((__section__ (".exitcall.exit")))

也許你會問那 __attribute__ ((__section__ (".init.text"))) __cold是什麼東東阿?
且看 info gcc C Extensions Attribute Syntax
section ("SECTION-NAME")'
     Normally, the compiler places the objects it generates in sections
     like `data' and `bss'.  Sometimes, however, you need additional
     sections, or you need certain particular variables to appear in
     special sections, for example to map to special hardware.  The
     `section' attribute specifies that a variable (or function) lives
     in a particular section.  For example, this small program uses
     several specific section names:
          struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
          struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
          char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
          int init_data __attribute__ ((section ("INITDATA"))) = 0;

          main()
          {
            /* Initialize stack pointer */
            init_sp (stack + sizeof (stack));

            /* Initialize initialized data */
            memcpy (&init_data, &data, &edata - &data);

            /* Turn on the serial ports */
            init_duart (&a);
            init_duart (&b);
          }

     Use the `section' attribute with an _initialized_ definition of a
     _global_ variable, as shown in the example.  GCC issues a warning
     and otherwise ignores the `section' attribute in uninitialized
     variable declarations.

     You may only use the `section' attribute with a fully initialized
     global definition because of the way linkers work.  The linker
     requires each object be defined once, with the exception that
     uninitialized variables tentatively go in the `common' (or `bss')
     section and can be multiply "defined".  You can force a variable
     to be initialized with the `-fno-common' flag or the `nocommon'
     attribute.

     Some file formats do not support arbitrary sections so the
     `section' attribute is not available on all platforms.  If you
     need to map the entire contents of a module to a particular
     section, consider using the facilities of the linker instead.

簡單來說是指示gcc把標記的數據或者函數放到指定sector。
linux中把一些啟動及初始化時候用的數據用__init標識,然後在適當的時候把它們釋放,回收內存。
說到這個__init,就不能不說module_init,subsys_initcall。
在init.h中我們能夠找到 #define subsys_initcall(fn)     __define_initcall("4",fn,4)
又是一個宏定義,簡直是無極中的圓環套圓環之城阿。
file:/include/linux/init.h
100 /* initcalls are now grouped by functionality into separate 
101  * subsections. Ordering inside the subsections is determined
102  * by link order. 
103  * For backwards compatibility, initcall() puts the call in 
104  * the device init subsection.
105  *
106  * The `id' arg to __define_initcall() is needed so that multiple initcalls
107  * can point at the same handler without causing duplicate-symbol build errors.
108  */
109 

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


subsys_initcall(usb_init)轉換後就變成了 static initcall_t  __initcall_usbinit4   __attribute_used__ \
__attribute__((__section__(".initcall 4.init"))) = usb_init
就是把usb_init的函數入口指針存放在.initcall4.init中。
file:/include/asm-generic/vmlinux.lds.h
239 #define INITCALLS                           \
240     *(.initcall0.init)                      \
241     *(.initcall0s.init)                     \
242     *(.initcall1.init)                      \
243     *(.initcall1s.init)                     \
244     *(.initcall2.init)                      \
245     *(.initcall2s.init)                     \
246     *(.initcall3.init)                      \
247     *(.initcall3s.init)                     \
248     *(.initcall4.init)                      \
249     *(.initcall4s.init)                     \
250     *(.initcall5.init)                      \
251     *(.initcall5s.init)                     \
252     *(.initcallrootfs.init)                     \
253     *(.initcall6.init)                      \
254     *(.initcall6s.init)                     \
255     *(.initcall7.init)                      \
256     *(.initcall7s.init)


file:/arch/kernel/vmlinux_32.lds.S
144   .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
145     __initcall_start = .;
146     INITCALLS
147     __initcall_end = .;
148   }

展開
   .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
     __initcall_start = .;
     *(.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_end = .;
   }


那麼系統是如何執行這些函數呢?
此話就長了阿~ 話說盤古開天芙蓉姐姐補天后我們來到了main.c這個linux中舉足輕重的文件
進入start_kernel
start_kernel  -->rest_init() -->kernel_init()  --> do_basic_setup()  -->do_initcalls()

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

huenlil 發表在 痞客邦 PIXNET 留言(0) 人氣()