Chinaunix首页 | 论坛 | 博客
  • 博客访问: 878197
  • 博文数量: 116
  • 博客积分: 3923
  • 博客等级: 中校
  • 技术积分: 1337
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-23 01:22
文章分类

全部博文(116)

文章存档

2013年(1)

2012年(17)

2011年(69)

2009年(29)

分类: LINUX

2011-12-18 18:20:48

【转】http://blog.chinaunix.net/link.php?url=http://blog.csdn.net%2Fzhshao09%2Farchive%2F2009%2F05%2F03%2F4145137.aspx

题目:系统初始化函数集(subsys_initcall)和初始化段应用

前言:前段时间做一个项目需要设计一个动态库,并希望在加载库的同时自动执行一些初始化动作,于是联想到了linux内核众子系统的初始化,于是研究之,并在过这 程中发现了初始化段的存在,利用初始化段实现了该功能。工作一年,笔记积累多了,慢慢变得杂乱无章,于是开博,一方面整理笔记,梳理知识,另一方面和大家 交流,共同进步。

1 系统初始化调用函数集分析(静态) 1.1 函数定义

 在linux内核代码里,运用了subsys_initcall来进行各种子系统的初始化,具体怎么初始化的呢?其实并不复杂。以2.6.29内核作为例子。在下就能找到subsys_initcall的定义:

#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又被定义为
#define __define_initcall(level,fn,id) /
 static initcall_t  __initcall_##fn##id   __used /
 __attribute__((__section__(".initcall" level ".init"))) = fn

所以 subsys_initcall(fn) == __initcall_fn4 它将被链接器放于section  .initcall4.init 中。(attribute将会在另一篇文章中介绍)

1.2 初始化函数集的调用过程 执行过程:
start_kernel->rest_init
系统在启动后在rest_init中会创建init内核线程
init->
do_basic_setup->do_initcalls
do_initcalls中会把.initcall.init.中的函数依次执行一遍:

for (call = __initcall_start; call < __initcall_end; call++) {
.    .....
result = (*call)();
.    ........
}

这个__initcall_start是在文件定义的:
 .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
   __initcall_start = .;
   INITCALLS
   __initcall_end = .;
  }

INITCALLS被定义于:
#define INITCALLS       /
   *(.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)

2 基于模块方式的初始化函数(动态) 2.1函数定义

subsys_initcall的静态调用方式应该讲清楚来龙去脉了,现在看看动态方式的初始化函数调用(模块方式)。在里,如果MODULE宏被定义,说明该函数将被编译进模块里,在系统启动后以模块方式被调用。

#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)

这是在定义MODULE的情况下对subsys_initcall的定义,就是说对于驱动模块,使用subsys_initcall等价于使用module_init

2.2 module_init 分析

下面先看看module_init宏究竟做了什么
#define module_init(initfn)     /
 static inline initcall_t __inittest(void)  / 
/*定义此函数用来检测传入函数的类型,并在编译时提供警告信息*/
 { return initfn; }     /
 int init_module(void) __attribute__((alias(#initfn))); /*声明init_modlue为 initfn的别名,insmod只查找名字为init_module函数并调用*/

typedef int (*initcall_t)(void); /*函数类型定义*/

在以模块方式编译一个模块的时候,会自动生成一个xxx.mod.c文件, 在该文件里面定义一个struct module变量,并把init函数设置为上面的init_module() 而上面的这个init_module,被alias成模块的初始化函数(参考

也就是说,模块装载的时候(insmod,modprobe),sys_init_module()系统调用会调用module_init指定的函数(对于编译成>模块的情况)。

2.3 module的自动加载

内核在启动时已经检测到了系统的硬件设备,并把硬件设备信息通过sysfs内核虚拟文件系统导出。sysfs文件系统由系统初始化脚本挂载到/sys上。udev扫描sysfs文件系统,根据硬件设备信息生成热插拔(hotplug)事件,udev再读取这些事件,生成对应的硬件设备文件。由于没有实际的硬件插拔动作,所以这一过程被称为coldplug

udev完成coldplug操作,需要下面三个程序:
udevtrigger——扫描sysfs文件系统,生成相应的硬件设备hotplug事件。

udevd——作为deamon,记录hotplug事件,然后排队后再发送给udev,避免事件冲突(race conditions)。
udevsettle——查看udev事件队列,等队列内事件全部处理完毕才退出。

要规定事件怎样处理就要编写规则文件了.规则文件是udev的灵魂,没有规则文件,udev无法自动加载硬件设备的驱动模块。它一般位于

3初始化段的应用

这里给出一个简单的初始化段的使用例子,将a.c编译成一个动态库,其中,函数a()和函数c()放在两个不同的初始化段里,函数b()默认放置;编译main.c,链接到由a.c编译成的动态库,观察各函数的执行顺序。

file: a.c

  1. #include <stdio.h>
  2. typedef int (*fn) (void);
  3. int a(void)
  4. {
  5.     printf("a/n");
  6.     return 0;
  7. }
  8. __attribute__((__section__(".init_array.2"))) static fn init_a = a;
  9. int c(void)
  10. {
  11.     printf("c/n");
  12.     return 0;
  13. }
  14. __attribute__((__section__(".init_array.1"))) static fn init_c = c;

  15. int b()
  16. {
  17.     printf("b/n");
  18.     return 0;
  19. }

file: main.c

  1. #include<stdio.h>
  2. int b();
  3. int main()
  4. {
  5.     printf("main/n");
  6.     b();
  7. }


file: mk.sh

  1. gcc -fPIC -g -c a.c
  2. gcc -shared -g -o liba.so a.o
  3. cp liba.so /lib/ -fr
  4. gcc main.c liba.so
  5. ldconfig
  6. ./a.out


output

  1. # gcc -fPIC -g -c a.c
  2. # gcc -shared -g -o liba.so a.o
  3. # cp liba.so /lib/
  4. # gcc main.c liba.so
  5. # ldconfig
  6. # ./a.out
  7. a
  8. c
  9. main
  10. b




阅读(1255) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~