打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
module_init, fs_initcall 精华

module_init, fs_initcall

1424人阅读 评论(0) 收藏 举报

ipv4内核初始化相关

已有 6249 次阅读 2009-11-4 17:55

所在文件:
net/ipv4/af_inet.c
初始化函数定义:

  1. static int __init inet_init(void)

初始化函数调用:

  1. fs_initcall(inet_init); //#define fs_initcall(fn)                 __define_initcall("5",fn,5)

这里的fs_initcall和module_init这样的函数是一样的功能,就是给系统内核添加一个功能函数。

这个宏的定义位于inlcludelinuxinit.h中:

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

其中 initcall_t 是个函数指针类型:typedef int (*initcall_t)(void);

而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。
以这个宏定义的的含义是:
1) 声明一个名称为__initcall_##fn##id的函数指针(其中##表示替换连接,);
2) 将这个函数指针初始化为fn;
3) 编译的时候需要把这个函数指针变量放置到名称为 “.initcall” level “.init”的section中(比如level=”1″,代表这个section的名称是 “.initcall1.init”)。

这些衍生宏宏的定义也位于 inlcludelinuxInit.h 中:

  1. #define pure_initcall(fn)               __define_initcall("0",fn,0)
  2. #define core_initcall(fn)               __define_initcall("1",fn,1)
  3. #define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
  4. #define postcore_initcall(fn)           __define_initcall("2",fn,2)
  5. #define postcore_initcall_sync(fn)      __define_initcall("2s",fn,2s)
  6. #define arch_initcall(fn)               __define_initcall("3",fn,3)
  7. #define arch_initcall_sync(fn)          __define_initcall("3s",fn,3s)
  8. #define subsys_initcall(fn)             __define_initcall("4",fn,4)
  9. #define subsys_initcall_sync(fn)        __define_initcall("4s",fn,4s)
  10. #define fs_initcall(fn)                 __define_initcall("5",fn,5)
  11. #define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
  12. #define rootfs_initcall(fn)             __define_initcall("rootfs",fn,rootfs)
  13. #define device_initcall(fn)             __define_initcall("6",fn,6)
  14. #define device_initcall_sync(fn)        __define_initcall("6s",fn,6s)
  15. #define late_initcall(fn)               __define_initcall("7",fn,7)
  16. #define late_initcall_sync(fn)          __define_initcall("7s",fn,7s)

因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为.initcall1.init的section中,而通过宏postcore_initcall() 来声明的函数指针,将放置到名称为.initcall2.init的section中,依次类推。
在:include/asm-generic/vmlinux.lds.h:

  1. #define INITCALLS                                                      
  2. *(.initcallearly.init)                                         
  3. VMLINUX_SYMBOL(__early_initcall_end) = .;    //注意这里的__early_initcall_end标志
  4. *(.initcall0.init)                                             
  5. *(.initcall0s.init)                                            
  6. *(.initcall1.init)                                             
  7. *(.initcall1s.init)                                            
  8. *(.initcall2.init)                                             
  9. *(.initcall2s.init)                                            
  10. *(.initcall3.init)                                             
  11. *(.initcall3s.init)                                            
  12. *(.initcall4.init)                                             
  13. *(.initcall4s.init)                                            
  14. *(.initcall5.init)                                             
  15. *(.initcall5s.init)                                            
  16. *(.initcallrootfs.init)                                        
  17. *(.initcall6.init)                                             
  18. *(.initcall6s.init)                                            
  19. *(.initcall7.init)                                             
  20. *(.initcall7s.init)
  21. #define INIT_CALLS                                                     
  22. VMLINUX_SYMBOL(__initcall_start) = .;                  
  23. INITCALLS                                              
  24. VMLINUX_SYMBOL(__initcall_end) = .;    //还有这里的__initcall_end

最终跟踪之后这个初始化的段会在arch/x86/kernel/vmlinux.lds.S这样的体系结构中内核二进制文件结构组织的配置文件中。
而在内核Makefile文件中有这样的编译语句:

  1. vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
  2. 。。。
  3. vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds
  4. 。。。

而在init/main.c 中:

  1. static void __init do_initcalls(void)
  2. {
  3. initcall_t *call;
  4.  
  5. for (call = __early_initcall_end; call < __initcall_end; call++)
  6. do_one_initcall(*call);
  7.  
  8. /* Make sure there is no pending stuff from the initcall sequence */
  9. flush_scheduled_work();
  10. }

该函数的调用关系如下:

  1. start_kernel-->rest_init->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
  2. |
  3. ->kernel_init(void * unused) ->do_initcalls(void)

也就是说对于所有的内核模块或是其它的以类似该形式加入到内核中的程序,都最终在内核所在的二进制文件中是有一个固定的段来存放的,而且内核在初始化的过程中也是找到这些段的地址让后做相应的加载和执行。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
__define_initcall
GCC特性之__init修饰解析
分析kernel的initcall函数 - Linux Kernel
linux kernel initcall 的顺序
linux driver 注册设备 platform_driver_register()
ARMlinux启动分析
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服