linux驱动开发day6--(epoll实现IO多路复用、信号驱动IO、设备树以及节点和属性解析相关API使用)
一、IO多路复用--epoll实现
1.核心:
红黑树、一张表以及三个接口、
2.实现过程及API
1)创建epoll句柄/创建红黑树根节点
int epfd=epoll_create(int size--无意义,>0即可)----------成功:返回根节点对应文件描述符,失败:-1
2)将要监测的文件描述符挂载到红黑树上
a.struct epoll_event event;定义事件结构体
b.struct epoll_event events[10];定义存放就绪事件描述符的数组
c.添加准备就绪事件进入epoll,如:
event.events = EPOLLIN; // 读事件
event.data.fd = fd1;
epoll_ctl(epfd, EPOLL_CTL_ADD---控制方法, fd1, &event)
3)监听事件是否发生,阻塞等待准备好的文件描述符
epoll_wait(epfd, events, 10, -1--不关心是否超时);
返回值:
>0:准备好的文件描述符的个数
=0:超时
<0:失败
4)遍历数组,做事件的处理
二、信号驱动IO
异步IO方式,linux预留了一个信号SIGIO用于进行信号驱动IO,当硬件数据准备就绪后会发起一个硬件中断,在中断的处理函数中向当前进程发送一个SIGIO信号。进程收到SIGIO信号后执行信号处理函数,在信号处理函数中将数据读走即可。
1.实现过程及API
应用程序:
1)打开设备文件
2)注册信号的信号处理函数--signal(SIGIO,信号处理函数)
3)回调驱动中的fasync方法,完成发送信号之前的准备工作
a.获取文件描述符属性
int flags=fcntl(fd,F_GETFL);
b.在文件描述符表的flags中添加FASYNC
fcntl(fd,F_SETFL,flags|FASYNC);
c.设置fd对应的驱动程序发送SIGIO信号只发送给当前进程
fcntl(fd,F_SETOWN,getpid());
4)注意:不能让主程序结束
驱动程序:
1)定义一个异步对象指针
struct fasync_struct *fp;
2)异步操作方法
int mycdev_fasync(int fd, struct file *file, int on) // 异步操作方法
{
// 完成发送信号之前的准备工作
fasync_helper(fd, file, on, &fp);
return 0;
}
需要在操作方法结构体对象中加 .fasync = mycdev_fasync,
3)向进程发送信号
参数: fp:异步对象的二级指针
sig:要发生的信号 SIGIO
band:发送信号时添加的事件标志 POLL_IN表述读数据操作
//发送信号
kill_fasync(&fp,SIGIO,POLL_IN);
三、设备树
1.概念
1)设备树(DeviceTree/DT/of)是用来保存设备信息的一种树形结构
2)设备树的源码是独立于linux内核源码存在的
3)设备树上的设备信息在内核启动后被内核解析,加载到内核空间
4)设备树上的节点(包含属性和子节点),保存设备的设备信息;设备的信息由多个属性(以链表形式存在,属性是键值对)共同描述.
2.引入设备树的原因
为了让驱动可以兼容更多硬件,不在驱动中指定设备信息,让驱动中获取设备树上的设备信息,基于这些设备信息完成硬件的控制
设备树linux官方手册:Device Tree Usage - eLinux.org
3.设备树节点结构体struct device_node和属性结构体 struct property
4.设备树节点解析API
1)根据设备树节点的名字解析指定的设备树节点信息
struct device_node *dnode;
dnode=of_find_node_by_name(NULL(默认从根节点解析),"mynode");
返回值:成功返回目标节点首地址,失败返回NULL
测试:
printk("name=%s,value=%s\n",dnode->properties->name,(char *)dnode->properties->value);
2)根据设备树节点路径解析设备树节点信息
3)根据节点的厂商信息解析指定的节点
dnode=of_find_compatible_node(NULL(默认从根节点解析),NULL(设备类型),:compatible值);
4)将大端字节序32位的数据转换成主机字节序
__u32 __be32_to_cpup(const __be32 *p)
printk("name=%s,value=%x %x\n",dnode->properties->next->next->name,
__be32_to_cpup((u32 *)dnode->properties->next->next->value),
__be32_to_cpup((u32 *)dnode->properties->next->next->value+1));
5.属性解析API
返回值:成功返回属性对象指针,失败返回NULL
struct property *pr;
int len;
pr=of_find_property(dnode,"uint",&len);
测试:
printk("name=%s value=%x %x\n",pr->name,__be32_to_cpup((u32 *)pr->value),
__be32_to_cpup((u32 *)pr->value+1));
epoll实现IO多路复用的应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/epoll.h>
/* According to earlier standards */
#include <sys/time.h>int main(int argc, char const *argv[])
{int fd1, fd2, epfd;struct epoll_event event; // 用于操作epollstruct epoll_event events[10]; // 存放就绪事件描述符的数组char buf[128] = {0};// 创建epoll句柄epfd = epoll_create(1);if (epfd < 0){printf("epoll_create filed\n");exit(-1);}// 打开设备文件fd1 = open("/dev/input/mouse0", O_RDWR);if (fd1 < 0){printf("打开鼠标设备文件失败\n");exit(-1);}fd2 = open("/dev/mycdev0", O_RDWR);if (fd2 < 0){printf("打开鼠标设备文件失败\n");exit(-1);}// 添加准备就绪事件进入epoll;event.events = EPOLLIN; // 读事件event.data.fd = fd1;if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &event) < 0){printf("epoll_ctl add filed\n");}event.events = EPOLLIN; // 读事件event.data.fd = fd2;if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &event) < 0){printf("epoll_ctl add filed\n");}// 监听事件是否发生while (1){// 如果成功,ret接收返回的事件个数,把就绪的事件放在events数组中int ret = epoll_wait(epfd, events, 10, -1);if (ret < 0){printf("epoll_wait filed\n");exit(-1);}int i;// 循环遍历数组,做事件的处理for (i = 0; i < ret; i++){if (events[i].events & EPOLLIN)//判断发生的事件是不是读事件{read(events[i].data.fd, buf, sizeof(buf));printf("buf:%s\n", buf);}}}close(fd1);close(fd2);return 0;
}
信号驱动IO:
proc1.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/epoll.h>
/* According to earlier standards */
#include <sys/time.h>
int fd; // 存放就绪事件描述符的数组
char buf[128] = {0};
// 定义信号处理函数
void sigio_handler(int sig)
{read(fd, buf, sizeof(buf));printf("buf:%s\n", buf);
}
int main(int argc, char const *argv[])
{// 打开设备文件fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("打开设备文件失败\n");exit(-1);}// 注册SIGIO信号的信号处理函数signal(SIGIO, sigio_handler);// 回调驱动中的fasync方法,完成发送信号之前的准备工作int flags = fcntl(fd,F_GETFL); // 获取文件描述符属性fcntl(fd,F_SETFL,flags|FASYNC); // 在文件描述符表的flags中添加FASYNC,就可以回调fasync方法fcntl(fd,F_SETOWN,getpid());//驱动发送信号只发送给当前进程while(1){printf("aaaaa\n");sleep(1);}close(fd);return 0;
}
proc2.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>int main(int argc, char const *argv[])
{char buf[128] = "hello world";int fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("打开设备文件失败\n");exit(-1);}write(fd, buf, sizeof(buf));close(fd);return 0;
}
fasync.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/poll.h>
struct cdev *cdev;
char kbuf[128] = {0};
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); // 方便在命令行传递major的值
struct class *cls;
struct device *dev;
struct fasync_struct *fp; // 定义一个异步对象指针// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{int ret;// 判断IO方式if (file->f_flags & O_NONBLOCK) // 非阻塞{}else // 阻塞{}ret = copy_to_user(ubuf, kbuf, size);if (ret){printk("copy_to_user err\n");return -EIO;}return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{int ret;// 从用户拷贝数据,模拟硬件数据ret = copy_from_user(kbuf, ubuf, size);if (ret){printk("copy_from_user err\n");return -EIO;}//发送信号(异步对象二级指针,要发生的信号,发送信号时添加事件的标志位)kill_fasync(&fp,SIGIO,POLL_IN);return 0;
}int mycdev_fasync(int fd, struct file *file, int on) // 异步操作方法
{// 完成发送信号之前的准备工作fasync_helper(fd, file, on, &fp);return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
// 定义一个操作方法结构体对象并且初始化
struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.fasync = mycdev_fasync,.release = mycdev_close,
};
static int __init mycdev_init(void)
{int ret;// 为字符设备驱动对象申请空间cdev = cdev_alloc();if (cdev == NULL){printk("字符设备驱动对象申请空间失败\n");ret = -EFAULT;goto out1;}printk("申请对象空间成功\n");// 初始化字符设备驱动对象cdev_init(cdev, &fops);// 申请设备号if (major > 0) // 静态指定设备号{ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");if (ret){printk("静态申请设备号失败\n");goto out2;}}else if (major == 0) // 动态申请设备号{ret = alloc_chrdev_region(&devno, minor, 3, "myled");if (ret){printk("动态申请设备号失败\n");goto out2;}major = MAJOR(devno); // 获取主设备号minor = MINOR(devno); // 获取次设备号}printk("申请设备号成功\n");// 注册字符设备驱动对象ret = cdev_add(cdev, MKDEV(major, minor), 3);if (ret){printk("注册字符设备驱动对象失败\n");goto out3;}printk("注册字符设备驱动对象成功\n");// 向上提交目录信息cls = class_create(THIS_MODULE, "myled");if (IS_ERR(cls)){printk("向上提交目录失败\n");ret = -PTR_ERR(cls);goto out4;}printk("向上提交目录成功\n");// 向上提交设备节点信息int i;for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点信息失败\n");ret = -PTR_ERR(dev);goto out5;}}printk("向上提交设备信息成功\n");return 0;
out5:// 释放前一次提交成功的设备信息for (--i; i >= 0; i--){device_destroy(cls, MKDEV(major, i));}class_destroy(cls); // 释放目录
out4:cdev_del(cdev);
out3:unregister_chrdev_region(MKDEV(major, minor), 3);
out2:kfree(cdev);
out1:return ret;
}
static void __exit mycdev_exit(void)
{// 释放节点信息int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 销毁目录class_destroy(cls);// 注销驱动对象cdev_del(cdev);// 释放设备号unregister_chrdev_region(MKDEV(major, minor), 3);// 释放对象空间kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
相关文章:

linux驱动开发day6--(epoll实现IO多路复用、信号驱动IO、设备树以及节点和属性解析相关API使用)
一、IO多路复用--epoll实现 1.核心: 红黑树、一张表以及三个接口、 2.实现过程及API 1)创建epoll句柄/创建红黑树根节点 int epfdepoll_create(int size--无意义,>0即可)----------成功:返回根节点对应文件描述符…...

