【Linux驱动】字符设备驱动相关宏 / 函数介绍(module_init、register_chrdev)
驱动运行有两种方式:
- 方式一:直接编译到内核,Linux内核启动时自动运行驱动程序
- 方式二:编译成模块,使用 insmod 命令加载驱动模块
我们在调试的时候,采用第二种方式是最合适的,每次修改驱动只需要编译一下驱动代码,然后使用 insmod 命令加载驱动模块( .ko 文件 ),不需要编译整个 Linux 代码。
下面以第二种方式为例,来了解一下编写字符驱动模块需要用到哪些宏或者函数。
目录
一、驱动模块的加载 / 卸载 —— module_init / module_exit
二、字符设备的注册 / 注销 —— register_chrdev
三、添加实现设备的具体操作函数
四、添加 LICENSE 和作者信息 —— MODULE_LICENSE
五、动态分配 / 释放设备号 —— alloc_chrdev_region
六、总结:字符驱动模板
一、驱动模块的加载 / 卸载 —— module_init / module_exit
驱动模块被加载,可能需要有一些初始化,但是我们要如何让内核去调用我们写的初始化函数呢,这就需要内核提供的宏: module_init 、module_exit
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
module_init
当前模块被加载到内核时,会自动调用 xxx_init 函数,这里的 module_init 就有点像是在给内核传递 xxx_init 函数的函数指针。
module_exit
同理,当我们想卸载模块时,可能会有一些收尾工作要做,比如关闭某个引脚。当前模块被卸载的时候,会自动调用 xxx_exit 函数。
// 使用 __init 修饰
static int __init chrdevbase_init(void)
{/* 驱动入口实现 */return 0;
}// 使用 __exit 修饰
static void __exit chrdevbase_exit(void)
{/* 驱动出口实现 */
}
module_init(chrdevbase_init); //注册模块加载函数
module_exit(chrdevbase_exit); //注册模块卸载函数
二、字符设备的注册 / 注销 —— register_chrdev
注册字符设备的目的是在你的驱动被加载到内核时,会在 /dev/ 目录下生成你的字符设备文件,应用程序读写这个字符设备文件时,就会自动调用驱动中的 read / write 函数。
这样就建立起了应用程序和驱动之间的基本联系。同理,注销时,/dev/ 下对应文件会被删除。register_chrdev、unregister_chrdev 是早期注册字符设备的函数,函数声明如下:
// 字符设备注册
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
// 字符设备注销
static inline void unregister_chrdev(unsigned int major, const char *name);
register_chrdev 参数解析
major:主设备号,Linux 下每个设备都有一个设备号,可以使用cat /proc/devices 查看已经使用的设备号。静态分配时注意不要和已有设备号重复;如果要动态分配,可以参考第六部分《动态分配设备号》

