驱动开发,IO多路复用实现过程,epoll方式

news2024/11/14 20:43:08

1.框架图

被称为当前时代最好用的io多路复用方式;

核心操作:一棵树(红黑树)、一张表(内核链表)以及三个接口;

 思想:(fd代表文件描述符)

        epoll要把检测的事件fd挂载到内核空间红黑树上,遍历红黑树,调用每个fd对应的操作方法,找到发生事件的fd,如果没有发生事件的fd,进程休眠,如果事件发生,将发生事件的fd拷贝一份放到内核链表,每个节点对应一个fd,最后把链表的节点信息传递到用户空间的数组中,用户空间无需判断事件的发生,需要判断事件类型(读写等);

 

2.代码

---pro1.c---应用程序(epoll方式)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/epoll.h>

int main(int argc, const char *argv[])
{
    int fd1,fd2,epfd;
    char buf[128] = {0};
    struct epoll_event event;  //用于操作epoll
    struct epoll_event events[10];  //用户空间存放发生事件的数组
    
    //创建epoll句柄,红黑树根节点
    epfd = epoll_create(1);
    if(epfd < 0)
    {
        printf("epoll_create fail\n");
        exit(-1);
    }
    //打开设备文件
    fd1 = open("/dev/input/mouse0", O_RDWR);
    if (fd1 < 0)
    {
        printf("鼠标事件文件失败\n");
        exit(-1);
    }

    fd2 = open("/dev/myled0", 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 fd1 fail\n");
    }
    event.events = EPOLLIN;  //读事件
    event.data.fd = fd2;
    if((epoll_ctl(epfd,EPOLL_CTL_ADD,fd2,&event)) < 0)
    {
        printf("epoll_ctl fd2 fail\n");
    }

    //监听时间是否发生
    while(1)
    {
        //成功接收返回时间的个数,放入events数组中
        int ret = epoll_wait(epfd,events,10,-1);
        if(ret < 0)
        {
            printf("epoll_wait fail\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);
                memset(buf,0,sizeof(buf));
            }
        }
    }

    close(fd1);
    close(fd2);

    return 0;
}
---pro2.c---应用程序(模拟自定义设备数据就绪)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, const char *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;
}
---epoll.c---驱动程序

 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include<linux/poll.h>

char kbuf[128] = {0};
unsigned int major;
struct class *cls;
struct device *dev;
unsigned int condition = 0;

// 定义一个等待队列头
wait_queue_head_t wq_head;

// 封装操作方法
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;
   
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy_to_ user err\n");
        return -EIO;
    }
    condition = 0; // 下一次硬件数据没有就绪

    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;
    }

    condition = 1;
    wake_up_interruptible(&wq_head);

    return 0;
}
//封装POLL方法
__poll_t mycdev_poll(struct file *file, struct poll_table_struct *wait)
{
    __poll_t mask = 0;
    //向上提交等待队列头
    poll_wait(file,&wq_head,wait);
    //根据事件是否发生给一个合适的返回值
    if(condition)
    {
        mask = POLLIN;
    }
    
    return mask;
}
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,
    .poll = mycdev_poll,
    .write = mycdev_write,
    .release = mycdev_close,
};

