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");

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

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

相关文章

构建无缝的服务网格体验:分享在生产环境中构建和管理服务网格的最佳实践

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

容器的数据卷

容器的数据卷 操作数据卷 # 基本格式 docker volume [common] # 创建一个volume docker volume create # 显示一个或多个volume docker volume inspect # 列出所以的volume docker volume ls # 删除未使用的volume docker volume prune # 删除一个或多个volume docker volume…

双节履带机械臂小车实现蓝牙遥控功能

1.功能描述 本文示例所实现的功能为&#xff1a;采用蓝牙远程遥控双节履带机械臂小车进行运动。 2.结构说明 双节履带机械臂小车&#xff0c;采用履带底盘&#xff0c;可适用于任何复杂地形。 前节履带抬起高度不低于10cm&#xff0c;可用于履带车进行爬楼行进。 底盘上装有一…

mybatis学习记录(三)-----关于SQL Mapper的namespace

关于SQL Mapper的namespace 视频总结笔记&#xff1a; 在SQL Mapper配置文件中<mapper>标签的namespace属性可以翻译为命名空间&#xff0c;这个命名空间主要是为了防止SQL id 冲突的。 创建CarMapper2.xml文件&#xff0c;代码如下&#xff1a; CarMapper2.xml: <?…

用Python判断是否为闰年并计算生肖年

1 问题 润平年以及生肖是新的一年到来我们应该了解的信息。那么如何利用python程序计算快速计算该年为什么年&#xff1f; 2 方法 利用if条件判断语句实现。 代码清单 1 year eval(input(请输入咨询的年份:))if (year % 4 0 and year %100 ! 0) or year % 400 0: print(…

java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统源码

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…

RocketMQ快速实战以及集群架构详解

⼀、 MQ 简介 MQ &#xff1a; MessageQueue &#xff0c;消息队列。是在互联⽹中使⽤⾮常⼴泛的⼀系列服务中间件。 这个词可以分两个部分来看&#xff0c;⼀是Message &#xff1a;消息。消息是在不同进程之间传递的数据。这些进程可以部署在同⼀台机器上&#xff0c;也可以…

17. 电话号码的字母组合

题目链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 思路&#xff1a; 数字对应字母用map(这里不好用&#xff09;&#xff0c;还是用数组映射&#xff0c;因为这里的映射表是个静态的 组合的思想。比如2,3就是需要选两个字母即搜…

TCP详解之流量控制

TCP详解之流量控制 发送方不能无脑的发数据给接收方&#xff0c;要考虑接收方处理能力。 如果一直无脑的发数据给对方&#xff0c;但对方处理不过来&#xff0c;那么就会导致触发重发机制&#xff0c;从而导致网络流量的无端的浪费。 为了解决这种现象发生&#xff0c;TCP 提…

一个方法用js生成随机双色球、大乐透

代码如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><s…

网络安全深入学习第六课——热门框架漏洞(RCE— Weblogic反序列化漏洞)

文章目录 一、Weblogic介绍二、Weblogic反序列化漏洞历史三、Weblogic框架特征1、404界面2、登录界面 四、weblogic常用弱口令账号密码五、Weblogic漏洞介绍六、Weblogic漏洞手工复现1、获取账号密码&#xff0c;这是一个任意文件读取的漏洞1&#xff09;读取SerializedSystemI…

MySQL性能优化——MYSQL执行流程

MySQL 执行流程1-5如下图。 MySQL 的架构共分为两层&#xff1a;Server 层和存储引擎层&#xff0c; Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现&#xff0c;主要包括连接器&#xff0c;查询缓存、解析器、预处理器、优化器、执行器等。…

【C刷题训练营】第四讲(打好基础很重要)

前言: 大家好&#xff0c;这是c语言刷题训练营的第四讲&#xff0c;打好基础便于对c语言语法与算法思维的提高&#xff0c;感谢你的来访与支持&#xff01; &#x1f4a5;&#x1f388;个人主页:​​​​​​Dream_Chaser&#xff5e; &#x1f388;&#x1f4a5; ✨✨刷题专栏…

操作系统基本概念

目录 一、基本概述 二、操作系统的特点 &#xff08;一&#xff09;并发性&#xff08;实质是微观的串行、宏观的并行&#xff09; 1. 对比看&#xff1a;并行性 2. 单核CPU和多核CPU &#xff08;二&#xff09;共享性 &#xff08;三&#xff09;虚拟性 &#xff08;…

升级OpenSSL并进行编译安装

Packaging (OpenSSL)组件存在安全漏洞的原因是由于当前爆出的Openssl漏洞。 这个漏洞可能会导致泄露隐私信息&#xff0c;并且涉及的机器和环境也有所不同&#xff0c;因此修复方案也会有所不同。 目前&#xff0c;一些服务器使用的Nginx是静态编译OpenSSL&#xff0c;直接将Op…

2023陇剑杯

2023陇剑杯初赛WP HW hard_web_1 ​ 首先判断哪个是服务器地址 ​ 从响应包看&#xff0c;给客户端返回数据包的就是服务器 所以确定服务器地址是192.168.162.188​ 再从开放端口来看&#xff0c;长期开放的端口 客户端发送一个TCP SYN包&#xff08;同步请求&#xff…

记一次线上BUG排查过程

1. 线上遇到一个非常奇怪的bug&#xff0c;为一个用户分配业务线类型后&#xff0c;该用户登录时&#xff0c;提示502&#xff0c;但其它的用户登录完全是正常的 2. 问题现象 3. 排查思路 先去看线上日志&#xff0c;看是否有error&#xff0c;但日志里边这个接口200正常返回…

Django系列:Django的项目结构与配置解析

Django系列 Django的项目结构与配置解析 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/132893616 【介…

Nginx配置最佳实践

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

2023/9/18 -- C++/QT

作业 完善登录框 点击登录按钮后&#xff0c;判断账号&#xff08;admin&#xff09;和密码&#xff08;123456&#xff09;是否一致&#xff0c;如果匹配失败&#xff0c;则弹出错误对话框&#xff0c;文本内容“账号密码不匹配&#xff0c;是否重新登录”&#xff0c;给定两…