字符设备驱动基础篇1——简单的驱动源码分析
以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。
参考资料:http://www.cnblogs.com/biaohc/p/6575074.html
module_test.c代码
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>#define MYMAJOR 200//这里手动地定义主设备号,之前必须确认200没有被用,查看方法是cat /proc/devices。比较好的方法是内核自动分配。
#define MYNAME "testchar"static int test_chrdev_open(struct inode *inode, struct file *file)
{// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。printk(KERN_INFO "test_chrdev_open\n");return 0;
}static int test_chrdev_release(struct inode *inode, struct file *file)
{printk(KERN_INFO "test_chrdev_release\n");return 0;
}// 自定义一个file_operations结构体变量,并且去填充.
//这里的.只是结构体变量初始化的一种方法而已。file_operations是内核中已经定义好的结构体。
static const struct file_operations test_fops = {.owner = THIS_MODULE, // 惯例,直接写即可.open = test_chrdev_open, // 将来应用open打开这个设备时实际调用的.release = test_chrdev_release, // 就是这个.open对应的函数
};// 模块安装函数
static int __init chrdev_init(void)
{ int ret = -1;printk(KERN_INFO "chrdev_init helloworld init\n");// 在module_init宏调用的函数中去注册字符设备驱动ret = register_chrdev(MYMAJOR, MYNAME, &test_fops);if (ret){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "register_chrdev success...\n");return 0;
}// 模块卸载函数
static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");// 在module_exit宏调用的函数中去注销字符设备驱动unregister_chrdev(MYMAJOR, MYNAME);}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
1、常用的模块操作命令
- lsmod(list module,将模块列表显示),功能是打印出当前内核中已经安装的模块列表;
- insmod(install module,安装模块),功能是向当前内核中安装一个模块,用法是insmod xxx.ko;
- modinfo(module information,模块信息),功能是打印出一个内核模块的自带信息,用法是modinfo xxx.ko;
- rmmod(remove module,卸载模块),功能是从当前内核中卸载一个已经安装的模块,用法是rmmod xxx(只需要输入模块名即可,不能加.ko后缀);
2、模块的安装
insmod与module_init宏
- insmod与module_init宏有关联,即执行insmod命令时,insmod内部实际执行的是使用module_init宏所声明的函数。
- 比如insmod module_test.ko时,insmod命令内部实际执行的操作是调用chrdev_init函数。
- 由上表明,模块的安装函数得自己编写,内核自身没有此模块的安装函数,内核只是帮忙调用你所编写的模块安装函数。
- 除了调用绑定的函数外,在内部还执行了另外一些事情。
3、模块卸载
rmmod和module_exit宏
- 和上面阐述的是同一个道理;
- 使用lsmod查看rmmod前后系统的模块记录变化。
4、模块的版本信息
(1)使用modinfo查看模块的版本信息;
(2)内核zImage中也有一个确定的版本信息;
(3)insmod时,驱动的版本信息和内核的版本信息要一致
- 否则不能安装,报错信息为:insmod: ERROR: could not insert module module_test.ko: Invalid module format;
- 模块的版本信息是为了保证模块和内核的兼容性,是一种安全措施;
(4)如何保证模块的vermagic和内核的vermagic一致?
- 确保某个内核源码树,即用来生成用于烧录的zImage,也用来编译驱动模块。
5、模块中常用宏
MODULE_xxx这种宏作用是用来添加模块描述信息,见代码的解释。
(1)MODULE_LICENSE,模块的许可证。
- 一般声明为GPL许可证,而且最好不要少,否则可能会出现莫名其妙的错误(譬如一些明显存在的函数提升找不到)。
(2)MODULE_AUTHOR 编写作者
(3)MODULE_DESCRIPTION 模块描述
(4)MODULE_ALIAS 别名信息
6、函数修饰符
(1)__init
- 本质上是宏定义,在内核源代码中有#define __init xxxx。
- 作用:将所修饰的函数放入.init.text段(默认情况下函数是被放入.text段中)。
- 这类函数都被链接器链接放入.init.text段中,因此这类函数被统一放在一起。
- 内核启动时,会统一加载.init.text段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存。因此安装完后这些函数就没用了。
- 下划线越多,越深入内核深处。
(2)__exit
7、static
8、printk函数详解
(1)printk在内核源码中用来打印信息的函数。
(2)printk和printf的差别
- printf是C库函数,在应用层编程中使用,不能在linux内核源代码中使用;
- printk是内核源码中的一个普通函数,只能在内核源码范围内使用,不能在应用编程中使用。
(3)printk可以设置打印级别
- printk的打印级别,用来控制printk打印的这条信息是否在终端上显示的;
- 应用程序中的调试信息要么全部打开,要么全部关闭,一般用条件编译来实现(DEBUG宏);
- 但是在内核非常庞大,打印信息非常多,因此需要设置打印级别。
- 可以用cat /proc/sys/kernel/printk查看打印级别;
- 可以用echo 4 /proc/sys/kernel/printk来设置打印级别为4。
(4)操作系统的命令行中也有一个打印信息级别属性,值为0-7。
- 执行printk的时候,对比printk中的打印级别与命令行中设置的打印级别;
- 小于命令行设置级别的信息会被放行打印出来,大于的就被拦截的。
- 但ubuntu中不管如何设置级别,都不能直接打印出来,必须使用dmesg命令去查看。
9、关于驱动模块中的头文件
- 驱动源代码中包含的头文件,和应用编程程序中包含的头文件不同。
- 应用编程中,所包含的头文件是应用层的头文件,是应用程序的编译器带来的(譬如gcc的头文件路径在 /usr/include下,与操作系统无关)。
- 驱动源码属于内核源码的一部分,驱动源码中的头文件,即内核源代码目录下的include目录下的头文件。
- 内核顶层目录下的include目录下的linux目录下,有init.h。
10、Makefile分析
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build //这是ubuntu提供的在本Ubuntu环境下开发驱动的内核源码树,因此如果想在此Ubuntu中开发驱动,则内核源码树目录就是它# 开发板的linux内核的源码树目录
KERN_DIR = /root/driver/kernelobj-m += module_test.oall:make -C $(KERN_DIR) M=`pwd` modules cp:cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: clean
clean:make -C $(KERN_DIR) M=`pwd` modules clean
(1)KERN_DIR,表示用来编译这个模块的内核源码树的目录;
(2)obj-m += module_test.o,-m表示将module_test.c文件编译成一个单独的模块;
(3)make -C $(KERN_DIR) M=`pwd` modules,此命令用来编译模块。
- 利用make -C $(KERN_DIR)进入指定的内核源码树目录,然后在源码目录树下,借用内核源码中定义的模块编译规则,去编译该模块modules;
- 其实就是make modules,modules是内核中的一个目标;中间的是参数,表明到某个目录下进行编译,编译完后回到当前目录(反引号表示它是一个命令)。
(4)编译完成后,把生成的文件拷贝到当前目录下,完成编译。
(5)make clean ,用来清除编译痕迹。
(5)总结
- 模块的makefile非常简单,本身并不能完成模块的编译,而是通过make -C进入到内核源码树下,借用内核源码的体系来完成模块的编译链接。
- 此Makefile非常模式化,(3)和(4)(5)是永远不用动的,只有(1)和(2)需要动。
字符设备驱动基础篇1——简单的驱动源码分析相关推荐
- AI作曲基础-Python编程作曲软件篇-FoxDot文档及源码分析-官方教程01
AI作曲基础-Python编程作曲软件篇-FoxDot文档及源码分析-官方教程01 前言 本系列系列目录放在文尾: 本系列是AI作曲的基础,暂时和AI关系不大,但尤为重要: 借助FoxDot,从文档分 ...
- 《微信小程序-进阶篇》Lin-ui组件库源码分析-列表组件List(一)
大家好,这是小程序系列的第二十篇文章,在这一个阶段,我们的目标是 由简单入手,逐渐的可以较为深入的了解组件化开发,从本文开始,将记录分享lin-ui的源码分析,期望通过对lin-ui源码的学习能加深组 ...
- java刷卡计时计次源码美萍_Java 定时调配 Timer 类和定任务 TimerTask 类(一篇详细且完整的源码分析以及四种简单的使用方法)...
前言 在我们日常生活中,我们常常会遇到有关计时器的事情.如商城类项目会在某年某月某日某时某分某秒进行特价活动,那么当时间到达这个时间点上的时候该事件就会触发. 1.Timer 类构造函数摘要 1 Ti ...
- input子系统基础之按键4——输入核心层源码分析
以下内容源于朱有鹏<物联网大讲堂>课程的学习,如有侵权,请告知删除. 主要分析input.c文件 一.输入核心层源码分析1 1.核心模块注册:input_init函数 (1)class_r ...
- SDIO_WiFi驱动学习之SDIO架构介绍及源码分析
一.引言 因为WiFi驱动比较复杂,所以WiFi驱动的博客将多分几篇来写. 本篇博客主要介绍Linux下的SDIO架构及源码分析. 本文部分内容摘抄自网络,若有侵权,请联系删除. 二.SDIO WiF ...
- AndroidVideoCache简单使用及源码分析
对于视频播放,如果需要用到缓存,AndroidVideoCach是一个不错的选择,该项目地址: https://github.com/danikula/AndroidVideoCache 优缺点: 优 ...
- android 弹簧震动动画,Android-Rebound(弹簧系统-让动画不再僵硬)的简单使用与源码分析...
综述:Rebound 通过胡克定律,实现的一个类似"弹簧"动画效果的第三方工具包. 单独使用 Spring spring = SpringSystem.create().creat ...
- 5.2.4.最简单的模块源码分析3
printk:printk内核态,printf用户态(没什么用) 打印级别内核把级别比命令行低的所有消息显示在终端(console)上.但是所有信息都会记录在printk的"ring buf ...
- Android基础-Facebook Rebound 弹性动画库 源码分析
Facebook Rebound 弹性动画库 源码分析 设计的时候老是闲动画太生硬,于是找到了这个弹性动画.这个弹性动画是facebook开源的,Rebound项目地址:https://github. ...
最新文章
- 如何用python画数据图-用Python绘制地理图
- 新基建7大产业链约500家企业图谱!
- Java消息中间件(activeMQ)
- 在Ubuntu Server 12.04 LTS上搭建可远程访问的Postgresql 9.1环境
- 洛谷P4630 [APIO2018] Duathlon 铁人两项 【圆方树】
- CL_CRM_REPORT_ACCRULE_ONEORDER
- [Buzz.Today]2011.06.26
- 计算机怎么查看U盘品牌,如何查看电脑u盘使用
- [Leedcode][JAVA][第236题][二叉树的公共祖先][后序遍历][BFS]
- 响应式设计之 —— 视口
- 基于JavaWeb实现的研究室综合系统
- 分子动力学模拟需要掌握的理论、语言和软件
- Word未保存文档恢复
- GRU 详解+实战(生成汪峰感觉的歌词)
- Centos 7.4 防火墙关闭命令
- Matlab:数模13-多元回归分析模型
- 使用Python API实现TRT版BN/hswish/Silu等算子
- Cesium加载GLB和GLTF模型文件踩坑实录
- 想学3D建模,去哪儿学比较好
- python练习(4)
热门文章
- 【PKUSC2019】线弦图【计数】【树形DP】【分治FFT】
- objectdatasouce的温故
- 快速判断数组中每个对象同一属性值是否相同
- WinForm 清空界面控件值的小技巧
- FPGA 状态机设计
- 本地浏览器缓存sessionStorage(临时存储) localStorage(长期存储)的使用
- GreenSock (TweenMax) 动画案例(二)
- To install 64-bit ODBC drivers
- 1682: [Usaco2005 Mar]Out of Hay 干草危机
- 更新了一个新版本的editplus 语法文件(for nagios)