Linux设备驱动(一)---内核模块简介
来源:互联网 发布:java项目收获总结 编辑:程序博客网 时间:2024/05/21 03:25
转载出处:Linux内核模块简介
http://www.cnblogs.com/tianyou/p/3448573.html
内核模块
什么是内核模块
独立内核源码以外开发出来,并且用于内核空间使用的代码块在应用空间存放,但内核需要的时候加载进来,不用的时去掉文件名:xxx.ko
如何实现一个内核模块
Linux内核整体结构已经很庞大,包含了很多的组件,而对于我们工程师而言,有两种方法将需要的功能包含进内核当中。 一:将所有的功能都编译进Linux内核。 二:将需要的功能编译成模块,在需要的时候动态地添加。
上述两种方式优缺点分析:
第一种:优点:不会有版本不兼容的问题,不需要进行严格的版本检查缺点:生成的内核会很大;要在现有的内核中添加新的功能,则要编译整个内核第二种:优点:模块本身不编译进内核,从而控制了内核的大小;模块一旦被加载,将和其它的部分完全一样。缺点:可能会有内核与模块版本不兼容的问题,导致内核崩溃;会造成内存的利用率比较低。
模块开发常用的指令
在内核模块开发的过程中常用的有以下指令。
1) insmod: 将模块插入内核中,使用方法:#insmod XXX.ko
2) rmmod: 将模块从内核中删除,使用方法:#rmmod XXX.ko
3) lsmod: 列表显示所有的内核模块,可以和grep指令结合使用。使用方法:#lsmod | grep XXX
4) modprobe: modprobe可载入指定的个别模块,或是载入一组相依赖的模块。modprobe会根据depmod所产生的依赖关系,决定要载入哪些模块。若在载入过程中发生错误,在modprobe会卸载整组的模块。依赖关系是通过读取 /lib/modules/2.6.xx/modules.dep得到的。而该文件是通过depmod 所建立。
5) modinfo: 查看模块信息。使用方法:#modinfo XXX.ko
6) tree –a: 查看当前目录的整个树结构。使用方法:#tree -a
内核模块程序结构
1)模块加载函数(一般需要)
在用insmod或modprobe命令加载模块时,该函数被执行。完成模块的初始化工作。
Linux内核的模块加载函数一般用_**_init**标识声明,模块加载函数必须以module_init(函数名)的形式被指定。该函数返回整型值,如果执行成功,则返回0,初始化失败时则返回错误编码,Linux内核当中的错误编码是负值,在<linux/errno.h>
中定义。
在Linux中,标识__init的函数在连接时放在.init.text这个区段,而且在.initcall.init中保留一份函数指针,初始化的时候内核会根据这些指针调用初始化函数,初始化结束后释放这些init区段(包括前两者)。
static int __init XXX_init(void){ return 0;}module_init(XXX_init);
2)模块卸载函数(一般需要)
在用rmmod或modprobe命令卸载模块时,该函数被执行。完成与加载相反的工作。
模块的卸载函数和模块加载函数实现相反的功能,主要包括
- 若模块加载函数注册了XXX,则模块卸载函数注销XXX
- 若模块加载函数动态分配了内存,则模块卸载函数释放这些内存
- 若模块加载函数申请了硬件资源,则模块卸载函数释放这些硬件资源
- 若模块加载函数开启了硬件资源,则模块卸载函数一定要关闭这些资源
static void __exit XXX_exit(void){}module_exit(XXX_exit);
3)模块许可证声明(必须)
如果不声明,则在模块加载时会收到内核被污染的警告,一般应遵循GPL协议。
MODULE_LICENSE("GPL");
4)模块参数(可选)
模块在被加载时传递给模块的值,本身应该是模块内部的全局变量。
示例程序book.c
#include <linux/init.h>#include <linux/module.h>static char *bookName = "Good Book.";static int bookNumber = 100;static int __init book_init(void){ printk(KERN_INFO "Book name is %s\n", bookName); printk(KERN_INFO "Book number is %d\n", bookNumber); return 0;}static void __exit book_exit(void){ printk(KERN_INFO "Book module exit.\n");}module_init(book_init);module_exit(book_exit);module_param(bookName, charp, S_IRUGO);module_param(bookNumber, int, S_IRUGO);MODULE_LICENSE("GPL");
在向内核插入模块的时候可以用以下方式,并且可以在内核日志中看到模块加载以后变量已经有了值。
5) 模块导出符号(可选)
使用模块导出符号,方便其它模块依赖于该模块,并使用模块中的变量和函数等。
在Linux2.6的内核中,/proc/kallsyms文件对应着符号表,它记录了符号和符号对应的内存地址。对于模块而言,使用下面的宏可以导出符号。
EXPORT_SYMBOL(符号名);
或
EXPORT_GPL_SYMBOL(符号名);
6) 模块信息(可选)
模块信息则是指模块的作者信息等。
模块使用计数
Linux内核提供了MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT宏来管理模块使用计数。但是对于内核模块而言,一般不会自己管理使用计数。
模块的编译
编写Makefile
vi Makefile
obj-m += test.oall: make modules M=`pwd` -C /home/mk-kernel/android-kernel-samsung-devclean: make modules clean M=`pwd` -C /home/mk-kernel/android-kernel-samsung-dev
两个参数:
M=dir // 指定源码所在的目录
-C kernel_dir // 指定内核源码所在
使用模块
如果功能不编译成模块,则无法绕开GPL,编译成模块后公司发布产品则只需要发布模块,而不需要发布源码。为了Linux系统能够支持模块,需要做以下的工作:
- 内核编译时选择“可以加载模块”,嵌入式产品一般都不需要卸载模块,则可以不选择“可卸载模块”
- 将我们的ko文件放在文件系统中
- Linux系统实现了insmod、rmmod等工具
- 使用时可以用insmod手动加载模块,也可以修改/etc/init.d/rcS文件,从而在系统启动的时候就加载模块。
总结
本文主要介绍内核模块的概念和基本编程方法,内核模块主要由加载、卸载函数功能函数等一系列声明组成。它可以被传入参数,也可以导出符号,供其它的模块使用。
- Linux设备驱动(一)---内核模块简介
- Linux设备驱动:一,简介
- 设备驱动----1.Linux内核模块-helloworld
- Linux内核模块编程-字符设备驱动
- 【Linux驱动】linux内核模块简介
- linux 设备驱动开发学习笔记(一):最简单的内核模块
- Linux设备驱动开发详解--笔记4--Linux内核模块
- Linux设备驱动开发基础---Linux内核模块
- 深入浅出Linux设备驱动编程--Linux内核模块
- 《Linux设备驱动开发详解》--Linux内核模块
- linux设备驱动开发详情-linux内核模块相关知识
- linux 字符设备驱动(一)模块与设备
- 深入浅出Linux设备驱动编程之内核模块
- Linux内核可装载模块对设备驱动的影响
- 深入浅出Linux设备驱动编程之内核模块
- Linux 2.6内核 动态加载字符设备驱动模块
- Linux 内核模块编程的第一个字符设备驱动
- 设备驱动-----2.Linux内核模块-Makefile解析
- 400% 的飞跃-web 页面加载速度优化实战(转)
- 【NOIP初赛】 杂碎知识点
- 复制粘贴(不知道为什么在移动端不好用)
- TestNG 入门教程
- 关于MyBatis中treeselect.tag控件使用技巧
- Linux设备驱动(一)---内核模块简介
- linux命令学习(转)
- 常见字符编码辨析
- 升华网网络部第一次培训~
- 谁会开发数字货币
- iOS开发之——从零开始完成页面切换形变动画
- 从下载php到配置phpStrom步骤
- unity3d AI 学习--个体行为操控(1)--具体行为类(1)
- Codeforces-731D-data structure