Chinaunix首页 | 论坛 | 博客
  • 博客访问: 344922
  • 博文数量: 46
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 562
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-14 13:32
个人简介

先知者为师

文章分类

全部博文(46)

文章存档

2016年(1)

2015年(6)

2014年(20)

2013年(19)

我的朋友

分类: LINUX

2013-05-21 18:46:37

作者:曹忠明,讲师。   

在前面helloworld的编写里面,我们使用了两个宏分别是module_init和module_exit,这里分析下为什么使用这两个宏。

在写模块的时候有两个特殊的函数,分别是init_module和cleanup_module,这两个函数分别在insmod的时候和rmmod的时候调用,并且insmod和rmmod只识别这两个特殊的函数,可是我们前面的例子里面并没有这两个函数。怎么会这样呢,那就必须得说说module_init/module_exit了。

一个驱动可以作为一个模块动态的加载到内核里,也可以作为内核的一部分静态的编译进内核,module_init/module_exit也就有了两个含义:

一、动态编译成模块

在内核里有如下定义:
        /* Each module must use one module_init(). */
        #define module_init(initfn) \
                static inline initcall_t __inittest(void) \
                { return initfn; } \
                int init_module(void) __attribute__((alias(#initfn)));

/* This is only required if you want to be unloadable. */
        #define module_exit(exitfn) \
                static inline exitcall_t __exittest(void) \
                { return exitfn; } \
                 void cleanup_module(void) __attribute__((alias(#exitfn)));

首先我们可以发现发现module_init有两个含义:

1、验证加载函数的格式

static inline initcall_t __inittest(void) \

{ return initfn; }

这个函数的作用是验证我们传过来的加载函数格式是否正确,linux内核规定加载函数的的原型是:

typedef int (*initcall_t)(void);

所以我们写加载函数的时候必须是返回值为int参数为void的函数,这个在内核里要求比较严格,所以我们写加载函数的时候必须按照这个约定。

2、定义别名

int init_module(void) __attribute__((alias(#initfn)));

这段代码的作用是给我们的加载函数定义一个别名,别名就是我们前面提到的init_module,这样insmod就能够执行我们的加载函数了。

module_exit的作用和module_init一样,同样也是验证函数格式和定义别名。

二、静态编译

在静态编译的时候module_init的定义如下:
        #define module_init(x) __initcall(x);
        #define __initcall(fn) device_initcall(fn)
        #define device_initcall(fn) __define_initcall("6",fn,6)
        #define __define_initcall(level,fn,id) \
                static initcall_t __initcall_##fn##id __used \
                __attribute__((__section__(".initcall" level ".init"))) = fn

通过这些段代码,我们能够看出最终的结果是将我们的使用module_init修饰的函数指针链接到一个叫.initcall的段里,也就是说最终所以的使用module_init修饰的函数指针都被链接在这个段里,最终内核在启动的时候顺序调用所有链接在这个段里的函数,实现设备的初始化。

module_exit在静态编译的时候没有意义,因为静态编译的驱动无法卸载!

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