module_init的加载和释放
转自:http://blog.csdn.net/dysh1985/article/details/7597105
像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
其中,init.h 定义了驱动的初始化和退出相关的函数,kernel.h 定义了经常用到的函数原型及宏定义,module.h 定义了内核模块相关的函数、变量及宏。
几乎每个linux驱动都有个module_init(与module_exit的定义在Init.h (\include\linux) 中)。没错,驱动的加载就靠它。为什么需要这样一个宏?原因是按照一般
的编程想法,各部分的初始化函数会在一个固定的函数里调用比如:
void init(void)
{
init_a();
init_b();
}
如果再加入一个初始化函数呢,那么在init_b()后面再加一行:init_c(); 这样确实能完成我们的功能,但这样有一定的问题,就是不能独立的添加初始化函数,每次添
加一个新的函数都要修改init函数。可以采用另一种方式来处理这个问题,只要用一个宏来修饰一下:
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。定义如下:
#define module_init(x) __initcall(x); //include\linux\init.h
#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
如果某驱动想以func作为该驱动的入口,则可以如下声明:module_init(func);被上面的宏处理过后,变成 __initcall_func6 __used加入到内核映像的".initcall"区
。内核的加载的时候,会搜索".initcall"中的所有条目,并按优先级加载它们,普通驱动程序的优先级是6。其它模块优先级列出如下:值越小,越先加载。
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
可以看到,被声明为pure_initcall的最先加载。
module_init除了初始化加载之外,还有后期释放内存的作用。linux kernel中有很大一部分代码是设备驱动代码,这些驱动代码都有初始化和反初始化函数,这些代码 linux就是这样做的,对只需要初始化运行一次的函数都加上__init属性,__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,module_exit的参数卸载时同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作,显然__init和__exit对动态加载的模块是无效的,只支持完全编译进内核。
在kernel初始化后期,释放所有这些函数代码所占的内存空间。连接器把带__init属性的函数放在同一个section里,在用完以后,把整个section释放掉。当函数初始化
完成后这个区域可以被清除掉以节约系统内存。Kenrel启动时看到的消息“Freeing unused kernel memory: xxxk freed”同它有关。
我们看源码,init/main.c中start_kernel是进入kernel()的第一个c函数,在这个函数的最后一行是rest_init();
static void rest_init(void)
{
.....
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
unlock_kernel();
cpu_idle();
.....
}
创建了一个内核线程,主函数kernel_init末尾有个函数: init_post();
这个init_post中的第一句就是free_initmem();就是用来释放初始化代码和数据的。
void free_initmem(void)
{
if (!machine_is_integrator() && !machine_is_cintegrator()) {
free_area((unsigned long)(&__init_begin),
(unsigned long)(&__init_end),
"init"); }
}
接下来就是kernel内存管理的事了。
module_init的加载和释放的更多相关文章
- module_init宏解析 linux驱动的入口函数module_init的加载和释放
linux驱动的入口函数module_init的加载和释放 http://blog.csdn.net/zhandoushi1982/article/details/4927579 void free_ ...
- linux驱动的入口函数module_init的加载和释放【转】
本文转载自:http://blog.csdn.net/zhandoushi1982/article/details/4927579 就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含 ...
- AppDomain加载与释放dll
AppDomain加载与释放dll 几年前写过同名随笔,但今天应不大适用了.但还有几个朋友留言关注,我重新发布相关代码. 首先我们的目的就是运行期间更新dll,并应用dll.这个过程需要应用 AppD ...
- Kernel启动时 驱动是如何加载的module_init,加载的次序如何;略见本文
Init.h中有相关initcall的启动次序,在system.map中可看出具体的__initcall指针的前后次序 #define pure_initcall(fn) __define_initc ...
- Unity开发实战探讨-资源的加载释放最佳策略
注:本文中用到的大部分术语和函数都是Unity中比较基本的概念,所以本文只是直接引用,不再详细解释各种概念的具体内容,若要深入了解,请查阅相关资料. Unity的资源陷阱 游戏资源的加载和释放导致的内 ...
- Unity5 AssetBundle 打包以及加载
using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEditor; us ...
- [转]全面理解Unity加载和内存管理
[转]全面理解Unity加载和内存管理 最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质 ...
- jquery的ready方法(DOM是否加载完)详解与使用
jquery的ready方法(准备DOM触发)还是比较复杂的,我们先看流程图:
- Android中插件开发篇之----动态加载Activity(免安装运行程序)
一.前言 又到周末了,时间过的很快,今天我们来看一下Android中插件开发篇的最后一篇文章的内容:动态加载Activity(免安装运行程序),在上一篇文章中说道了,如何动态加载资源(应用换肤原理解析 ...
随机推荐
- VS2010出现FileTracker : error FTK1011编译错误的解决办法
VS2010出现FileTracker : error FTK1011不知道是不是vs2010的一个bug,反正有人提交了. FileTracker : error FTK1011编译错误的解决办法有 ...
- Python项目之我的第一个爬虫----爬取豆瓣图书网,统计图书数量
今天,花了一个晚上的时间边学边做,搞出了我的第一个爬虫.学习Python有两个月了,期间断断续续,但是始终放弃,今天搞了一个小项目,有种丰收的喜悦.废话不说了,直接附上我的全部代码. # -*- co ...
- [Android]Volley源码分析(四)
上篇中有提到NetworkDispatcher是通过mNetwork(Network类型)来进行网络访问的,现在来看一下关于Network是如何进行网络访问的. Network部分的类图:
- Html特殊字符表
原始字符 entity 原始字符 entity " " & & ' ' < < > > ¡ ¡ ¢ ¢ £ £ ¤ ¤ ¥ ¥ ¦ ...
- <!DOCTYPE>标签的定义与用法
<!DOCTYPE> 声明不是 HTML 标签:它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令. 在 HTML 4.01 中,<!DOCTYPE> 声明 ...
- [Unity3d]游戏中子弹碰撞的处理
如果使用Collider+Rigidbody的方式来处理,则它是每一帧进行判定碰撞:如果子弹过快导致碰撞发生在2帧之间,则会导致无法捕获这个碰撞效果 基于上述原因,我们要使用射线Raycast进行子弹 ...
- 循环中的continue功能
在oracle存储过程中,有时我们希望在循环中为某种情况时不做任何处理,类似于c语言中的continue,跳过本次循环:在oracle 11g中好像增加了这个关键字,在之前版本中我们可以通过如下方式来 ...
- HTC学习笔记
添加一个属性的setter, getter 建立一个页面 <html> <head> <title>TODO supply a title</title> ...
- VBA笔记(一)
开启VBA编程环境--VBE 方法一:按<Alt+F11>组合建 方法二:查看代码 宏设置 当然启用宏的设置方式不同,宏的启动方式也不一样. 首先打开"office 按钮&quo ...
- 使用excel计算指数平滑和移动平均
指数平滑法 原数数据如下: 点击数据——数据分析 选择指数平滑 最一次平滑 由于我们选择的区域是B1:B22,第一个单元格“钢产量”,被当做标志,所以我们应该勾选标志.当我们勾选了标志后,列中的第 ...