《linux系统内核设计与实现》-实现最简单的字符设备驱动
开发linux内核驱动需要以下4个步骤:
1 编写hello驱动代码
驱动代码如下 helloDev.c,这是一个最小、最简单的驱动,去掉了其他的不相干代码,尽量让大家能了解驱动本身。
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>#define BUFFER_MAX (10) // buff大小
#define OK (0)
#define ERROR (-1)struct cdev *gDev; // 字符设备结构体指针
struct file_operations *gFile; // 文件操作结构体指针
dev_t devNum; // 设备号
unsigned int subDevNum = 1; // 注册设备的数量int reg_major = 232; // 主设备号
int reg_minor = 0; // 次设备号
char *buffer; // 缓冲区/*** printk 是内核中用于输出调试信息、错误信息和其他日志信息的函数。* 它将消息输出到内核日志缓冲区,这些日志可以通过 dmesg 命令查看* KERN_INFO 是一个宏:信息性消息
*/// hello_open函数:打开文件
int hello_open(struct inode *p, struct file *f)
{printk(KERN_INFO "hello_open\r\n");return 0;
}// hello_write函数
ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
{printk(KERN_INFO "hello_write\r\n");return 0;
}// hello_read函数
ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
{printk(KERN_INFO "hello_read\r\n");return 0;
}int hello_init(void)
{devNum = MKDEV(reg_major, reg_minor); // 根据主次设备号,生成devNum,唯一标识设备// 把设备号注册到内核中。从devNum开始注册subDevNum个设备。if (OK == register_chrdev_region(devNum, subDevNum, "helloworld")) {printk(KERN_INFO "register_chrdev_region ok \n");} else {printk(KERN_INFO "register_chrdev_region error n");return ERROR;}printk(KERN_INFO " hello driver init \n");// 内核模块成功分配并初始化了一个字符设备结构gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);// 文件操作gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);// 赋值。回调函数gFile->open = hello_open;gFile->read = hello_read;gFile->write = hello_write;gFile->owner = THIS_MODULE;// 建立联系:通过这两行代码,驱动程序完成了字符设备的初始化和注册,使得字符设备可以被用户进程打开、读取和写入。cdev_init(gDev, gFile); // 初始化字符设备结构体gDev,并将其与文件操作结构体gFile关联起来。cdev_add(gDev, devNum, 1); // 将初始化好的字符设备gDev 添加到内核字符设备层,使其成为一个有效的字符设备,可以被用户空间访问和操作return 0;
}// 驱动退出函数
void __exit hello_exit(void)
{printk(KERN_INFO " hello driver exit \n");cdev_del(gDev); // 删除字符设备kfree(gFile); // 释放内存kfree(gDev); // 释放内存unregister_chrdev_region(devNum, subDevNum); // 注销设备号return;
}module_init(hello_init); // 声明驱动的入口函数。执行insmod的时候调用
module_exit(hello_exit); // 声明驱动的退出函数。执行rmmod的时候调用
MODULE_LICENSE("GPL"); // 指定模块的许可证
有了驱动文件之后,还需要一个Makefile才能把驱动编译出来
2 编写makefile
ifneq ($(KERNELRELEASE),)
obj-m := helloDev.o
else
PWD := $(shell pwd) # 获取当前目录路径,并赋值给变量 PWD
# KDIR:= /lib/modules/4.4.0-31-generic/build
# KDIR:= /home/winter/linux-4.9.229
KDIR := /lib/modules/`uname -r`/build # 指定内核构建目录
all:make -C $(KDIR) M=$(PWD)
clean:rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~
endif
# $(KERNELRELEASE) 是内核构建系统设置的变量,当通过内核构建系统调用此 Makefile 时,这个变量会被定义。
# -C $(KDIR):切换到内核构建目录
# M=$(PWD):指定模块源码目录为当前目录
# make -C $(KDIR) M=$(PWD):调用内核构建系统
# 这段 Makefile 通过条件分支来区分内核构建环境和用户构建环境,在用户环境中调用内核构建系统进行模块编译,并提供了清理编译生成文件的功能。
linux应用层程序在编译的时候,需要链接c运行时库和glibc库。那驱动需不需要呢?
3 编译和加载hello驱动
驱动也需要,但是驱动不能链接和使用应用层的任何lib库,驱动需要引用内核的头文件和函数。所以,编译的时候需要指定内核源码的地址。为了开发方便,也可以安装内核开发包,之后引用这个内核开发包的目录也可以。本例为:/lib/modules/4.4.0-31-generic/build
make
编译出来的驱动文件,名称为:helloDev.ko
接下来把这个驱动加载到内核
# 清零内核日志
dmesg -c
# 执行
insmod helloDev.ko
# 再看内核日志
dmesg
# 查看驱动
lsmod
# 卸载驱动
rmmod helloDev.ko
# 再查看驱动,无
4 编写应用程序测试hello驱动test.c
/*************************************************************************> File Name: test.c> Author: Winter> Created Time: 2024年05月17日 星期五 21时03分06秒************************************************************************/#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>#define DATA_NUM (64)
int main(int argc, char *argv[])
{int fd, i;int r_len, w_len;fd_set fdset;char buf[DATA_NUM]="hello world";memset(buf,0,DATA_NUM);fd = open("/dev/hello", O_RDWR);printf("%d\r\n",fd);if(-1 == fd) {perror("open file error\r\n");return -1;} else {printf("open successe\r\n");}w_len = write(fd,buf, DATA_NUM);r_len = read(fd, buf, DATA_NUM);printf("%d %d\r\n", w_len, r_len);printf("%s\r\n",buf);return 0;
}
编译执行。因为没有/dev/hello这个设备文件,所以打开失败
需要创建设备文件
mknod
man mknod
# 名字 类型 【主设备号 次设备号】
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
mknod /dev/hello c 232 0# 查看
ls -l /dev/hello# 再执行测试程序
./test
有调用日志
这个返回值是驱动部分对应的返回值
5 一些命令
dmesg -c # 清零内核日志
insmod helloDev.ko # 插入(加载)Linux内核模块的命令
dmesg # dmesg 命令用于查看和管理 Linux 内核的环形缓冲区中的消息
lsmod # 查看驱动
rmmod helloDev.ko # 卸载驱动# 创建设备文件:名字 类型 【主设备号 次设备号】
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
mknod /dev/hello c 232 0
相关文章:

