Chinaunix首页 | 论坛 | 博客
  • 博客访问: 637548
  • 博文数量: 237
  • 博客积分: 4285
  • 博客等级: 上校
  • 技术积分: 2701
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-15 14:05
文章分类

全部博文(237)

文章存档

2014年(2)

2013年(3)

2012年(47)

2011年(15)

2010年(68)

2009年(102)

我的朋友

分类: 嵌入式

2009-11-15 15:37:50

结构体struct module内核的其中之一的模块,多个模块形成的链表是模块的集合。通过insmod(实际执行module_init系统调用)把本模块插入集合,相当于插入链表当中。
 

如上所述会逐步替换,最终会替换成如下:(所以说以上的宏使用都是等价的,都可以使用)
至于如下的这个是什么意思? 我还没弄明白。
      static initcall_t __initcall_##fn##id __used \
      __attribute__((__section__(".initcall" level ".init"))) = fn
 
其中level参数即0~7(实际是0~7s)共14个级别,
 
#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)
 
这样内核在编译时会生产14个名为”.initcall.(0~7s).init”的section,例如.initcall0.init、.initcall2s.init等。被不同宏修饰的函数被放到对应的section中,例如上例的fs_initcall(acpi_event_init)最终会被放到.initcall5.init。
 
 

    我们向内核注册一个模块,一般会使用命令insmod,该命令实际上调用了系统调用module_init(),在该系统调用函数中,首先调用module_init(),参数传递过来的初始化函数.
如:
static int __init init_function(){…..}
module_init(init_function);
在模块自动加载或手动注册的时候,会自动调用init_function函数,切记init_function的函数原型(static int __init init_function(){…..}),一定不可以修改,必须这么写,这是内核的规定。
 
 __init, __initdata等属性标志, 是要把这种属性的代码放入目标文件的.init.text节, 数据放入.init.data节──这一过程是通过编译内核时为相关目标平台提供了xxx.lds链接脚本, 来指导ld完成的。 对模s3c2410来说, 可以参考arch/s3c2410/kernel/vmlinux.lds.S文件。
对编译成module的代码和数据来说, 当模块加载时, __init属性的函数就被执行;
对静态编入内核的代码和数据来说, 当内核引导时, do_basic_setup()函数调用do_initcalls()函数, 后者负责所有.init节函数的执行。
和module_init(init_function); 对应的函数是module_exit(init_function);
如:
static void __exit exit_function(){…..}
module_exit(exit_function);
 
__exit修饰词标记函数只在模块卸载时使用。
  如果模块被直接编进内核则该函数就不会被调用。如果内核编译时没有包含该模块,则此标记的函数将被简单地丢弃。
 
下面是一个简单的例子
include
#include
//“Dual BSD/GPL”或者 “Dual MPL/GPL”或者 “GPL ,GPL v2,”其中之一
MODULE_LICENSE(*****);
static int __init init_function(void)
{
 //加载模块时调用
    return 0;
}
static void  __exit exit_function(void)
//模块卸载时调用
}
//切记一定要记得写下面2个方法。最好要在函数的最后写,避免不必要的函数声明
module_init(init_function);
module_exit(exit_function);
 
 
 
 
 
 
 
 
 
链接脚本的如下代码:
  .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
      __initcall_start = .;
       INITCALLS
      __initcall_end = .;
  }
 
生成了initcall表,起始地址和结束地址存在__initcall_start和__initcall_end中。很多资料说根据level参数的值不同,内核在不同阶段调用这些函数。但从代码来看,我认为并非如此,内核只分两个阶段调用,即early initcall阶段和剩余阶段(level = 0~7s)。感兴趣的朋友可以看看上面INITCALLS宏的展开以及do_initcalls()、do_pre_smp_initcalls()两个函数,很容易就能明白。
在构建data segment的最后部分,我们看到如下代码:
  .bss : AT(ADDR(.bss) - LOAD_OFFSET) {
       __init_end = .;
       __bss_start = .;              /* BSS */
       *(.bss.page_aligned)
       *(.bss)
       . = ALIGN(4);
       __bss_stop = .;
      _end = . ;
       /* This is where the kernel creates the early boot page tables */
       . = ALIGN(PAGE_SIZE);
       pg0 = . ;
  }
它告诉我们.bss section位于data segment的最后,变量pg0存放的是“Provisional kernel Page Tables”的地址,
 模块便与一个struct module结构体相关联,并成为内核的一部分。
下面是结构体struct module的完整定义,接下来会逐个解释:
    struct module
    {
        enum module_state state;
        struct list_head list;
        char name[MODULE_NAME_LEN];
        struct module_kobject mkobj;
        struct module_param_attrs *param_attrs;
        const char *version;
        const char *srcversion;
        const struct kernel_symbol *syms;
        unsigned int num_syms;
        const unsigned long *crcs;
        const struct kernel_symbol *gpl_syms;
        unsigned int num_gpl_syms;
        const unsigned long *gpl_crcs;
        unsigned int num_exentries;
        const struct exception_table_entry *extable;
        int (*init)(void);
        void *module_init;
        void *module_core;
        unsigned long init_size, core_size;
        unsigned long init_text_size, core_text_size;
        struct mod_arch_specific arch;
        int unsafe;
        int license_gplok;
#ifdef CONFIG_MODULE_UNLOAD
        struct module_ref ref[NR_CPUS];
        struct list_head modules_which_use_me;
        struct task_struct *waiter;
        void (*exit)(void);
#endif
#ifdef CONFIG_KALLSYMS
        Elf_Sym *symtab;
        unsigned long num_symtab;
        char *strtab;
        struct module_sect_attrs *sect_attrs;
#endif
        void *percpu;
        char *args;
    };
 

本文来自CSDN博客,转载请标明出处:学习笔记/ARM汇编及相关/驱动开发中__init,__exit等作用.mht
阅读(1724) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~