// 入口函数
static int __init mycdev_init(void)
{
    //初始化等待队列
    init_waitqueue_head(&wq_head);

    major = register_chrdev(0, "myled", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 向上提交目录
    cls = class_create(THIS_MODULE, "MYLED");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    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");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}

// 出口函数
static void __exit mycdev_exit(void)
{
    // 销毁设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }

    // 销毁目录信息
    class_destroy(cls);

    // 字符设备驱动注销
    unregister_chrdev(major, "myled");
}

// 声明
// 入口函数地址
module_init(mycdev_init);
// 出口函数地址
module_exit(mycdev_exit);
// 遵循的GPL协议
MODULE_LICENSE("GPL");

 

3.测试结果

 执行pro2.c,自定义事件被监听到;

 在ubuntu上动鼠标,鼠标事件被监听;

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

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

相关文章

Ubuntu安装深度学习环境相关(yolov8-python部署)

Ubuntu安装深度学习环境相关(yolov8-python部署) 本文将从如下几个方面总结相关的工作过程&#xff1a; Ubuntu系统安装(联想小新pro16) 2.显卡驱动安装3.测试深度学习模型 1. Ubunut 系统安装 之前在台式机上安装过Ubuntu&#xff0c;以为再在笔记本上安装会是小菜一碟&…

Linux内核源码分析 (B.x)Linux内存布局

一、32位系统的内存布局 为什么要将进程地址空间划分成内核空间和用户空间&#xff1f; 这个和处理器的体系结构有关。比如X86分为ring0~ring3级别&#xff0c;ring0给内核空间使用&#xff0c;ring3给用户空间使用&#xff1b;同样的&#xff0c;ARMv7也是如此&#xff0c;svc…

openGauss学习笔记-70 openGauss 数据库管理-创建和管理普通表-查看表数据

文章目录 openGauss学习笔记-70 openGauss 数据库管理-创建和管理普通表-查看表数据70.1 查询数据库所有表的信息70.2 查询表的属性70.3 查询表的数据量70.4 查询表的所有数据70.5 查询字段的数据70.6 过滤字段的重复数据70.7 查询字段为某某的所有数据70.8 按照字段进行排序 o…

C++之unordered_map,unordered_set模拟实现

unordered_map&#xff0c;unordered_set模拟实现 哈希表源代码哈希表模板参数的控制仿函数增加正向迭代器实现*运算符重载->运算符重载运算符重载! 和 运算符重载begin()与end()实现 unordered_set实现unordered_map实现map/set 与 unordered_map/unordered_set对比哈希表…

python 自(3)1使用urlencode多个参数请求使用 2百度翻译post请求post无法添加路径 3百度翻译全部数据获取 4豆瓣get请

1 使用urlencode 多个参数请求使用 # 使用urlencode 多个参数请求使用 # https://www.baidu.com/s?wd周杰伦&sex男 网页 import urllib.request import urllib.parsebase_url https://www.baidu.com/s?data {wd: 周杰伦,sex: 男,sing:歌曲 }new_data urllib.par…

牛客: BM3 链表中的节点每k个一组翻转

牛客: BM3 链表中的节点每k个一组翻转 文章目录 牛客: BM3 链表中的节点每k个一组翻转题目描述题解思路题解代码 题目描述 题解思路 用一个[]int保存一组节点的val,一个快节点先遍历k个节点将节点的val顺序保存在[]int中,然后慢节点再遍历k个节点,逆序将[]int的val设置给节点的…

北斗导航 | 基于奇异值分解的接收机自主完好性监测算法

===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 基于奇异值分解的接收机自主完好性监测算法 摘 要:基于最小二乘法残差…

Python Opencv实践 - 视频文件操作

参考资料&#xff1a; 视频处理VideoCapture类---OpenCV-Python开发指南&#xff08;38&#xff09;_python opencv videocapture_李元静的博客-CSDN博客 OpenCV VideoCapture.get()参数详解 - 简书FOURCC四字符码对照表_4fvcc_Kellybook的博客-CSDN博客 import cv2 as cv im…

【计算机网络】传输层协议——TCP(下)

文章目录 1. 三次握手三次握手的本质是建立链接&#xff0c;什么是链接&#xff1f;整体过程三次握手过程中报文丢失问题为什么2次握手不可以&#xff1f;为什么要三次握手&#xff1f; 2. 四次挥手整体过程为什么要等待2MSL 3. 流量控制4. 滑动窗口共识滑动窗口的一般情况理解…

星际争霸之小霸王之小蜜蜂(十三)--接着奏乐接着舞

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十二&#xff09;--猫有九条命 星际争霸之小霸王之小蜜蜂&#xff08;十一&#xff09;--杀杀杀 星际争霸之小霸王之小蜜蜂&#xff08;十&#xff09;--鼠道 星际争霸之小霸王之小蜜蜂&#xff08;九&#xff09;--狂鼠之…

国家网络安全周 | 天空卫士荣获“2023网络安全优秀创新成果大赛优胜奖”

9月11日上午&#xff0c;四川省2023年国家网络安全宣传周在泸州开幕。在开幕式上&#xff0c;为2023年网络安全优秀创新成果大赛——成都分站赛暨四川省“熊猫杯”网络安全优秀作品大赛中获奖企业颁奖&#xff0c;天空卫士银行数据安全方案获得优秀解决方案奖。 本次比赛由四川…

免费好用的天翎bpm流程引擎,实现生产管理系统

1.什么是生产管理系统 针对中小型制造企业的生产应用而开发&#xff0c;能够帮助企业建立一个规范准确即时的生产数据库&#xff0c;同时实现轻松、规范、细致的生产业务、库存业务一体化管理工作。提高管理效率&#xff08;企业管理的科学方法&#xff09;、掌握及时、准确、全…

Mysql高级——索引创建和使用

索引的创建 1. 索引的声明与使用 1.1 索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 从功能逻辑上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 按照物理实现方式&#xff…

Spring之IOC容器(依赖注入)基本介绍基本配置多模块化

标题一&#xff1a;什么是spring&#xff0c;它能够做什么? Spring是一个开源框架&#xff0c;它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而&#xff0c;Spring的用途不仅限于服务器端的…

【计算机视觉 | CNN】Image Model Blocks的常见算法介绍合集(一)

文章目录 一、Residual Block二、Bottleneck Residual Block三、Dense Block四、Squeeze-and-Excitation Block五、Inception Module六、Non-Local Block七、Spatial Attention Module八、Spatial Transformer九、ResNeXt Block十、Fire Module十一、Inception-v3 Module十二、…

数据结构与算法(C语言版)P2---线性表之顺序表

前景回顾 #mermaid-svg-sXTObkmwPR34tOT4 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-sXTObkmwPR34tOT4 .error-icon{fill:#552222;}#mermaid-svg-sXTObkmwPR34tOT4 .error-text{fill:#552222;stroke:#552222;}#…

拥有这个中文版CustomGPT,你也能定制自己的AI问答机器人

人工智能技术的快速发展为各行各业带来了前所未有的机会&#xff0c;其中之一就是定制化的问答机器人。这些机器人可以用于客户支持、知识管理、虚拟助手等多个领域&#xff0c;帮助企业提高效率&#xff0c;提供更好的用户体验。很多人可能都知道通过CustomGPT能够设计自己的人…

Golang使用sqlx报错max_prepared_stmt_count超过16382

文章目录 背景mysql的预处理查看实例预处理详情com_stmt_prepare开启performance_schema 本地查看预处理语句 预处理语句飙升的原因生成预处理语句但是不close执行sql过程中发生错误 go服务分析抓包分析发送给mysql的包debug查看预处理细节sqlx发送statement command指令sqlx关…

伦敦银时走势与获利机会

交易时间灵活、资金杠杆充沛是伦敦银交易的主要优势&#xff0c;投资者应该充分利用这个品种的制度优势&#xff0c;结合自己个人的作息时间&#xff0c;在工作、投资与生活三者之间取得平衡的前提下&#xff0c;借助国际白银市场的波动&#xff0c;通过交易逐步实现自己的财富…

外贸电商商品如何做好上架工作?

跨境电商业务的蓬勃发展已经成为互联网行业的热点话题之一。不论是将海外货源卖回国内&#xff0c;还是通过国内货源销往海外&#xff0c;跨境电商平台都面临着如何实现商品上架的关键问题。在这篇文章中&#xff0c;将探讨成功上架商品的关键步骤。 一、准备好接口。 跨境电商…