当前位置: 首页 > news >正文

Ubuntu18.04添加内核模块(字符设备)

Ubuntu18.04添加内核模块(字符设备)

虚拟机Ubuntu18.04(内核版本linux-5.4.0-135-generic)

参考

嵌入式Linux驱动开发(一)——字符设备驱动框架入门

1 编译内核模块

  • 创建字符设备代码文件char_dev.c
#include <linux/init.h>     //定义了module_init
#include <linux/module.h>   //最基本的头文件,其中定义了MODULE_LICENSE这一类宏
#include <linux/fs.h>       // file_operations结构体定义在该头文件中
#include <linux/device.h>    //class、class_device结构体的定义位置static const char* devive_name = "first_driver";  //  定义设备名
static struct class *first_class;    //定义class结构体
static struct device *first_dev;    //定义device结构体//定义了open函数
static int first_drv_open (struct inode *inode, struct file *file)
{printk("open\n");return 0;
}//定义了write函数
static ssize_t first_drv_write (struct file *file, const char __user *buf, size_t size, loff_t * ppos)
{printk("write\n");return 0;
}//在file_operations中注册open和write函数
static struct file_operations first_drv_fo =
{.owner  =  THIS_MODULE,//将对应的函数关联在file_operations的结构体中.open   =  first_drv_open,      .write  =  first_drv_write,
};static int dev_id = 0;     //初始化的设备号0
//init驱动的入口函数
static int __init first_drv_init(void)
{      //注册设备,实际是将file_operations结构体放到内核的制定数组中,以便管理//在register_chrdev中制定dev_id作为主设备号,若dev_id为0则自动分配一个主设备号dev_id = register_chrdev(dev_id, devive_name , &first_drv_fo);first_class = class_create(THIS_MODULE, "first_drv");    //初始化class结构体,指定设备文件名first_dev = device_create(first_class, NULL, MKDEV(dev_id, 0), NULL, "first_drv");// 根据class来初始化device,会创建出对应的设备文件 /dev/first_drvprintk("init\n");return 0;
}//驱动的出口函数
static void __exit first_drv_exit(void)
{printk("exit\n");unregister_chrdev(dev_id, devive_name);  //卸载设备,实际是将file_operations结构体从内核维护的相关数组中以主设备号作为索引删除device_unregister(first_dev); // 后创建的先卸载class_destroy(first_class);
}//内核将通过这个宏,来直到这个驱动的入口和出口函数
module_init(first_drv_init);  
module_exit(first_drv_exit);MODULE_AUTHOR("Ethan Lee <4128127@qq.com>");
MODULE_LICENSE("GPL");  //指定协议
  • 同目录下创建Makefile文件:
obj-m += char_dev.o
KERN_DIR=/usr/src/linux-headers-5.4.0-135-genericall:make -C ${KERN_DIR} M=${shell pwd} modulesclean:rm -f *.ko *.o *.mod.o *.mod.c *.sysvers
  • 编译:
make

2 加载内核模块

sudo insmod char_dev.ko
  • 查看是否添加成功:
cat /proc/devices

结果如下:

Character devices:...189 usb_device204 ttyMAX226 drm240 first_driver  #这里是我们添加的模块241 aux242 hidraw
...Block devices:7 loop8 sd9 md11 sr65 sd66 sd
...
  • 创建一个测试程序char_dev_test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>int main(int argc, char **argv)
{int fd;      //声明设备描述符int val = 1;  //随便定义变量传入到fd = open("/dev/first_drv",  O_RDWR);  //根据设备描述符打开设备if(fd < 0)          //打开失败printf("can't open\n");  write(fd, &val, 4);  //根据文件描述符调用writereturn 0;
}
  • 编译并运行测试程序:
gcc char_dev_test.c -o char_dev_testsudo ./char_dev_test
  • 查看结果:
$ dmesg  | tail -10
[ 1746.094412] CPU3 has been hot-added
[ 1746.094945] CPU4 has been hot-added
[ 1746.097525] CPU5 has been hot-added
[ 1746.098038] CPU6 has been hot-added
[ 1746.098708] CPU7 has been hot-added
[ 2861.264107] char_dev: loading out-of-tree module taints kernel.
[ 2861.264142] char_dev: module verification failed: signature and/or required key missing - tainting kernel
[ 2861.264398] init
[ 3070.234439] open
[ 3070.234441] write

3.卸载内核模块

sudo rmmod char_dev
  • 查看结果:
$ dmesg  | tail -1
[ 4282.264114] exit

相关文章:

  • CCF-202012-2:期末预测之最佳阈值
  • Linux chattr命令教程:如何改变文件或目录的属性(附案例详解和注意事项)
  • 2、如何使用ETAS INCA进行发动机标定
  • wsl-oraclelinux 安装 cuda
  • Quartz的分布式功能化设计
  • git 版本回退
  • 服务器段的连接端口和监听端口编程实现
  • 【实战】一、Jest 前端自动化测试框架基础入门(三) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(三)
  • 【机器学习300问】33、决策树是如何进行特征选择的?
  • mutex 和 channel 哪一个工作效率更高?
  • Redis 订阅发布(Pub/Sub) 详解 如何使用订阅发布
  • 模拟电子技术实验(二)
  • CentOS7 利用remi yum源安装php8.1
  • 运维自动化之ANSIBLE
  • 【位运算】【脑筋急转弯】2749. 得到整数零需要执行的最少操作数
  • 分享一款快速APP功能测试工具
  • 【mysql】环境安装、服务启动、密码设置
  • HashMap剖析之内部结构
  • MySQL数据库运维之数据恢复
  • oldjun 检测网站的经验
  • Redis在Web项目中的应用与实践
  • 初识 webpack
  • 对象引论
  • ------- 计算机网络基础
  • 简析gRPC client 连接管理
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 通过npm或yarn自动生成vue组件
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (12)Hive调优——count distinct去重优化
  • (java)关于Thread的挂起和恢复
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • .NET 5种线程安全集合
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .net Signalr 使用笔记
  • .NET 发展历程
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .Net环境下的缓存技术介绍
  • .NET中的Exception处理(C#)
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • [ solr入门 ] - 利用solrJ进行检索
  • [Android Pro] AndroidX重构和映射
  • [Android Studio 权威教程]断点调试和高级调试
  • [Android Studio] 开发Java 程序
  • [ASP.NET MVC]如何定制Numeric属性/字段验证消息
  • [C#7] 1.Tuples(元组)
  • [IDF]聪明的小羊
  • [IE技巧] 使IE8以单进程的模式运行
  • [java后端研发]——文件上传与下载(2种方式)