⭐驱动之module_init/module_exit与系统启动关系
在前面helloworld的编写里面,我们使用了两个宏分别是module_init和module_exit,这里分析下为什么使用这两个宏。
在写模块的时候有两个特殊的函数,分别是init_module和cleanup_module,这两个函数分别在insmod的时候和rmmod的时候调用,并且insmod和rmmod只识别这两个特殊的函数,可是我们前面的例子里面并没有这两个函数。怎么会这样呢,那就必须得说说module_init/module_exit了。
一个驱动可以作为一个模块动态的加载到内核里,也可以作为内核的一部分静态的编译进内核,module_init/module_exit也就有了两个含义:
一、动态编译成模块
在内核里有如下定义:
/* 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))); /* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
首先我们可以发现发现module_init有两个含义:
1、验证加载函数的格式
static inline initcall_t __inittest(void) \
{ return initfn; }
这个函数的作用是验证我们穿过来的加载函数格式是否正确,linux内核规定加载函数的的原型是:
typedef int (*initcall_t)(void);
所以我们写加载函数的时候必须是返回值为int参数为void的函数,这个在内核里要求比较严格,所以我们写加载函数的时候必须按照这个约定。
2、定义别名
int init_module(void) __attribute__((alias(#initfn)));
这段代码的作用是给我们的加载函数定义一个别名,别名就是我们前面提到的init_module,这样insmod就能够执行我们的加载函数了。
module_exit的作用和module_init一样,同样也是验证函数格式和定义别名。
二、静态编译
在静态编译的时候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
通过这些段代码,我们能够看出最终的结果是将我们的使用module_init修饰的函数指针链接到一个叫.initcall的段里,也就是说最终所以的使用module_init修饰的函数指针都被链接在这个段里,最终内核在启动的时候顺序调用所有链接在这个段里的函数,实现设备的初始化。
module_exit在静态编译的时候没有意义,因为静态编译的驱动无法卸载!
显然 对动态加载的模块是无效的;
Init.h中有相关initcall的启动次序,在system.map中可看出具体的__initcall指针的前后次序
#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)
module_init在的启动序号为6,它的展开后就是__define_initcall("6",fn,6)
#definedevice_initcall(fn) __define_initcall("6",fn,6)
#define__initcall(fn) device_initcall(fn)
#definemodule_init(x) __initcall(x);
Kernel通过调用do_initcalls(void)加载模块,具体流程如下图:
static void__init do_initcalls(void)
{
initcall_t*fn;
for (fn =__early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
/* Makesure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
因此驱动模块在Kernel启动过程中的启动次序是非常靠后的
具体的每个驱动的启动次序可以从system.map看出,特别对于同一个优先级的各类驱动:
c003288ct __initcall_i2c_init2
c00328b0 t__initcall_video_early_init3
c00328b4 t__initcall_video2_early_init3
c00328b8t __initcall_aml_i2c_init3
c0032c18t __initcall_i2c_dev_init6
c0032c28 t__initcall_videodev_init6
c0032c30t __initcall_v4l2_i2c_drv_init6
c0032c34t __initcall_v4l2_i2c_drv_init6
c0032d24 t__initcall_video_init6
c0032d28 t__initcall_video2_init6
对于同一级别的 __initcall的次序 主要由MakeFile中.o文件的链接次序决定,具体看Kernel下的主Makefile ---- Build vmlinux
以及kernel/driver 下的obj-y
/* end */
⭐驱动之module_init/module_exit与系统启动关系的更多相关文章
- 驱动之module_init/module_exit
在前面helloworld的编写里面,我们使用了两个宏分别是module_init和module_exit,这里分析下为什么使用这两个宏. 在写模块的时候有两个特殊的函数,分别是init_module ...
- linux驱动 之 module_init解析 (上)【转】
转自:https://blog.csdn.net/Richard_LiuJH/article/details/45669207 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...
- module_init module_exit
像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:#include <linux/init.h>#inc ...
- linux内核驱动module_init解析(2)
本文转载自博客http://blog.csdn.net/u013216061/article/details/72511653 如果了解过Linux操作系统启动流程,那么当bootloader加载完k ...
- linux总线、设备和设备驱动的关系
之一:bus_type 总线是处理器和一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连,甚至是内部的虚拟"platform"总线.可以通过ls -l /sys/bu ...
- linux驱动工程面试必问知识点
linux内核原理面试必问(由易到难) 简单型 1:linux中内核空间及用户空间的区别?用户空间与内核通信方式有哪些? 2:linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化, ...
- platform总线驱动代码分析
/************************************************************************/ Linux内核版本:2.6.35.7 运行平台:三 ...
- 【Linux开发】【DSP开发】Linux设备驱动之——PCI 总线
PCI总线概述 随着通用处理器和嵌入式技术的迅猛发展,越来越多的电子设备需要由处理器控制.目前大多数CPU和外部设备都会提供PCI总线的接口,PCI总线已成为计算机系统中一种应用广泛.通用的总线标准 ...
- Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建
1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/font ...
随机推荐
- leetcode 94 中序遍历模板
/**递归的写法 * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * Tre ...
- robotframework安装和配置【转IBM:https://www.ibm.com/developerworks/cn/opensource/os-cn-robot-framework/index.html】
内容 概览 Robot Framework 介绍 Robot Framework 的安装和配置 RIDE 编辑器介绍 创建测试项目 简单的测试用例的编写 总结 相关主题 评论 Robot Fr ...
- log4j详细配置解析
出自:http://www.blogjava.net/zJun/archive/2006/06/28/55511.html Log4J的配置文件(Configuration File)就是用来设置记录 ...
- PHP文件锁定机制
<?php //如果多用户访问一个文件,采用文件锁定机制 /* flock()文件锁定 */ header("Content-Type:text/html;charset=utf8&q ...
- 洛谷 [P1290] 欧几里得的游戏
SG函数的应用 看到这题就想到了SG函数 那么可以考虑最终情况:一个数是x,另一个是0,那么先手必败(因为上一个人已经得到0了,其实游戏已经结束了) 剩下的情况:一个数n, 一个数m,假设n>m ...
- P3102 [USACO14FEB]秘密代码Secret Code
题目描述 Farmer John has secret message that he wants to hide from his cows; the message is a string of ...
- 【HDOJ6222】Heron and His Triangle(Java,二分,递推)
题意:让你找这样的一个三角形,三条边为t,t-1,t+1,并且面积为整数,最后满足t大于等于n. n<=1e30 思路:直接推式子不会,打表找规律 f(n)=4*f(n-1)-f(n-2)(n& ...
- 【Codeforces Round #502 (Div. 1 + Div. 2) 】
A:https://www.cnblogs.com/myx12345/p/9843032.html B:https://www.cnblogs.com/myx12345/p/9843050.html ...
- Codeforces983D. Arkady and Rectangles
$n \leq 100000$个矩形,一个一个覆盖在坐标系上,每个颜色都不一样,问最后能看到几种颜色. 由于后面的颜色可以覆盖前面的颜色,可以把颜色与时间联系上,第$i$个矩形颜色$i$来把时间维变成 ...
- C++ 中new
operator new在C++中的各种写法 (2011-09-21 14:59:33) 标签: 杂谈 乍一看,在C++中动态分配内存很简单:new是分配,delete是释放,就这么简单.然而,这 ...