http://blog.youkuaiyun.com/citytramper/article/details/600708
http://blog.youkuaiyun.com/citytramper/article/details/664930
http://www.360doc.com/content/11/0917/11/7473909_148946026.shtml#
感谢上面的提供,我还是粘贴一下哈。
一直以来写linux驱动,都是按照固定格式,定义一个初始化和推出函数,书上告诉我这两个函数会被调用,至于为什么会被调用,在哪调用,一直不清楚。
偶然的一个机会,看到blob里面的代码,里面有一个初始化函数列表。按照一般的编程想法,各部分的初始化函数会在一个固定的函数里调用比如:
void init(void)
{
init_a();
init_b();
}
如果再加入一个初始化函数呢,那么再init_b()后面再加一行:
init_c();
这样确实能完成我们的功能,但这样有一定的问题,就是不能独立的添加初始化函数,每次添加一个新的函数都要修改init函数,blob中的初始化函数就是完全独立的,只要用一个宏来修饰一下:
void init_a(void)
{
}
__initlist(init_a, 1);
它是通过这个宏来实现初始化函数列表的呢?
先来看__initlist的定义:
#define __init __attribute__((unused, __section__(".initlist")))
#define __initlist(fn, lvl) /
static initlist_t __init_##fn __init = { /
magic: INIT_MAGIC, /
callback: fn, /
level: lvl }
看来就是定义了一个结构体,存了初始化函数的指针,没什么特别的。请注意:__section__(".initlist")
这个属性起什么作用呢?它告诉连接器这个变量存放在.initlist区段,如果所有的初始化函数都是用这个宏,那么每个函数会有对应的一个initlist_t结构体变量存放在.initlist区段,也就是说我们可以在.initlist区段找到所有初始化函数的指针。怎么找到.initlist区段的地址呢?
extern u32 __initlist_start;
extern u32 __initlist_end;
这两个变量起作用了,__initlist_start是.initlist区段的开始,__initlist_end是结束,通过这两个变量我们就可以访问到所有的初始化函数了。
这两个变量在那定义的呢?
在一个连接器脚本文件里
. = ALIGN(4);
.initlist : {
__initlist_start = .;
*(.initlist)
__initlist_end = .;
}
这两个变量的值正好定义在.initlist区段的开始和结束地址,所以我们能通过这两个变量访问到所有的初始化函数。
与此类似,内核中也是用到这种方法,所以我们写驱动的时候比较独立,不用我们自己添加代码在一个固定的地方来调用我们自己的初始化函数和退出函数,连接器已经为我们做好了。当然module_init还有其他的特性,比如:我们的初始化函数在完成初始化后,代码占用的空间会被释放,这又是为什么呢?今天晚了,下次再写。
==============================
linux kernel中有很大一部分代码是设备驱动代码,这些驱动代码都有初始化和反初始化函数,这些代码一般都只执行一次,为了有更有效的利用内存,这些代码所占用的内存可以释放出来。
linux就是这样做的,对只需要初始化运行一次的函数都加上__init属性。在kernel初始化后期,释放所有这些函数代码所占的内存空间。它是怎么做到的呢?看过module_init和module_exit 的人知道,连接器把带__init属性的函数放在同一个section里,在用完以后,把整个section释放掉。
口说无凭,我们看源码,init/main.c中start_kernel是进入kernel的第一个c函数,在这个函数的最后一行是
rest_init();
static void rest_init(void)
{
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
unlock_kernel();
cpu_idle();
}
创建了一个内核线程,主函数init,代码如下:
static int init(void * unused)
{
lock_kernel();
do_basic_setup();
prepare_namespace();
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
free_initmem();
unlock_kernel();
红色那行代码就是用来释放初始化代码和数据的。
void free_initmem(void)
{
#ifndef CONFIG_XIP_ROM
if (!machine_is_integrator()) {
free_area((unsigned long)(&__init_begin),
(unsigned long)(&__init_end),
"init");
}
#endif
}
接下来就是kernel内存管理的事了。