Linux内核与驱动开发学习总结:initcall 和contain_of宏(十)
来源:互联网 发布:淘宝开服装店 编辑:程序博客网 时间:2024/05/01 01:02
内核驱动初始化顺序:
main.c/start_kernel() ---> rest_init() ---> kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)
---> kernel_init() ---> do_basic_setup() ---> do_initcalls() ---> do_one_initcall()
__define_initcall的作用:
宏定义__define_initcall(level,fn,id)对于内核的初始化很重要,他指示编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序放在一个section中。
在内核初始化阶段,do_initcalls() 将按顺序从该section中以函数指针形式取出这些函数的起始地址,来依次完成相应的初始化。由于内核某些部分的初始化需要依赖于其他某些部分的初始化的完成,因此这个顺序排列常常很重要。
[html] view plaincopy
1. #define module_init(x) __initcall(x);
[html] view plaincopy
1. #define __initcall(fn) device_initcall(fn)
[html] view plaincopy
1. #define device_initcall(fn) __define_initcall("6",fn,6)
1. 分析__define_initcall(level, fn, id) 宏定义:
这个宏定义在 kernel/include/linux/init.h中:
[html] view plaincopy
1. #define __define_initcall(level,fn,id) \
2. static initcall_t __initcall_##fn##id __used \
3. __attribute__((__section__(".initcall" level ".init"))) = fn
其中initcall_t 是个函数指针类型:
[html] view plaincopy
1. typedef int (*initcall_t)(void);
而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。
所以这个宏定义的含义是:
a) 声明一个名称为__initcall_##fn的函数指针(其中##表示替换连接);
b) 将这个函数指针初始化为fn;
c) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"的section中(比如level="1",代表这个section的名称是 ".initcall1.init")。
d) level 值越小表示优先及更高。kernel中 level的范围是(0~7);
2. 和初始化相关的section-initcall.init段:
section-initcall.init 分成7个子section:
[html] view plaincopy
1. __initcall_start = .;
2. .initcall.init : {
3. *(.initcall1.init)
4. *(.initcall2.init)
5. *(.initcall3.init)
6. *(.initcall4.init)
7. *(.initcall5.init)
8. *(.initcall6.init)
9. *(.initcall7.init)
10. }
Makefile中有:LDFLAGS_vmlinux += -T arch/$(ARCH)/kernel/vmlinux.lds.s
3. 内核初始化函数do_initcalls():
do_initcalls() 将从 .initcall.init 中,也就是这7个section中依次取出任何的函数指针,并调用这些函数指针所指向的函数,来完成内核的一些相关的初始化。
总结:
1) __define_initcall(level,fn,id)的作用就是指示编译器把一些初始化函数的指针(即:函数起始地址)按照顺序放置一个名为 .initcall.init 的section中,这个section又被分成了7个子section,它们按顺序排列。 在内核初始化阶段,这些放置到这个section中的函数指针将供 do_initcalls() 按顺序依次调用,来完成相应初始化。
2) 函数指针放置到的子section由宏定义的level确定,对应level较小的子section位于较前面。而位于同一个子section内的函数指针顺序不定,将由编译器按照编译的顺序随机指定。
3) 因此,如果你希望某个初始化函数在内核初始化阶段就被调用,那么你 就应该使用宏__define_initcall(level,fn,id) 或 其7个衍生宏 把这个函数fn的对应的指针放置到按照初始化的顺序放置到相关的 section 中。同时,如果某个初始化函数fn_B需要依赖于另外一个初始化函数fn_A的完成,那么你应该把fn_B放在比fn_A对应的level值较大的子section中, 这样,do_initcalls()将在fn_A之后调用fn_B。
Contain_of:
1.该宏定义在include/linux/kernel.h中
439/**
440 * container_of - cast a member of a structure out to the containing structure
441 * @ptr: the pointer to the member.
442 * @type: the type of the container struct this is embedded in.
443 * @member: the name of the member within the struct.
444 *
445 */
446 #define container_of(ptr, type, member) ({ /
447 const typeof( ((type *)0)->member ) *__mptr = (ptr); /
448 (type *)( (char *)__mptr - offsetof(type,member) );})
它的作用显而易见,那就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。
1.#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
先分析一下这个宏的运行机理:
1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型。巧妙之处在于将0转换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;
Reference:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26404201&id=3212360
- Linux内核与驱动开发学习总结:initcall 和contain_of宏(十)
- Linux内核与驱动开发学习总结:内核访问外设IO.map_desc和ioremap(七)
- Linux内核与驱动开发学习总结:内核初始化宏__init(十二)
- Linux内核与驱动开发学习总结:主设备号和次设备号(二)
- Linux内核与驱动开发学习总结:自旋锁和信号量(五)
- Linux内核与驱动开发学习总结:中断和中断处理(八)
- Linux内核与驱动开发学习总结:设备驱动模型(九)
- 理解Linux contain_of()宏
- Linux内核与驱动开发学习总结:DMA与中断(一)
- Linux内核与驱动开发学习总结:嵌入式中南北桥(三)
- Linux内核与驱动开发学习总结:字符设备(四)
- Linux内核与驱动开发学习总结:原子操作实现(六)
- Linux内核与驱动开发学习总结:PCI中线初始化(十一)
- Linux内核和驱动学习总结
- Linux内核设计基础(十)之内核开发与总结
- 理解 linux contain_of() 宏
- Linux kernel contain_of 宏
- 理解 linux contain_of() 宏
- django csrf解决办法
- jvm指令集
- 关于Eclipse启动后,提示failed to create the java virtual machine的解决方案
- C++|C++学习笔记|三、数组、字符串、指针
- find命令详解
- Linux内核与驱动开发学习总结:initcall 和contain_of宏(十)
- 文件系统 IO 并发 一致性
- 线程安全的BlockingQueue
- 你所不知的 CSS ::before 和 ::after 伪元素用法
- CSS清除浮动_清除float浮动
- 什么是CSS清除浮动?
- Flowplayer-一款免费的WEB视频播放器
- SVN中tag branch trunk用法详解
- linux grep命令