《linux系统内核设计与实现》-实现最简单的字符设备驱动
开发linux内核驱动需要以下4个步骤: 1 编写hello驱动代码 驱动代码如下 helloDev.c,这是一个最小、最简单的驱动,去掉了其他的不相干代码,尽量让大家能了解驱动本身。 #include <linux/module.h> #include <linux/mod…...

【MotionCap】pycharm 远程在wsl2 ubuntu20.04中root的miniconda3环境
pycharm wsl2 链接到pycharmsbin 都能看到内容,/root 下内容赋予了zhangbin 所有,pycharm还是看不到/root 下内容。sudo 安装了miniconda3 引发了这些问题 由于是在 root 用户安装的miniconda3 所以安装路径在/root/miniconda3 里 这导致了环境也是root用户的,会触发告警 WA…...

[BJDCTF 2nd]简单注入
sqlsqlsqlsqlsql又来喽 过滤了单双引号,等于符号,还有select等,但是这里没有二次注入 。扫描发现hint.txt 看出题人的意思是,得到密码即可获得flag。 select * from users where username$_POST["username"] and passw…...

java项目的一些功能(完善登录功能、注册接口参数校验、完善分页查询、完善日期格式、更新文章分类和添加文章分类的分组校验、自定义校验、文件上传 )
目录 完善登录功能 注册接口参数校验 完善分页查询 完善日期格式 更新文章分类和添加文章分类的分组校验 编辑 自定义校验 文件上传 完善登录功能 对前端传过来的明文密码进行md5加密处理 password DigestUtils.md5DigestAsHex(password.getBytes()); 这样既可 注…...

