Chinaunix首页 | 论坛 | 博客
  • 博客访问: 13774
  • 博文数量: 2
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2016-03-13 09:56
文章分类
文章存档

2016年(2)

我的朋友
最近访客

分类: LINUX

2016-06-23 10:13:48

一、linux驱动编程或者内核编程都需要包含下面这三个最基本的头文件:
    1. #include               //init.h 定义了驱动的初始化和退出相关的函数。
    2. #include         //module.h 定义了内核模块相关的函数、变量及宏。
    3. #include          //kernel.h 定义了经常用到的函数原型及宏定义。
二、内核中的描述:
    刚开始接触linux驱动,一开始我们就需要使用module_init和module_exit宏, 但是不明白这个宏的具体是怎么实现的,看了下内核的代码,在内核中这两个宏描述

点击(此处)折叠或打开

  1. /**
  2.  * module_init() - driver initialization entry point     驱动初始化入口
  3.  * @x: function to be run at kernel boot time or module insertion   在内核引导的时候或者模块插入的时候运行
  4.  * 
  5.  * module_init() will either be called during do_initcalls() (if
  6.  * builtin) or at module insertion time (if a module).  There can only
  7.  * be one per module.
  8.  **/
  9. #define module_init(x)    __initcall(x);
  10. /**
  11.  * module_exit() - driver exit entry point
  12.  * @x: function to be run when driver is removed
  13.  *
  14.  * module_exit() will wrap the driver clean-up code
  15.  * with cleanup_module() when used with rmmod when
  16.  * the driver is a module. If the driver is statically
  17.  * compiled into the kernel, module_exit() has no effect.
  18.  * There can only be one per module.
  19.  */
  20. #define module_exit(x)    __exitcall(x);
  21. /* Each module must use one module_init(). */
  22. #define module_init(initfn)                   
  23. static inline initcall_t __inittest(void)       
  24.     { return initfn; }                    \
  25.     int init_module(void) __attribute__((alias(#initfn)));
  26. /* This is only required if you want to be unloadable. */、
  27. #define module_exit(exitfn)                    \
  28.     static inline exitcall_t __exittest(void)        \
  29.     { return exitfn; }                    \
  30.     void cleanup_module(void) __attribute__((alias(#exitfn)));


以经典的hello_world模块为例,在模块实现文件中,我们编写了如下语句:

module_init(hello_init);

它会被编译器展开成如下:

在文件linux/init.h中定义了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

那么在编译时刻,编译器会将其转化为如下语句
static initcall_t __initcall_hello_init6 __used __attribute__((__section__(".initcall6 .init"))) = hello_init;
实际上定义了一个类型为函数指针的变量__initcall_hello_init6,存放的是hello_init函数地址.
其中initcall_t是一个函数指针 typedef int (*initcall_t)(void); //linux/init.h中定义,
__used也是一个宏,在使用GCC3.4之前的编译器被展开成__attribute__((unused))来禁止编译器弹出有关函数没有被用到的的警告信息。在3.4之后被展开成__attribute__((used))功能一样
__attribute__((__section__(".initcall6 .init")))将该变量加载到段.initcall6.init上,在链接脚本头文件上可以找到如下语句:

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

编译过内核后,生成的vmlinux.lds文件中有如下语句:

__initcall_start = .; *(.initcallearly.init) __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) __initcall_end = .;

当insmod的时候,内核从initcall6.init段中读取到该地址,然后跳转到该地址去执行,所以就打印出hello_init语句中实现的输出.




参考:module_init宏分析    来源:linux社区   作者:jefbai



阅读(2588) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:驱动基础(二)总线设备驱动注册流程

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