现在学习看linux内核代码,一开始看一头雾水,不知道从那里开始,偶尔听一个同学说起内核就是一个while(1)循环,在看TPC/IP 内核协议栈时,经常看到内核的回调机制,结合自己看代码的经验,对内核结构猜测:内核是一个模型,这个模型具有良好的分层结构,在不同的模块里通过回调init完成注册,在推出时通过注册回调exit完成退出。
在这里,内核使用了面向对象的思想。在我们讨论编程语言时,经常这样认为:C是面向过程的,C++是面向对象的语言。诚然,C++语言的语法上使用了面向对象的机制,C在语法上是面向过程的。是否可以这样认为:面向对象是一种编程思想,在我们使用C++时,编译器和C++语法帮助我们实现了对象的模型;而C则需要我们动脑筋抽象出模型,然后通过回调来实现不同的对象对应不同的FUNCTION。
后来接触《Linux内核修炼之道》,才恍然大悟,内核的开始就是一个初始化得过程,呵呵,这一下就找到了内核的入口。在include/linux/init.h 里我们看到了内核初始化定义的数据结构。init.h头文件的开始就是__init数据结构的解释。
* These macros are used to mark some functions or
* initialized data (doesn't apply to uninitialized data)
* as `initialization' functions. The kernel can take this
* as hint that the function is used only during the initialization
* phase and free up used memory resources after
*
......
这个宏(__init)用来表明一些函数或者初始化的数据(而不是未初始化得数据)是初始化函数。kernel把这些函数做为隐藏类型,仅仅在初始化阶段使用,初始化完成后,这些数据所用的内存资源会被释放。这就是宏(__init)的作用。接下来是三个列子。
第43行:
#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)
是 __init 的定义 这句话的意思是 __init 类型的数据放在kernel文件的 (.init.text)。为什么会这样,和__section这个字段有关,请参考相关资料。在这里我猜测一下,kernel文件中有一个 init 的数据区,这个数据区分为text 区,data区,rodata区。 __init 修饰的是函数,所以放在text区,__initdata修饰数据,放在data区。
下面是《Linux内核修炼之道》的一段解释:“好像这里引出了更多的疑问,__attribute__是什么?Linux内核代码使用了大量的GNU C扩展,以至于GNU C成为能够编译内核的唯一编译器,GNU C的这些扩展对代码优化、目标代码布局、安全检查等方面也提供了很强的支持。而__attribute__就是这些扩展中的一个,它主要被用来声明一些特殊的属性,这些属性主要被用来指示编译器进行特定方面的优化和更仔细的代码检查。GNU C支持十几个属性,section是其中的一个,我们查看GCC的手册可以看到下面的描述
‘section ("section-name")'
Normally, the compiler places the code it generates in the `text'
section. Sometimes, however, you need additional sections, or you
need certain particular functions to appear in special sections.
The `section' attribute specifies that a function lives in a
particular section. For example, the declaration:
extern void foobar (void) __attribute__ ((section ("bar")));
puts the function ‘foobar' in the ‘bar' section.
Some file formats do not support arbitrary sections so the
‘section' attribute is not available on all platforms. If you
need to map the entire contents of a module to a particular
section, consider using the facilities of the linker instead.
通常编译器将函数放在.text节,变量放在.data或.bss节,使用section属性,可以让编译器将函数或变量放在指定的节中。那么前面对__init的定义便表示将它修饰的代码放在.init.text节。连接器可以把相同节的代码或数据安排在一起,比如__init修饰的所有代码都会被放在.init.text节里,初始化结束后就可以释放这部分内存。”
#ifndef MODULE
#ifndef __ASSEMBLY__
这是两个比较常见的宏:
下面解释下它的作用。
__ASSEMBLY__用于汇编和c代码共享的头文件
源程序==》预处理器(cpp)==》编译器(ccl)==》汇编器(as)==》链接器(ld)==》可执行文件
有些定义cc1可以辨认,而as汇编器不能辨认。汇编程序不需要编译器
cpp会根据源文件是否是汇编程序(.s)来选择正确的定义
__ASSEMBLY__就是为了解决这样的问题。
MODULE 宏随后解释。
在273行:
#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)
#define security_initcall(fn) module_init(fn)
/* 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)));
就是MODULE的定义:int init_module(void) __attribute__((alias(#initfn))); 在MODULE模式下,init_modele 的数据是放在alias(#initfn)这个区。上边是相关宏的定义,所有的数据区都被放在这里了。在156行以后,是#ifndef MODULE 模式,这两种情况下,数据在内核中的位置是不一样的。这就对应了驱动的两种模式,编译进内核和作为模块安装。
阅读(1069) | 评论(0) | 转发(0) |