⭐驱动之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 ...
随机推荐
- c++ primer note
---恢复内容开始--- 1.decltype 2.auto 3.cbegin 4.cend 5.constexpr 6.(*Parray)[10]=&arr; //Parray 指向一个含有 ...
- 准备 KVM 实验环境
KVM 是 OpenStack 使用最广泛的 Hypervisor,本节介绍如何搭建 KVM 实验环境 安装 KVM 上一节说了,KVM 是 2 型虚拟化,是运行在操作系统之上的,所以我们先要装一个 ...
- linux 管道符号 | ,以及&& ||等等特殊符号笔记
管道和重导向:“|”.“>”.“>>”.“<” 重导向就是使命令改变它所认定的标准输出.“>”可将结果输出到文件中,该文件原有内容会被删除,“>>”则将结果附 ...
- Linux crontab 定时任务Demo
# 查看定时任务 crontab -l #编辑定时任务crontab -e # 同步时间 */ * * * * /usr/sbin/ntpdate -u 127.0.0.1 # 定时删除文件 */ * ...
- centos7 搭建hadoop
参考文档:http://blog.csdn.net/xiaoxiangzi222/article/details/52757168 https://waylau.com/centos-7-instal ...
- Linux上安装使用SSH
參考博客:http://blog.csdn.net/xqhrs232/article/details/50960520 Ubuntu安装使用SSH ubuntu默认并没有安装ssh服务,如果通过ssh ...
- 最小费用最大流粗解 poj2516
最小费用最大流,一般解法如下: 在流量基础上,每条边还有权费用,即单位流量下的所需费用.在最大流量下,求最小费用.解法:在最大流算法基础上,每次按可行流增广改为每次用spfa按最小费用(用单位费用)增 ...
- AC日记——[SDOI2009]晨跑 bzoj 1877
1877: [SDOI2009]晨跑 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 2131 Solved: 1142[Submit][Status][ ...
- python3.x对python2.x变动
原文地址:http://rookiedong.iteye.com/blog/1185403 python 2.4 与 python 3.0 的比较 一. print 从语句变为函数 原: pr ...
- CODEVS_1034 家园 网络流 最大流
原题链接:http://codevs.cn/problem/1034/ 题目描述 Description 由于人类对自然的疯狂破坏,人们意识到在大约2300年之后,地球不能再居住了,于是在月球上建立了 ...