9月15日作业
Qt代码 #include "mywnd.h"//构造函数的定义 mywnd::mywnd(QWidget *parent): QWidget(parent) //显性调用父类的有参构造完成对子类从父类继承下来成员的初始化工作 {//窗口设置this->resize(QSize(500, 433));this->setWindowTitle("Widget&quo…...
关于Java多线程的那些事
多线程 多线程1. 关于多线程的理解1.1 进程和线程1.2 并行和并发1.3 线程调度 2. 创建多线程的方式创建线程有哪几种方式?2.1 通过继承Thread类来创建并启动线程的步骤如下:2.2 通过实现Runnable接口来创建并启动线程的步骤如下:2.3 通过实现…...

信息化项目验收的依据、内容和验收测评报告
随着信息系统业务覆盖率的提高和深度整合创新的逐步提高,信息系统运行阶段的复杂性和资源比例逐渐增加。一方面,信息已成为业务创新、技术应用和运营服务的综合体,而不仅仅是技术平台建设。另一方面,信息采购是技术平台建设。另一…...

解决IntelliJ IDEA执行maven打包,执行java -jar命令提示jar中没有主清单属性
问题场景 IDEA执行mvn clean package -DskipTesttrue命令或者借助工具的Maven菜单进行打包操作,然后执行java -jar app.jar命令后,提示jar中没有主清单属性 D:\WorkSpace\demo\target>java -jar demo-SNAPSHOT.jar demo-SNAPSHOT.jar中没有主清单属性…...
Python--文件和异常
目录 1、读取文件 1.1 读取文件的全部内容 1.2 相对路径和绝对路径 1.3 访问文件中的各行 1.4 使用文件中的内容 1.5 包含100万位的大型文件 1.6 圆周率中的生日 2、写入文件 2.1 写入一行 2.2 写入多行 3、异常 3.1 处理ZeroDivisionError 异常 3.2 使用try-exce…...

