【Linux驱动】linux内核模块简介
2023-09-14 08:56:49 时间
这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程。主要从模块开发中的常用指令、内核模块程序的结构、模块使用计数以及模块的编译等角度对内核模块进行介绍。在Linux系统开发过程中,以模块的形式开发其重要性不言自明,而在嵌入式设备驱动开发中将驱动程序以模块的形式发布,更是极大地提高了设备使用的灵活性——用户只需要拿到相关驱动模块,再插入到用户的内核中,即可灵活地使用你的设备。
二. 文章提纲
2) 模块卸载函数(一般需要)
在用rmmod或modprobe命令卸载模块时,该函数被执行。完成与加载相反的工作。
模块的卸载函数和模块加载函数实现相反的功能,主要包括
若模块加载函数注册了XXX,则模块卸载函数注销XXX
若模块加载函数动态分配了内存,则模块卸载函数释放这些内存
若模块加载函数申请了硬件资源,则模块卸载函数释放这些硬件资源
若模块加载函数开启了硬件资源,则模块卸载函数一定要关闭这些资源
代码清单:
3) 模块许可证声明(必须)
如果不声明,则在模块加载时会收到内核被污染的警告,一般应遵循GPL协议。
代码清单:
在向内核插入模块的时候可以用以下方式,并且可以在内核日志中看到模块加载以后变量已经有了值。
5) 模块导出符号(可选)
使用模块导出符号,方便其它模块依赖于该模块,并使用模块中的变量和函数等。
在Linux2.6的内核中,/proc/kallsyms文件对应着符号表,它记录了符号和符号对应的内存地址。对于模块而言,使用下面的宏可以导出符号。
八.使用模块绕开
如果功能不编译成模块,则无法绕开GPL,编译成模块后公司发布产品则只需要发布模块,而不需要发布源码。为了Linux系统能够支持模块,需要做以下的工作:
内核编译时选择“可以加载模块”,嵌入式产品一般都不需要卸载模块,则可以不选择“可卸载模块”
将我们的ko文件放在文件系统中
Linux系统实现了insmod、rmmod等工具
使用时可以用insmod手动加载模块,也可以修改/etc/init.d/rcS文件,从而在系统启动的时候就加载模块。
九.总结
本文主要介绍内核模块的概念和基本编程方法,内核模块主要由加载、卸载函数功能函数等一系列声明组成。它可以被传入参数,也可以导出符号,供其它的模块使用。
十.补充
学习内核,避免不了要和模块打交道,而模块的编译一直都是用以前写好的makefile文件,没有研究过模块是如何编译的,后来在内核里面找到了一份文档,才发现模块的编译的方式很“丰富”
---------------------------------------------------------------------------------------
一,一般的模块的编译。
我喜欢用的一种格式如下:
1,内核源代码的绝对路径,kernel_path。
2,模块所在的位置,一般都是当前目录,PWD变量指定。
3,产生的模块的目标文件。这文件名要和模块.c文件名一致。
------------------------------------------------------------------------------------------
二,多个C文件的模块编译。
在用户态编程时,很多时候都是多个文件编译,这么多文件组成一个项目。
这么多文件可以由makefile组织到一起,同样模块中也可以编译多个文件。
而且实现起来也特别简单。
见上面的Makefile文件。
-----------------------------------------------------------------------------------------
三,多个模块同时编译。 C文件:hello.c find_mod.c 目标模块:hello.ko find_mod.ko
----------------------------------------------------------------------------------------
四,其他。
还有很多其他的编译方式,比如说要在包含指定目录的.h文件,该如何包含,加入这个模块中有多个目录,又该怎么编写Makefile,等等,这些在module.txt中就有介绍。
---------------------------------------------------------------------------------------
Linux驱动简介 系统调用是内核和应用程序之间的接口,而驱动程序是内核和硬件之间的接口。它为应用程序屏蔽了硬件的细节,故对应用程序而言,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。
1. 摘要
2. 文章提纲
3. 概述
4. 模块开发常用的指令
5. 内核模块程序结构
6. 模块使用计数
7. 模块的编译
8. 使用模块绕开GPL
9. 总结
三.概述 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区段(包括前两者)。 代码清单:![复制代码](http://common.cnblogs.com/images/copycode.gif)
1 static int __init XXX_init(void) 5 return 0; 10 moudle_init(XXX_init);
![复制代码](http://common.cnblogs.com/images/copycode.gif)
![复制代码](http://common.cnblogs.com/images/copycode.gif)
1 static void __exit XXX_exit(void) 9 moudle_exit(XXX_exit);
![复制代码](http://common.cnblogs.com/images/copycode.gif)
1 MODULE_LICENSE("GPL");4) 模块参数(可选) 模块在被加载时传递给模块的值,本身应该是模块内部的全局变量。 示例程序book.c
![复制代码](http://common.cnblogs.com/images/copycode.gif)
1 #include linux/init.h 3 #include linux/module.h 7 static char *bookName = "Good Book."; 9 static int bookNumber = 100; 13 static int __init book_init(void) 17 printk(KERN_INFO "Book name is %s\n", bookName); 19 printk(KERN_INFO "Book number is %d\n", bookNumber); 21 return 0; 27 static void __exit book_exit(void) 31 printk(KERN_INFO "Book module exit.\n"); 37 module_init(book_init); 39 module_exit(book_exit); 41 module_param(bookName, charp, S_IRUGO); 43 module_param(bookNumber, int, S_IRUGO); 47 MODULE_LICENSE("GPL");
![复制代码](http://common.cnblogs.com/images/copycode.gif)
![](http://images.cnitblog.com/blog/461469/201311/28220616-7763d5e796f34b42b3ebe2e39be3c4a3.png)
1 EXPORT_SYMBOL(符号名);或
1 EXPORT_GPL_SYMBOL(符号名);6) 模块信息(可选) 模块信息则是指模块的作者信息等。 六.模块使用计数 Linux内核提供了MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT宏来管理模块使用计数。但是对于内核模块而言,一般不会自己管理使用计数。 七.模块的编译 将下面的Makefile文件放在book.c同级的目录下,然后使用#make命令或者#make all命令编译即可生成book.ko模块文件。 对应的Makefile:
![复制代码](http://common.cnblogs.com/images/copycode.gif)
1 ifneq ($(KERNELRELEASE),) 3 mymodule_objs := book.o 5 obj-m := book.o 7 else 9 PWD := $(shell pwd) 11 KVER ?= $(shell uname -r) 13 KDIR := /usr/src/linux-headers-2.6.38-8-generic 17 all: 19 $(MAKE) -C $(KDIR) M=$(PWD) 21 clean: 23 rm -rf *.mod.c *.mod.o *.ko *.o *.tmp_versions *.order *symvers 25 endif
![复制代码](http://common.cnblogs.com/images/copycode.gif)
---------------------------------------------------------------------------------------
一,一般的模块的编译。
我喜欢用的一种格式如下:
1,内核源代码的绝对路径,kernel_path。
2,模块所在的位置,一般都是当前目录,PWD变量指定。
3,产生的模块的目标文件。这文件名要和模块.c文件名一致。
------------------------------------------------------------------------------------------
二,多个C文件的模块编译。
在用户态编程时,很多时候都是多个文件编译,这么多文件组成一个项目。
这么多文件可以由makefile组织到一起,同样模块中也可以编译多个文件。
而且实现起来也特别简单。
见上面的Makefile文件。
-----------------------------------------------------------------------------------------
三,多个模块同时编译。 C文件:hello.c find_mod.c 目标模块:hello.ko find_mod.ko
----------------------------------------------------------------------------------------
四,其他。
还有很多其他的编译方式,比如说要在包含指定目录的.h文件,该如何包含,加入这个模块中有多个目录,又该怎么编写Makefile,等等,这些在module.txt中就有介绍。
---------------------------------------------------------------------------------------
Linux驱动简介 系统调用是内核和应用程序之间的接口,而驱动程序是内核和硬件之间的接口。它为应用程序屏蔽了硬件的细节,故对应用程序而言,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。
相关文章
- oracle linux 启动
- 嵌入式Linux驱动和固件有何区别?供应商是如何用固件压缩成本的?
- 【学习总结】快速上手Linux玩转典型应用-第2章-linux简介
- Linux Shell脚本自动化编程实战- scp非交互传文件
- Linux基础之linux服务器服务器间拷贝文件
- linux配置网卡IP地址命令详细介绍及一些常用网络配置命令
- linux deepin/debian/ubuntu apt查看软件版本 && apt安装指定版本的包
- linux fedora35更改开机默认等待时间timeout
- Atitit. 查找linux 项目源码位置
- linux驱动学习
- 30个在开发中常用的linux命令以及linux常用快捷键,比如pwd,which,ls,cd,cat,tail,touch,mkdir,cp,mv,sudo,chown,find,yum,ps等
- 【Linux 内核 内存管理】内存管理架构 ④ ( 内存分配系统调用过程 | 用户层 malloc free | 系统调用层 brk mmap | 内核层 kmalloc | 内存管理流程 )
- 【Linux 内核】编译 Linux 内核 ② ( 解压内核源码 | 查询当前 Linux 内核版本号 | 进入并查看 linux 内核源码目录 )
- linux驱动面试题目汇总
- Linux I2C设备驱动编写(二)
- Linux lscpu显示CPU架构信息
- L64.linux命令每日一练 -- 第十章 Linux网络管理命令 -- ifconfig和ifup
- L57.linux命令每日一练 -- 第九章 Linux进程管理命令 -- ps和pstree
- L40.linux命令每日一练 -- 第七章 Linux用户管理及用户信息查询命令 -- useradd和usermod
- L1.linux命令每日一练 -- 第一章 Linux命令行简介
- Linux下Camera驱动结构
- Linux之iftop命令,实时流量监控工具
- 嵌入式linux开发,web服务,lighttpd移植
- 嵌入式linux开发,pptp失败,ConfReq无回复,LCP: timeout sending Config-Requests,Connection terminated,Modem Modem
- Linux基础命令-free显示系统内存使用量
- linux下常用压缩命令 tar压缩Linux解压 压缩Linux压缩Ubuntu压缩centos压缩解压gz txt.gz
- Linux安装conda Ubuntu安装conda Linux安装百度云 Ubuntu安装百度云 服务器安装百度云 Linux上传到百度云 Linux上传文件到百度云 Linux从百度云下载
- Linux 系统中用户切换(su user与 su - user 的区别)
- 【Linux驱动开发100问】什么是Linux内核?
- Linux用户密码管理