驱动-定时-秒-字符设备

文章目录

  • 目的
  • 相关资料参考
  • 实验
    • 驱动程序-timer_dev.c
    • 编译文件-Makefile
    • 测试程序-timer.c
      • 分析
  • 加载驱动-运行测试程序
  • 总结


目的

通过定时器timer_list、字符设备、规避竞争关系-原子操作,综合运用 实现一个程序,加深之前知识的理解。

  • 实现字符设备驱动框架, 自动生成设备节点。
  • 根据上一小节学到的知识, 实现秒计时。
  • 通过原子变量来记录递增的秒数, 避免竞争的发生。
  • 通过用户空间和内核空间的数据交换, 将记录的秒数传递到应用空间, 并通过应用程
    序打印出来

相关资料参考

驱动-原子操作
驱动-Linux定时-timer_list

实验

驱动程序-timer_dev.c


#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>struct device_test
{dev_t  dev_num;  //定义dev_t类型变量来表示设备号int major,minor; //定义int 类型的主设备号和次设备号struct cdev cdev_test;   //定义字符设备struct class *class;   //定义结构体变量class 类struct device *device;  // 设备int sec;               //秒};
atomic64_t v = ATOMIC_INIT(0);//定义原子类型变量v,并定义为0struct device_test dev1;  static void function_test(struct timer_list *t);//定义function_test定时功能函数
DEFINE_TIMER(timer_test,function_test);//定义一个定时器
static void function_test(struct timer_list *t)
{atomic64_inc(&v);//原子变量v自增dev1.sec = atomic_read(&v);//将读取到的原子变量v,赋值给secprintk("the sec is %d\n",dev1.sec);mod_timer(&timer_test,jiffies_64 + msecs_to_jiffies(1000));//使用mod_timer函数将定时时间设置为一秒后
}/*打开设备函数*/
static int open_test(struct  inode  *inode,struct file *file){file->private_data=&dev1;//设置私有数据printk("\n this is open_test \n");add_timer(&timer_test);	//添加一个定时器return 0;};static ssize_t read_test(struct file *file, char __user *buf, size_t size, loff_t *off)
{if(copy_to_user(buf,&dev1.sec,sizeof(dev1.sec))){//使用copy_to_user函数将sec传递到应用层printk("copy_to_user error \n");return -1;}return 0;
}static int release_test(struct inode *inode,struct file *file)
{del_timer(&timer_test);//删除一个定时器printk("\nthis is release_test \n");return 0;
}static struct file_operations fops_test = {.owner=THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块.open = open_test,//将open字段指向chrdev_open(...)函数.read = read_test,//将open字段指向chrdev_read(...)函数.release = release_test,//将open字段指向chrdev_release(...)函数
};//定义file_operations结构体类型的变量cdev_test_opsstatic int __init timer_dev_init(void)//驱动入口函数
{if(alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name") < 0){printk("alloc_chrdev_region is error\n");}   printk("alloc_chrdev_region is ok\n");dev1.major=MAJOR(dev1.dev_num);//通过MAJOR()函数进行主设备号获取dev1.minor=MINOR(dev1.dev_num);//通过MINOR()函数进行次设备号获取printk("major is %d\n",dev1.major);printk("minor is %d\n",dev1.minor);使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构体cdev_init(&dev1.cdev_test,&fops_test);dev1.cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 cdev_add(&dev1.cdev_test,dev1.dev_num,1);printk("cdev_add is ok\n");dev1.class  = class_create(THIS_MODULE,"test");//使用class_create进行类的创建,类名称为class_testdevice_create(dev1.class,NULL,dev1.dev_num,NULL,"test");//使用device_create进行设备的创建,设备名称为device_testreturn 0;
}
static void __exit timer_dev_exit(void)//驱动出口函数
{cdev_del(&dev1.cdev_test);//使用cdev_del()函数进行字符设备的删除unregister_chrdev_region(dev1.dev_num,1);//释放字符驱动设备号 device_destroy(dev1.class,dev1.dev_num);//删除创建的设备class_destroy(dev1.class);//删除创建的类printk("module exit \n");}
module_init(timer_dev_init);//注册入口函数
module_exit(timer_dev_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("wang fang chen "); //作者信息

编译文件-Makefile

#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += timer_dev.o
KDIR :=/home/wfc123/Linux/rk356x_linux/kernel
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean

测试程序-timer.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc,char *argv[]){int fd;//定义int类型的文件描述符fdint count;//定义int类型记录秒数的变量countfd = open("/dev/test",O_RDWR);//使用open()函数以可读可写的方式打开设备文件while(1){	read(fd,&count,sizeof(count));//使用read函数读取内核传递来的秒数sleep(1);printf("num is %d\n",count);}return 0;
}

编译测试程序变成可执行文件:

aarch64-linux-gnu-gcc -o timer timer.c

分析

通过函数 read(fd,&count,sizeof(count)) 调用, 为什么count 值会变化。 有的人会问,count 默认值是0 ,read 读取。

ssize_t read(int fd, void *buf, size_t count);

函数read 参数解释:

  • fd 参数代表文件描述符,是一个整数,用于标识要读取的文件或设备。
  • buf 参数是一个指向缓冲区的指针,read 函数会将读取的数据存储在这个缓冲区中。
  • count 参数指定了最多要读取的字节数。

这里好好思考一下如下,会不会有问题,需要理解清楚,思考清楚。

测试程序每隔1秒读取count值 放到$count 缓冲区, 然后读取;内核端每隔一秒增加sec 值,通过原子操作+1,然后内核层copy_to_user。 用户层在缓冲区读取的值就是内核层每隔一秒通过 buffer 传递过来的。

在这里插入图片描述

加载驱动-运行测试程序

insmod timer_dev.ko./timer

实际返回值,如下:
在这里插入图片描述

总结

这个程序实验,用到了原子操作+定时器timer_list+字符设备操作。 简要了解即可,加深前面知识印象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/906398.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Java实战]Spring Boot整合Kafka:高吞吐量消息系统实战(二十七)

[Java实战]Spring Boot整合Kafka&#xff1a;高吞吐量消息系统实战&#xff08;二十七&#xff09; 一、引言 Apache Kafka作为一款高吞吐量、低延迟的分布式消息队列系统&#xff0c;广泛应用于实时数据处理、日志收集和事件驱动架构。结合Spring Boot的自动化配置能力&…

Kotlin Multiplatform--04:经验总结(持续更新)

Kotlin Multiplatform--04&#xff1a;经验总结&#xff08;持续更新&#xff09; 引言 引言 本章用来记载笔者开发过程中的一些经验总结 一、Ktor设置Header 在官方文档中&#xff0c;想要设置Header的示例代码如下&#xff1a; client.get("https://ktor.io&qu…

在 Ubuntu 系统中,将 JAR 包安装为服务

在 Ubuntu 系统中&#xff0c;将 JAR 包安装为服务可以通过 systemd 来实现。以下是详细的操作步骤&#xff1a; 准备工作 确保 JAR 文件路径和 Java 运行时环境已准备好。验证 Java 是否可用&#xff1a; java -version创建 systemd 服务文件 systemd 的服务文件通常位于 …

电商项目-商品微服务-品牌管理微服务开发

一、功能分析 品牌管理微服务包括&#xff1a; &#xff08;1&#xff09;查询全部列表数据 &#xff08;2&#xff09;根据ID查询实体数据 &#xff08;3&#xff09;增加 &#xff08;4&#xff09;修改 &#xff08;5&#xff09;删除 &#xff08;6&#xff09;分页…

Spring Boot开发—— 整合Lucene构建轻量级毫秒级响应的全文检索引擎

文章目录 一、为什么选择 Lucene?轻量级搜索的底层密码二、核心原理:Lucene 的倒排索引2.1 倒排索引:速度之源2.2 段合并优化策略三、Spring Boot集成Lucene实战3.1 依赖配置3.2 实体与索引设计3.3 核心索引服务(含异常处理)3.4 使用示例(测试类)四、高级优化技巧4.1 索…

SpringBootDay1|面试题

目录 一、springboot框架 1、什么是springboot 2、Spring Boot的主要优点 3、springboot核心注解 4、定义banner&#xff08;springboot的logo&#xff09; 5、springboot配置文件 6、springboot 整合 jdbc 二、面试题 1&#xff09;springmvc的作用 ​编辑 2&#x…

jQuery Ajax中dataType 和 content-type 参数的作用详解

jQuery Ajax中dataType与contentType参数解析 一、核心概念对比 参数作用对象数据类型默认值dataType响应数据预期接收的数据格式jQuery自动判断&#xff08;根据响应头MIME类型&#xff09;contentType请求数据发送数据的编码格式application/x-www-form-urlencoded 二、da…

几款常用的虚拟串口模拟器

几款常用的虚拟串口模拟器&#xff08;Virtual Serial Port Emulator&#xff09;&#xff0c;适用于 Windows 系统&#xff0c;可用于开发和调试串口通信应用&#xff1a; 1. com0com (开源免费) 特点&#xff1a; 完全开源免费&#xff0c;无功能限制。 可创建多个虚拟串口…

LLM笔记(六)线性代数

公式速查表 1. 向量与矩阵&#xff1a;表示、转换与知识存储的基础 向量表示 (Vectors): 语义的载体 在LLM中&#xff0c;向量 x ∈ R d \mathbf{x}\in\mathbb{R}^d x∈Rd 是信息的基本单元&#xff0c;承载着丰富的语义信息&#xff1a; 词嵌入向量 (Word Embeddings)&am…

[特殊字符] Word2Vec:将词映射到高维空间,它到底能解决什么问题?

一、在 Word2Vec 之前,我们怎么处理语言? 在 Word2Vec 出现之前,自然语言处理更多是“工程方法”,例如字符串匹配、关键词提取、正则规则...。但这些表示通常缺乏语义,词与词之间看不出任何联系以及非常浅显。当然,技术没有好坏,只有适合的场景。例如: 关键词匹配非常…

栈和队列的模拟实现

栈和队列的模拟实现 容器适配器priority_queue(优先级队列&#xff09;priority_queue的使用priority_queue的模拟实现&#xff1a; 仿函数什么叫仿函数&#xff1f;需要自己实现仿函数的情况&#xff1a; 栈的模拟实现队列的模拟实现deque&#xff08;vector和list的缝合怪&am…

idea本地debug断点小技巧

idea本地debug断点小技巧 简单的设置断点条件 断点后&#xff0c;右键这个断点&#xff0c;可以在 condition 中填写能得出布尔的表达式 a 1 你如果写如下&#xff0c;表示先给他赋值&#xff0c;然后断住 a 2; true 断点后设置某个变量的值 在 debug 区域可以设置变量…

Oracle中如何解决FREE BUFFER WAITS

基于性能上的考虑&#xff0c;服务器进程在扫描LRU主列的同时&#xff0c;会将脏块移至LRU-W列&#xff0c;如果发现没有足够可用&#xff08;可替换&#xff09;的BUFFER CACHE&#xff0c;进程并不会无止尽地扫描整条LRU主列&#xff0c;而是在扫描到某个阀值&#xff08;该阀…

Git命令使用全攻略:从创建分支到合并的完整流程

Git命令使用全攻略&#xff1a;从创建分支到合并的完整流程 引言一、初始化项目与基础配置1.1 克隆远程仓库1.2 查看当前分支状态 二、创建与管理分支2.1 从main分支创建新功能分支2.2 查看分支列表2.3 提交代码到新分支2.4 推送分支到GitHub 三、版本发布与标签管理3.1 创建轻…

MATLAB跳动的爱心

520&#xff0c;一个会动的心~~~ function particleHeart2 % author : slandarer% 所需匿名函数 col1Func(n) repmat([255,158,196]./255,[n,1])repmat([-39,-81,-56]./255,[n,1]).*rand([n,1]); col2Func(n) repmat([118,156,216]./255,[n,1])repmat([137,99,39].*.1./255,[n,…

Go的单测gomock及覆盖率命令

安装gomock&#xff1a; go get github.com/golang/mock/gomockgo get github.com/golang/mock/mockgen 使用 mockgen 生成 mock 代码: 参考 mockgen -sourceservice/user.go -destinationservice /mocks/mock_user_service.go -packagemocks go test -coverprofilecoverage.ou…

vue添加loading后修复页面渲染问题

问题&#xff1a;想要通过选择流程&#xff08;1&#xff09;后加载出角色信息&#xff08;2&#xff09; 选择后无法展示经过排查&#xff0c;再调用接口给角色数组赋值后&#xff0c;页面在接口调用完之前就已经渲染完成。接口是采用的异步加载解决&#xff1a;loadingRoles…

Python入门手册:Python简介,什么是Python

在当今数字化时代&#xff0c;编程语言犹如一把把神奇的钥匙&#xff0c;能够开启通往技术世界的大门。而Python&#xff0c;无疑是其中最闪耀的一颗明星。今天&#xff0c;就让我们一起走进Python的世界&#xff0c;从它的起源、应用领域以及优缺点三个方面&#xff0c;来全面…

用PyTorch在超大规模下训练深度学习模型:并行策略全解析

我猜咱们每个人肯定都累坏了&#xff0c;天天追着 LLM 研究社区跑&#xff0c;感觉每天都冒出个新的最牛模型&#xff0c;把之前的基准都给打破了呢。要是你好奇为啥创新速度能这么快&#xff0c;那主要就是研究人员能够在超大规模下训练和验证模型啦&#xff0c;这全靠并行计算…

提示工程(Prompt Engineering)应用技巧

Prompt&#xff08;提示&#xff09;就是用户与大模型交互输入的代称。即我们给大模型的输入称为 Prompt&#xff0c;而大模型返回的输出一般称为 Completion。 Prompt 需要清晰明确地表达需求&#xff0c;提供充足上下文&#xff0c;使语言模型能够准确理解我们的意图。更长、…