【linux驱动笔记】linux模块机制浅析
1. 模块module
操作系统分微内核和宏内核,微内核优点,可以使操作系统仅作很少的事,其它事情如网络处理等都作为应用程序来实现,微内核精简的同时,必然带来性能的下降。而linux的宏内核设计,保证了设计性能,但linux作为一个通用操作系统,必然会兼容很多硬件,而linux本身的宏内核设计,导致了如果同时兼容所有的硬件,其编译代码将庞大无比,为了解决这个问题,linux效仿微内核,采用了模块这一天才思想。当内核配置make menuconfig时,可以选择M,将驱动作为模块来加载,其生成的文件后缀为*.ko. 即kernel object,内核对象。采用模块设计,解决了上述问题,但是本质上来说,模块加载进内核后,还是运行在内核态,所以驱动的bug有可能引起系统崩溃,这是在驱动设计时需要特别注意的。
1.1. 模块实例
#include <linux/init.h>
#include <linux/module.h>
static int hellokernel_init(void)
{
printk("Hello driver!\n");
return 0;
}
static void hellokernel_exit(void)
{
printk("Bye driver!\n");
}
module_init(hellokernel_init);
module_exit(hellokernel_exit);
上面都的module_init,module_exit都干了什么,当加载模块insmod 和卸载模块rmmod时又干了什么,下面将说明。
1.2. module_init,module_exit
\include\linux\Init.h 中有:
#ifndef MODULE
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
#define module_exit(x) __exitcall(x);
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
#else
/* 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)));
#endif
从上述处理可以看出,当以非模块编译时,module_init等通过如下路径被执行,其被静态编译进内核。
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
而上述用__define_initcall(fn, id)定义的函数都是在linux内核启动时被执行的。
start_kernel→rest_init→kernel_init→kernel_init_freeable→do_basic_setup→do_initcalls
而如果是模块编译时,为动态编译
int init_module(void) __attribute__((alias(#initfn)));
这段代码的作用是给我们的加载函数定义一个别名,别名就是init_module
1.3. 加载模块
将hello.ko拷贝到/home/tang/wk-tzp/prj/nfs/fs/ramdisk_fs/lib/modules/3.10.53的目录或者其子目录下,
必须拷贝到对应的版本号如3.10.53下,否则会出问题。
insmod hello.ko
insmod是个应用程序,在嵌入式中,这些应用包括ls,pwd等都来自于大名鼎鼎的busybox。
因此需要阅读busybox的源代码,才能知道insmod究竟干了什么。
在busybox-1.22.1\modutils的insmod.c中:有:
insmod_main→bb_init_module
→
…
try_to_mmap_module //通过mmap函数将 xxx.ko文件内容映射到内存中。
…
init_module(image, image_size, options) //是宏syscall(__NR_init_module, mod, len, opts),调用linux系统调用
// image:指向***.ko文件内容开始,image_size为***.ko模块文件大小,options为参数
…
在arch/arm/include/asm/unistd.h中,有#define __NR_init_module (__NR_SYSCALL_BASE+128)
在linux/arch/arm/kernel/calls.S有:
/* 125 */ CALL(sys_mprotect)
CALL(sys_sigprocmask)
CALL(sys_ni_syscall) /* was sys_create_module */
CALL(sys_init_module)
CALL(sys_delete_module)
可见,__NR_init_module 对应 sys_init_module,所以insmod最终调用sys_init_module
在Module.c (src\kernel) 中有:
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
在Syscalls.h (src\include\linux) 中有:#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
而#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
由上述可见,SYSCALL_DEFINE3(init_module…定义了 sys_init_module函数。
继续调用:
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
→load_module→do_init_module→do_one_initcall(mod->init)→ret = fn();即执行了module_init(hello_init)中定义的函数
hello_init。
1.4. 卸载模块
rmmod hello, 不能用 rmmod hello.ko
仍然从应用层开始,rmmod是由工具箱busybox实现的,
rmmod.c (busybox-1.22.1\modutils): int rmmod_main(int argc UNUSED_PARAM, char **argv)
→bb_delete_module // 宏定义为syscall(__NR_delete_module, mod, flags),执行linux系统调用sys_delete_module
同insmod讨论的一样,最后会执行如下的语句
在Module.c (src\kernel) 中有:
SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags)
→mod->exit(); //即module_exit(exit_func)定义的函数exit_func
1.5. 细节
src\Makefile:make modules : 目标是modules, 编译好后需要安装在制作的文件系统
make modules_install INSTALL_MOD_PATH=~/fs/ramdisk_fs
THIS_MODULE: http://www.cnblogs.com/ziziwu/archive/2012/07/06/2578283.html
insmod/modprobe rmmod lsmod modinfo
insmod hellokernel.ko irq=100 pstr=china fish=1,2,3
echo 200 > /sys/module/hellokernel/parameters/irq
echo 10,20,30 > /sys/module/hellokernel/parameters/fish
权限问题:
非0:在/sys/module/模块名/paramters/文件
通过修改这个文件完成对变量的内容修改
问题:会占用内存的资源
权限为0:就不会有一个文件存在,只能在模块 加载的时候才能修改
1.6. 函数集合
insmod/modprobe
rmmod
lsmod
modinfo
1.7. 文献
http://www.cnblogs.com/fanzhidongyzby/p/3730131.html
欢迎转载,转载时需保留作者信息,谢谢。
博客园地址:http://www.cnblogs.com/embedded-tzp
Csdn博客地址:http://blog.csdn.net/xiayulewa
【linux驱动笔记】linux模块机制浅析的更多相关文章
- Linux模块机制浅析
Linux模块机制浅析 Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...
- Linux模块机制浅析_转
Linux模块机制浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/3730131.htmlLinux允许用户通过插入模块,实现干预内核的目的.一直以来,对l ...
- 【ARM-Linux开发】Linux模块机制浅析
Linux模块机制浅析 Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...
- 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】
转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...
- webpack模块机制浅析【一】
webpack模块机制浅析[一] 今天看了看webpack打包后的代码,所以就去分析了下代码的运行机制. 下面这段代码是webpack打包后的最基本的形式,可以说是[骨架] (function(roo ...
- Linux学习笔记-Linux系统简介
Linux学习笔记-Linux系统简介 UNIX与Linux发展史 UNIX是父亲,Linux是儿子. UNIX发行版本 操作系统 公司 硬件平台 AIX IBM PowerPC HP-UX HP P ...
- 【Linux开发】Linux模块机制浅析
Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! 我们通过创建一个简单的模块 ...
- linux驱动编写之poll机制
一.概念 1.poll情景描述 以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值.这样做的效果是:如果有按键按下了,调用该re ...
- 【linux驱动】linux驱动总览
欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http:// ...
- Linux 驱动框架---linux 设备
Linux 设备 Linux驱动中的三大主要基础成员主要是设备,总线和驱动.今天先来从设备开始分析先把设备相关的数据结构放到这里方便后面看到来查,其中有些进行了简单的注释. struct device ...
随机推荐
- NGUI 按钮音效问题
昨天给NGUI的按钮添加音效时,刚开始是自己新建空对象绑定声音的,后来发现NGUI按钮携带button sound组件,直接将音效拖入即可,不用写一行代码,非常简单.但是后来发现添加相同的音效有的按钮 ...
- jquery事件链式写法
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...
- codeforces 463E . Caisa and Tree
题目链接 给一棵树, 两种操作, 一种是将点u的权值改为y, 另一种是查询根节点到点u的路径上, gcd(v, u)>1的深度最深的点v. 修改操作不超过50次. 这个题, 暴力可以过, 但是在 ...
- Linux下同步工具inotify+rsync使用详解
1. rsync 1.1 什么是rsync rsync是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件.它使用所谓的“Rsync演算法”来使本地和远程两个主机之间的文件达到同步,这 ...
- 返回hash 类型的json
else{ $hash{$phone}="没有找到需要的验证码信息"; $c->render(json => \%hash );
- Uva 1103 Ancient Messages
大致思路是DFS: 1. 每个图案所包含的白色连通块数量不一: Ankh : 1 ; Wedjat : 3 ; Djed : 5 ; Scarab : 4 ; Was : 0 ; Ak ...
- Java数据类型BooleanDemo
- 在Xcode中使用C++与Objective-C混编
有时候,出于性能或可移植性的考虑,需要在iOS项目中使用到C++. 假设我们用C++写了下面的People类: // // People.h // MixedWithCppDemo // // ...
- datetime方法
DateTime dt = DateTime.Now; dt.ToString();//2005-11-5 13:21:25 dt.ToFileTime().ToString(); dt.ToFile ...
- java类和对象的基础(笔记)
在Java类的设计中,有时希望一个类在任何时候只能有一个实例.这时可以将该类设计为单例模式(singleton).要将一个类设计为单例模式,需要把类的构造方法的访问修饰符声明为private,然后在类 ...