Chinaunix首页 | 论坛 | 博客
  • 博客访问: 373580
  • 博文数量: 53
  • 博客积分: 1411
  • 博客等级: 上尉
  • 技术积分: 701
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-04 14:40
文章分类

全部博文(53)

文章存档

2011年(6)

2010年(20)

2009年(18)

2008年(9)

我的朋友

分类: LINUX

2011-09-02 18:37:10

Linux驱动程序中,经常可以看到这样的代码:subsys_initcall(start_xxx); start_xxx是驱动模块中实现的一个初始化函数。这种写法有什么用途呢?函数又是如何调用的呢?这就是我们要讨论的initcall相关的内容。

我们先来看看subsys_initcall这个定义的出处。这是一个宏定义,下就能找到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

这个宏定义是什么意思呢?定义中,initcall_t 是个函数指针类型,描述如下

typedef int (*initcall_t)(void); 而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。

所以__define_initcall这个宏定义的的含义是:

1) 声明一个名称为__initcall_##fn的函数指针(其中##表示替换连接)

2) 将这个函数指针初始化为fn

3) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"section(e.g. level="1",那么section的名称是".initcall1.init")

比如,通过宏 core_initcall() 来声明的函数指针,将放置到名称为.initcall1.initsection中,而通过宏 postcore_initcall() 来声明的函数指针,将放置到名称为.initcall2.initsection中,那么我们之前的语句:subsys_initcall(start_xxx); start_xxx的意义就是

定义了initcall_t 类型的函数指针 __initcall_start_xxx_5s 并赋值为 start_xxx,并将这个函数指针放在命名为".initcall4s.init"section中。

那么,注册好的函数是什么时候被调用到的呢?Linux系统启动后,初始化函数集的调用过程执行过程:init/main.c中函数start_kernel()开始,

start_kernel()->rest_init()

rest_init中会创建init内核线程kernel_init

kernel_init->do_basic_setup->do_initcalls

do_initcalls中会把.initcall.init.中的函数依次执行一遍:

static void __init do_initcalls(void)

{

    initcall_t *call;

    for (call = __early_initcall_end; call < __initcall_end; call++)

        do_one_initcall(*call);

    /* Make sure there is no pending stuff from the initcall sequence */

    flush_scheduled_work();

}

__early_initcall_end__initcall_end的定义在文件中:

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

                                                                                       

#define INIT_CALLS                          \                                          

        VMLINUX_SYMBOL(__initcall_start) = .;           \                              

        INITCALLS                       \                                              

        VMLINUX_SYMBOL(__initcall_end) = .;                   

这样,系统启动的时候,就会对相应的驱动模块,进行初始化。

另外需要注意的是,若驱动选择为内核模块的形式,那么subsys_initcall宏定义如下:

#ifndef MODULE

... ...

#else /* MODULE */

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

#endif

在定义MODULE的情况下对subsys_initcall的定义,等价于使用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成模块的初始化函数(参考关键字:__attribute__, alias, visibility, hidden>)。 

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

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

udev完成coldplug操作,需要下面三个程序:

udevtrigger——扫描sysfs文件系统,生成相应的硬件设备hotplug事件。 

udevd——作为deamon,记录hotplug事件,然后排队后再发送给udev,避免事件冲突(race conditions)。 

udevsettle——查看udev事件队列,等队列内事件全部处理完毕才退出。

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

若应用程序中,需要在一个so加载的时候,做一些事情,可以仿照上述kernel驱动模块initcall的做法。下面结合例子说明。

$cat mk.sh

gcc -fPIC -g -c liba.c

gcc -shared -g -o liba.so liba.o

gcc -o test main.c -L./ -la

export LD_LIBRARY_PATH+="$(pwd)"

./test

$cat liba.c

#include 

typedef int (*fn) (void);

int fn_a()

{

printf("fn_a() called\n");

return 0;

}

__attribute__((__section__(".init_array.2"))) static fn init_a = fn_a;

int fn_b()

{

printf("fn_b() called\n");

return 0;

int fn_c()

{

printf("fn_c() called\n");

return 0;

}

__attribute__((__section__(".init_array.1"))) static fn init_c = fn_c;

$cat main.c

#include

extern int fn_b();

int main()

{

    printf("main() called\n");

    fn_b();

    return 0;

}

结果如下:

fn_c() called

fn_a() called

main() called

fn_b() called


阅读(3928) | 评论(0) | 转发(3) |
0

上一篇:linux sed用法

下一篇:Linux shell脚本示例

给主人留下些什么吧!~~