IDEFICS 简介: 最先进视觉语言模型的开源复现
我们很高兴发布 IDEFICS ( Image-aware Decoder Enhanced la Flamingo with Ininterleaved Cross-attention S ) 这一开放视觉语言模型。IDEFICS 基于 Flamingo,Flamingo 作为最先进的视觉语言模型,最初由 DeepMind 开发,但目前尚未公开发布…...
玩转Mysql系列 - 第20篇:异常捕获及处理详解
这是Mysql系列第20篇。 环境:mysql5.7.25,cmd命令中进行演示。 代码中被[]包含的表示可选,|符号分开的表示可选其一。 需求背景 我们在写存储过程的时候,可能会出现下列一些情况: 插入的数据违反唯一约束ÿ…...
一些工具类
1、字符串处理工具类 1.1、StrUtils package com.study.java8.util;/*** Classname:StrUtils* Description:字符串工具类* Date:2023/9/9 9:37* Author:jsz15*/import org.apache.commons.lang.text.StrBuilder; import org.apa…...

20230916后台面经整理
1.面对抢优惠券这样的高负载场景,你从架构、负载均衡等方面说一下你的设计? 答了参考Nginx进行负载均衡,然后在每台服务器怎么怎么弄(架构每一层怎么设计) 参考https://toutiao.io/posts/6z3uu2m/preview,h…...