Mac安装AndroidStudio连接手机 客户端测试
参考文档:https://www.cnblogs.com/andy0816/p/17097760.html 环境依赖 需要java 1.8 java安装 略 下载Android Studio 地址 下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 本机对应的包进行下载 安装过程 https://www.cnblogs.c…...
Git 完整的提交规范教程
约定式提交规范 本文中的关键词 “必须(MUST)”、“禁止(MUST NOT)”、“必要(REQUIRED)”、“应当(SHALL)”、“不应当(SHALL NOT)”、“应该(S…...

【TB作品】51单片机 Proteus仿真 00001仿真实物PID电机调速系统
实验报告:Proteus 仿真 PID 电机调速系统 一、实验背景 PID(比例-积分-微分)控制器广泛应用于工业控制系统中,用于调节各种物理变量。本实验的目的是通过 Proteus 仿真软件设计并实现一个 PID 电机调速系统,以控制直…...

【LInux】从动态库的加载深入理解页表机制
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...

IDEA与通义灵码的智能编程之旅
1 概述 本文主要介绍在IDEA中如何安装和使用通义灵码来助力软件编程,从而提高编程效率,创造更大的个人同企业价值。 2 安装通义灵码 2.1 打开IDEA插件市场 点击IDEA的设置按钮,下拉选择Plugins,如下: 2.2 搜索通义灵码 在搜索框中输入“通义灵码”,如下: 2.3 安…...

多表查询sql
概述:项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,分为三种: 一对多多对多一对一 一、多表关系 一对多 案例:部门与…...

移动端UI风格营造舒适氛围
移动端UI风格营造舒适氛围...

摸鱼大数据——Spark SQL——DataFrame详解一
1.DataFrame基本介绍 DataFrame表示的是一个二维的表。二维表,必然存在行、列等表结构描述信息表结构描述信息(元数据Schema): StructType对象字段: StructField对象,可以描述字段名称、字段数据类型、是否可以为空行: Row对象列: Column对象ÿ…...

【Java探索之旅】初识多态_概念_实现条件
文章目录 📑前言一、多态1.1 概念1.2 多态的实现条件 🌤️全篇总结 📑前言 多态作为面向对象编程中的重要概念,为我们提供了一种灵活而强大的编程方式。通过多态,同一种操作可以应用于不同的对象,并根据对象…...
[Day 26] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
數據科學與AI的整合應用 數據科學(Data Science)和人工智能(AI)在現代技術世界中扮演著至關重要的角色。兩者的整合應用能夠為企業和研究人員提供強大的工具,以更好地理解、預測和解決各種複雜的問題。本文將深入探討…...

算法 —— 滑动窗口
目录 长度最小的子数组 无重复字符的最长子串 最大连续1的个数 将x减到0的最小操作数 找到字符串中所有字母异位词 最小覆盖子串 长度最小的子数组 sum比target小就进窗口,sum比target大就出窗口,由于数组是正数,所以相加会使sum变大&…...

【设计模式】工厂模式(定义 | 特点 | Demo入门讲解)
文章目录 定义简单工厂模式案例 | 代码Phone顶层接口设计Meizu品牌类Xiaomi品牌类PhoneFactory工厂类Customer 消费者类 工厂方法模式案例 | 代码PhoneFactory工厂类 Java高级特性---工厂模式与反射的高阶玩法方案:反射工厂模式 总结 其实工厂模式就是用一个代理类帮…...
Linux之计划和日志
计划任务 计划任务概念解析 在Linux操作系统中,除了用户即时执行的命令操作以外,还可以配置在指定的时间、指定的日期执行预先计划好的系统管理任务(如定期备份、定期采集监测数据)。通过安装at和crontabs这两个系统服务实现一次性、周期性计划任务的功能,并分别通过at、…...

C++ 多态篇
文章目录 1. 多态的概念和实现1.1 概念1.2 实现1.2.1 协变1.2.2 析构函数1.2.3 子类虚函数不加virtual 2. C11 final和override3.1 final3.2 override 3. 函数重载、重写与隐藏4. 多态的原理5. 抽象类6.单继承和多继承的虚表6.1 单继承6.2 多继承 7. 菱形继承的虚表(了解)7.1 菱…...

【LVGL-SquareLine Studio】
LVGL-SquareLine Studio ■ SquareLine Studio-官网下载地址■ SquareLine Studio-参考博客■ SquareLine Studio-安装■ SquareLine Studio-汉化■ SquareLine Studio-■ SquareLine Studio-■ SquareLine Studio-■ SquareLine Studio-■ SquareLine Studio- ■ SquareLine S…...
mysqli 与mysql 区别和联系, 举例说明
mysqli是一种PHP的扩展,用于与MySQL数据库进行交互。它提供了一套面向对象的接口,可以更方便地操作数据库。MySQL是一种关系型数据库管理系统,用于存储和管理数据。 区别: mysqli是MySQL的扩展,而不是单独的数据库管…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...