name:设备名称,当驱动注册成功以后,在 /dev/ 下显示的名称
fops:当前驱动的操作函数集合,函数声明放在file_operations结构体中,这里要传入的就是file_operations结构体指针。
unregister_chrdev 参数解析
major:要注销的设备对应的主设备号。
name:要注销的设备对应的设备名。
三、添加实现设备的具体操作函数
上面注册字符设备的第三个参数是 fops ,这里要传入的就是你要为当前设备注册哪些操作函数,比如定义了下面的结构体,结构体对象为 chrdevbase_fops,同时要为该结构体注册 open、read、write 、close 函数:
- open 函数:chrdevbase_open
- read 函数:chrdevbase_read
- write 函数:chrdevbase_write
- close 函数:chrdevbase_release
/** 设备操作函数结构体*/
static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE, // 注册当前结构体的指针对象.open = chrdevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};
既然这里已经注册了 open、read、write 、close 函数,那么你需要自己去实现这些操作函数,关于这些函数的声明,可以在 include/linux/fs.h 的第 1588 行找到。
/* 打开设备 */
static int chrdevbase_open(struct inode *, struct file *)
{/* 用户实现具体功能 */return 0;
}/* 读取设备 */
static ssize_t chrdevbase_read(struct file *, char __user *, size_t, loff_t *);
{/* 用户实现具体功能 */return 0;
}/* 向设备写数据 */
static ssize_t chrdevbase_write(struct file *, const char __user *, size_t, loff_t *)
{/* 用户实现具体功能 */return 0;
}/* 关闭设备 */
static int chrdevbase_release (struct inode *, struct file *)
{/* 用户实现具体功能 */return 0;
}
四、添加 LICENSE 和作者信息 —— MODULE_LICENSE
驱动模块中必须添加 LICENSE 信息,不然编译会报错,作者信息可有可无。LICENSE 和作者信息的添加使用 如下两个函数:
MODULE_LICENSE("GPL"); //添加模块 LICENSE 信息
MODULE_AUTHOR("作者名"); //添加模块作者信息
五、动态分配 / 释放设备号 —— alloc_chrdev_region
动态分配设备号 —— alloc_chrdev_region
函数声明如下:
int alloc_chrdev_region(dev_t *dev, \unsigned baseminor, \unsigned count, \const char *name);
dev:输出型参数。保存申请到的设备号
baseminor:次设备号起始地址。alloc_chrdev_region 可以申请一段连续的多个设备号,这 些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递 增。一般 baseminor 为 0,也就是说次设备号从 0 开始。
count:要申请的设备号数量
name:设备名字
动态释放设备号 —— unregister_chrdev_region
注销字符设备之后要释放掉设备号。函数声明如下:
void unregister_chrdev_region(dev_t from, unsigned count);
from:要释放的设备号。(这里指的是主设备号,并非上面次设备号的起始地址)
count:表示从 from 开始,要释放的设备号数量。
六、总结:字符驱动模板
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>#define CHRDEVBASE_MAJOR 200 /* 主设备号 */
#define CHRDEVBASE_NAME "chrdevbase" /* 设备名 *//* 打开设备 */
static int chrdevbase_open(struct inode *, struct file *)
{/* 用户实现具体功能 */return 0;
}/* 读取设备 */
static ssize_t chrdevbase_read(struct file *, char __user *, size_t, loff_t *);
{/* 用户实现具体功能 */return 0;
}/* 向设备写数据 */
static ssize_t chrdevbase_write(struct file *, const char __user *, size_t, loff_t *)
{/* 用户实现具体功能 */return 0;
}/* 关闭设备 */
static int chrdevbase_release (struct inode *, struct file *)
{/* 用户实现具体功能 */return 0;
}/** 设备操作函数结构体*/
static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE, .open = chrdevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};/** @description : 驱动入口函数 * @param : 无* @return : 0 成功;其他 失败*/
static int __init chrdevbase_init(void)
{int retvalue = 0;/* 注册字符设备驱动 */retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);if(retvalue < 0){printk("chrdevbase driver register failed\r\n");}printk("chrdevbase init!\r\n");return 0;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit chrdevbase_exit(void)
{/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\r\n");
}/* * 将上面两个函数指定为驱动的入口和出口函数 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);/* * LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author_name");相关文章:
【Linux驱动】字符设备驱动相关宏 / 函数介绍(module_init、register_chrdev)
驱动运行有两种方式: 方式一:直接编译到内核,Linux内核启动时自动运行驱动程序方式二:编译成模块,使用 insmod 命令加载驱动模块 我们在调试的时候,采用第二种方式是最合适的,每次修改驱动只需…...
axios解决跨域问题
Vue3中使用axios访问聚合的天气API,出现跨域问题,需要在前端进行一些配置: 首先是修改vue.config.js: const { defineConfig } require(vue/cli-service) module.exports defineConfig({transpileDependencies: true,devServe…...
R语言作图——热图聚类及其聚类结果输出
代码 不多说了,做个记录,代码如下。 library(pheatmap) library(RColorBrewer) # args commandArgs(TRUE) betafile "twist_common_panel_434.csv" infofile "twist_common_panel_434.txt" title "twist_common_panel&qu…...
Tomcat优化
Tomcat优化 Tomcat默认安装下的缺省配置并不适合生产环境,它可能会频繁出现假死现象需要重启,只有通过不断压测优化才能让它最高效率稳定的运行。优化主要包括三方面,分别为操作系统优化(内核参数优化),Tom…...
我的GIT练习TWO
目录 前言 GIT安装教程 Git作者 GIT优点 GIT缺点 为什么要使用 Git GIT练习TWO C1 C2 C3 C4 C5 C6 C7 总结 前言 Git 是一个分布式版本控制及源代码管理工具;Git 可以为你的项目保存若干快照,以此来对整个项目进行版本管理 GIT安装教程 点击进入查看教程…...
个人器件库整理
样品本 包含如下: 电容器件: 元件值封装备注钽电容47uF 10V1206钽电容10uF 10V1206电容10uF 10% 10V0603X5R,CL10A106KP8NNNC 元件值封装备注100nF电容50V,10%0603 电阻器件: 元件值封装备注75 Ω \Omega Ω…...
javascript——内存管理
JavaScript内存管理是Web开发中的一个重要主题。正确管理内存可以提高应用程序的性能和稳定性。本文将介绍JavaScript中的内存管理概念、常见的内存泄漏问题以及一些有效的内存管理技巧。 什么是JavaScript内存管理? JavaScript具有自动内存管理机制,开…...
Qt5.15.2安卓Android项目开发环境配置
1、Qt Creator 4.11.2 官方下载:https://download.qt.io/archive/qtcreator/4.11/4.11.2/ 镜像下载:https://mirrors.cloud.tencent.com/qt/archive/qtcreator/4.11/4.11.2/ 2、Qt 5.15.2 Android 官方更新器内部下载 参考:https://blog…...
第四十三章 弹跳训练2(灵识扫描)
“再不脱离便会陷死在里面。”这个声音似乎来自脑海深处某个隐秘角落。 双眼一睁,灵识退去,空空的头壳兀自嗡嗡作响,一股说不清道不明的失落感笼罩全身,似要将自己拖入抑郁的谷底。 不!没什么好失落沮丧的!…...
【location对象的方法,history对象,navigator--BOM】
location对象的方法 location.assign()//跟href一样,可以跳转页面(也称重定向页面) location.replace()//替换当前页面,因为不记录历史,所以不能后退页面 location.reload()//重新加载页面,相当于刷新按钮或…...
论文笔记:Normalizing Flows for Probabilistic Modeling and Inference
Abstract 正则流(Normalizing flows)提供了一种通用的机制来定义富有表达力的概率分布,只需要指定一个(通常简单的)基础分布和一系列可逆变换。 Intraduction 正则流通过将简单的密度通过一系列变换来产生更丰富、可…...
java 异常类介绍
Java 异常(Exception)是指在程序运行期间出现的错误或异常情况。Java 异常处理机制允许程序在出现异常情况时进行处理,避免程序崩溃或出现不可预知的错误 一、Java 异常的概念 Java 异常是指程序在运行期间出现的错误或异常情况。Java 异常…...
shiro 550 反序列化rce
Apach shiro 是一款开源安全框架,提供身份验证,授权,会话管理等。 shiro 550 反序列化漏洞rce 通关利用它反序列化的漏洞直接执行rce 加密的用户信息序列化后储存在名为remenber -me的cooike中。攻击者可以使用shiro默认密钥伪造cooike&am…...
【C++】---模板初阶(超详练气篇)
个人主页:平行线也会相交💪 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【C之路】💌 本专栏旨在记录C的学习路线,望对大家有所帮助🙇 希望我们一起努力、成长&…...
windows系统典型漏洞分析
内存结构 缓冲区溢出漏洞 缓冲区溢出漏洞就是在向缓冲区写入数据时,由于没有做边界检查,导致写入缓冲区的数据超过预先分配的边界,从而使溢出数据覆盖在合法数据上而引起系统异常的一种现象。 ESP、EPB ESP:扩展栈指针(…...
WPF开发txt阅读器:需求分析和文件读写
文章目录 需求分析读取文本文件保存文本文件 需求分析 尽管现在比较主流的阅读格式已经是epub, modi之类的,但txt的使用范围要远比前两者广泛,所以做一个txt阅读器还是有必要的。 但是对于书籍阅读而言,纯文本不包含目录信息,这…...
C++服务器框架开发9——日志系统LogFormatter_4/各个类的关系梳理/std::function/std::get
该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见:[C高级教程]从零开始开发服务器框架(sylar) 上一篇:C服务器框架开发8——日志系统LogFormatter_3/override/宏定义优化switchcase结构 C服务器框架开发9——日志系统LogFormatt…...
arm平台上的MNN编译与运行
0.成果物 直接获取成果物见:https://download.csdn.net/download/u012824853/87867665 以下为编译、运行过程 1.编译准备 在GitHub - alibaba/MNN: MNN is a blazing fast, lightweight deep learning framework, battle-tested by business-critical use cases …...
python 编译安装指定版本 for linux
python环境是linux中必备的,部分发行版会自带python,有时候需要安装手动安装 注意:如果需要多个版本并存,建议使用conda环境,如果自己配置多版本,需要用多个软链接 conda环境,可以参考&#x…...
在Linux系统下基于Docker搭建Redis集群
创建镜像 #部署Redis集群,该集群有3个节点; --cluster-enabled yes允许启用集群; docker create --name redis-node--01 --net host -v /data/redis-data/node1:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file redis-node--01.conf --port 6379…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
小智AI+MCP
什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析:AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github:https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...
基于谷歌ADK的 智能产品推荐系统(2): 模块功能详解
在我的上一篇博客:基于谷歌ADK的 智能产品推荐系统(1): 功能简介-CSDN博客 中我们介绍了个性化购物 Agent 项目,该项目展示了一个强大的框架,旨在模拟和实现在线购物环境中的智能导购。它不仅仅是一个简单的聊天机器人,更是一个集…...
解决MybatisPlus使用Druid1.2.11连接池查询PG数据库报Merge sql error的一种办法
目录 前言 一、问题重现 1、环境说明 2、重现步骤 3、错误信息 二、关于LATERAL 1、Lateral作用场景 2、在四至场景中使用 三、问题解决之道 1、源码追踪 2、关闭sql合并 3、改写处理SQL 四、总结 前言 在博客:【写在创作纪念日】基于SpringBoot和PostG…...