如何通过快解析测试接口内外网?本地内网ip让外网访问连接
接口调试测试是网络技术员经常工作内容之一。如在公司内部api项目webserver测试,在公司内办公室个人电脑是正常用内网IP访问连接测试的,但在外网电脑需要远程测试时需要怎么测试呢?这里提供一种内网地址让外网访问的通用方法:快解…...

用c++实现五子棋小游戏
五子棋是一款经典小游戏,今天我们就用c实现简单的五子棋小游戏 目录 用到的算法: 思路分析 定义变量 开始写代码 完整代码 结果图: 用到的算法: 合法移动的判断:isValidMove 函数通过检查指定位置是否在棋盘范…...

Android 12.0 SystemUI下拉状态栏定制化之隐藏下拉通知栏布局功能实现(二)
1.前言 在12.0的系统定制化开发中,由于从12.0开始SystemUI下拉状态栏和11.0的变化比较大,所以可以说需要从新分析相关的SystemUI的 布局,然后做分析来实现不同的功能,今天就开始实现关于隐藏SystemUI下拉状态栏中的通知栏布局系列二,去掉下拉状态栏中 通知栏部分 白色的…...

通过finalshell快速在ubuntu上安装jdk1.8
这篇文章主要介绍一下怎么通过finalshell连接ubuntu,然后在ubuntu上安装jdk1.8,让不熟悉linux操作系统的童鞋也能快速地完成安装。 目录 一、准备一台虚拟机 二、安装finalshell远程连接工具 三、获取ubuntu虚拟机的ip地址 四、通过finalshell连接u…...

【Linux从入门到精通】多线程 | 线程互斥(互斥锁)
上篇文章我们对线程 | 线程介绍&线程控制介绍后,本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。 文章目录 引入 一、重入与临界 1、1 可…...

Echarts 散点图的详细配置过程
文章目录 散点图 简介配置步骤简易示例 散点图 简介 Echarts散点图是一种常用的数据可视化图表类型,用于展示两个或多个维度的数据分布情况。散点图通过在坐标系中绘制数据点的位置来表示数据的关系。 Echarts散点图的特点如下: 二维数据展示ÿ…...

Nginx详解 五:反向代理
文章目录 1. 正向代理和反向代理1.1 正向代理概述1.1.1 什么是正向代理1.1.2 正向代理的作用1.1.3 正向代理的基本格式 1.2 反向代理概述1.2.1 什么是反向代理1.2.2 反向代理可实现的功能1.2.3 反向代理的可用模块 2. 配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其…...

【PDF密码】PDF文件打开之后不能打印,怎么解决?
正常的PDF文件是可以打印的,如果PDF文件打开之后发现文件不能打印,我们需要先查看一下自己的打印机是否能够正常运行,如果打印机是正常的,我们再查看一下,文件中的打印功能按钮是否是灰色的状态。 如果PDF中的大多数功…...

深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数
前言:对于库函数有适当了解的朋友们,对于 qsort 函数想必是有认知的,因为他可以对任意数据类型进行排序的功能属实是有点厉害的,本次分享,笔者就给大家带来 qsort 函数的全面的解读 本次知识的分享笔者分为上下俩卷文章…...

Azure + React + ASP.NET Core 项目笔记一:项目环境搭建(二)
有意义的标题 pnpm 安装umi4 脚手架搭建打包语句变更Visual Studio调试Azure 设置变更发布 pnpm 安装 参考官网,或者直接使用npm安装 npm install -g pnpmumi4 脚手架搭建 我这里用的umi4,官网已附上 这里需要把clientapp清空,之后 cd Cl…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...