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

【IMX6ULL驱动开发学习】04.应用程序和驱动程序数据传输和交互的4种方式:非阻塞、阻塞、POLL、异步通知

一、数据传输

1.1 APP和驱动 

APP和驱动之间的数据访问是不能通过直接访问对方的内存地址来操作的,这里涉及Linux系统中的MMU(内存管理单元)。在驱动程序中通过这两个函数来获得APP和传给APP数据:

  • copy_to_user
  • copy_from_user

简单来讲,应用程序与内核/驱动程序在物理空间上是隔离开的,应用程序和驱动程序是不可能互相访问到的。驱动程序里的copy_from_user得到应用层传来的数据,驱动程序可以使用copy_to_user把数据发给应用程序,即应用程序和驱动程序通过这两个函数交换数据。

1.2 驱动和硬件

  • 各个子系统函数
  • 通过ioremap映射寄存器地址后,直接访问寄存器

驱动程序操作硬件可以通过子系统的方式(调用函数)来操作硬件;或者用最原始的办法ioremap,映射寄存器的地址(不是直接操作寄存器地址),这样在驱动程序里就可以访问寄存器了。

二、APP使用驱动的4种方式

驱动程序:提供能力,不提供策略(驱动程序提供各种作用的函数,供应用程序抉择并使用)。

2.1 非阻塞(查询)

如果在应用程序里open这个argv[1](设备节点)时,指定了非阻塞,表示读数据时,如果没有数据并且这个文件的flag是非阻塞,则立刻返回一个错误。APP指定了非阻塞方式,驱动程序是否判断它的flag完全由用户决定。

//应用程序
//O_RDWR可读可写,O_NONBLOCK非阻塞方式
fd = open(argv[1], O_RDWR | O_NONBLOCK);
//驱动程序的read
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;int key;if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_wait, !is_key_buf_empty());key = get_key();err = copy_to_user(buf, &key, 4);	return 4;
}

2.2 阻塞(休眠+唤醒)

如果一开始buf里没有数据,APP调用读函数,驱动程序读函数会进入wait_event_interruptible里休眠(放弃运行,不是死等),等待被唤醒。所以我们经常看到read函数很久没有返回,是因为在驱动程序里休眠了。该事件会记录在gpio_wait队列中。

//应用程序
//O_RDWR可读可写,不设置非阻塞
fd = open(argv[1], O_RDWR);
//驱动程序的read
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;int key;if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_wait, !is_key_buf_empty());key = get_key();err = copy_to_user(buf, &key, 4);	return 4;
}

通常配合中断+定时器的方式来唤醒该队列里面等待唤醒的进程/线程。

//中断函数
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc = dev_id;printk("gpio_key_isr key %d irq happened\n", gpio_desc->gpio);//定时器 用来消除抖动//修改定时器的超时时间= jiffies(当前时间) + 赫兹/5mod_timer(&gpio_desc->key_timer, jiffies + HZ/5);return IRQ_HANDLED;//成功处理
}
//定时器超时函数
static void key_timer_expire(unsigned long data)
{struct gpio_desc *gpio_desc = (struct gpio_desc *)data;int val;int key;val = gpio_get_value(gpio_desc->gpio);key = (gpio_desc->key) | (val<<8);put_key(key);//按键值放入环形缓冲区//唤醒队列中的进程/线程wake_up_interruptible(&gpio_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

2.3 POLL(休眠+唤醒+超时时间)

2.3.1 POLL机制流程

使用休眠-唤醒的方式等待某个事件发生时,有一个缺点: 等待的时间可能很久。我们可以加上一个超时时间,这时就可以使用 poll 机制。poll机制流程如下6步:

①APP不知道驱动程序中是否有数据,可以先调用poll函数查询一下,poll函数可以传入超时时间;

②APP进入内核态,调用到驱动程序的poll函数,如果有数据的话立刻返回;

③如果发现没有数据时就休眠一段时间;

④当有数据时,比如当按下按键时,驱动程序的中断服务程序和定时器超时函数被调用,它会记录数据、唤醒APP;

⑤当超时时间到了之后,内核也会唤醒APP;

⑥APP根据poll函数的返回值就可以知道是否有数据,如果有数据就调用read得到数据。

2.3.2 POLL执行流程

                                                                 图1 poll机制

函数执行流程如上图①~⑧所示,重点从③开始看。假设一开始无按键数据:

③APP调用poll之后,进入内核态;

④在循环中执行程序,致驱动程序的drv_poll被调用;注意,drv_poll要把自己这个线程挂入等待队列 wq 中!,并没有休眠,且无数据返回0,有数据返回POLLIN;

⑤当前没有数据,则在内核态中休眠一会,等待超时内核唤醒中断+定时器唤醒;

中断+定时器唤醒情况:

⑥过程中,按下了按键,发生了中断+定时器超时函数,在定时器超时函数里记录了按键值,并且从gpio_wait队列中把线程唤醒了;

⑦从休眠中被唤醒,继续执行 for 循环,再次调用 drv_poll,在drv_poll中返回数据状态(POLLIN);

⑧有数据返回到内核态,内核态返回到应用态;

⑨APP调用read函数读数据。


超时内核唤醒情况:接着上面的⑤

⑥在休眠过程中,一直没有按下了按键,超时时间到,内核把这个线程唤醒;

⑦线程从休眠中被唤醒,继续执行 for 循环,再次调用 drv_poll,drv_poll返回数据状态

⑧还是没有数据,但是超时时间到了,那从内核态返回到应用态;

⑨APP不能调用 read 函数读数据。

需要注意一下几点!!!

  • drv_poll 要把线程挂入队列gpio_wait,但是并不是在 drv_poll 中进入休眠,而是在调用 drv_poll 之后休眠
  • drv_poll 要返回数据状态
  • APP 调用一次 poll,有可能会导致 drv_poll 被调用 2 次
  • 线程被唤醒的原因有 2个:中断(+定时器)发生了去队列gpio_wait中把它唤醒,超时时间到了内核把它唤醒
  • APP 要判断 poll 返回的原因:有数据,还是超时。有数据时再去调用read函数

2.3.3 POLL应用和驱动编程 

驱动程序中的poll代码

static unsigned int gpio_drv_poll(struct file *fp, poll_table * wait)
{poll_wait(fp, &gpio_wait, wait);return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

 驱动程序中的中断触发函数:按键消抖+修改了定时器超时时间

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc = dev_id;printk("gpio_key_isr key %d irq happened\n", gpio_desc->gpio);//定时器 用来消除抖动mod_timer(&gpio_desc->key_timer, jiffies + HZ/5);//修改定时器的超时时间= jiffies(当前时间) + 赫兹/5return IRQ_HANDLED;//成功处理
}

 定时器超时函数:获取按键值+储存按键值+唤醒线程

static void key_timer_expire(unsigned long data)
{struct gpio_desc *gpio_desc = (struct gpio_desc *)data;int val;int key;val = gpio_get_value(gpio_desc->gpio);key = (gpio_desc->key) | (val<<8);put_key(key);//按键值放入环形缓冲区wake_up_interruptible(&gpio_wait);//唤醒队列里的线程kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

应用程序代码:

struct pollfd fds[1]
int timeout_ms = 5000;
int ret;
int fd;
fds[0].fd = fd;           //查询fd这个文件
fds[0].events = POLLIN;   //POLLIN表示查询这个文件有没有数据让我读进来 fd = open(argv[1], O_RDWR);
if(fd == -1)
{printf("can not open file %s\n", argv[1]);
}while(1)
{ret = poll(fds, 1, timeout_ms);//ret为1表示fds结构体中有文件满足返回条件,且返回的事件是这个文件有数据让我读进来POLLINif((ret == 1) && (fds[0].revents & POLLIN)){read(fd, &val, 4);printf("get button : 0x%x\n", val);}else{printf("timeout\n");}}

 

2.4 异步通知

2.4.1 异步通知流程

使用休眠-唤醒、POLL机制时,都需要休眠等待某个事件发生时,它们的差别在于后者可以指定休眠的时长。如果APP不想休眠怎么办?也有类似的方法:驱动程序有数据时主动通知APP,APP收到信号后执行信息处理函数,这就是异步通知。


图2 异步通知的信号流程

重点从②开始:
② APP 给 SIGIO 这个信号注册信号处理函数 func,以后 APP 收到 SIGIO信号时,这个函数会被自动调用;
③ 把 APP 的 PID(进程 ID)告诉驱动程序,这个调用不涉及驱动程序,在内核的文件系统层次记录 PID;
④ 读取驱动程序文件 Flag;
⑤ 设置 Flag 里面的 FASYNC 位为 1:当 FASYNC 位发生变化时,会导致驱动程序的 fasync 被调用;
⑥⑦ 调 用 faync_helper , 它会根据FAYSNC的值决定是否设置button_async->fa_file=驱动文件 filp:驱动文件 filp 结构体里面含有之前设置的 PID。
⑧ APP 可以做其他事;
⑨⑩ 按下按键,发生中断,驱动程序的中断服务程序被调用,里面调用kill_fasync 发信号;
⑪⑫⑬ APP 收到信号后,它的信号处理函数被自动调用,可以在里面调用read 函数读取按键。

2.4.1 异步通知应用和驱动编程

应用程序:信号处理函数+注册信号处理函数+打开驱动+把进程ID告诉驱动+使能驱动的FASYNC功能

static void sig_func(int sig)
{int val;read(fd, &val, 4);printf("get button : 0x%x\n", val);
}signal(SIGIO, sig_func);fd = open(argv[1], O_RDWR);
if(fd == -1)
{printf("can not open file %s\n", argv[1]);
}fcntl(fd, F_SETOWN, getpid());         //告诉驱动程序,要给谁发信号
flags = fcntl(fd, F_GETFL);            //获得之前的flags
fcntl(fd, F_SETFL, flags | FASYNC);    //这是新的flags并使能驱动的FASYNC功能(使能异步通知)

驱动程序中的fasync被调用:使能异步通知后会调用这个辅助函数来构造结构体,结构体里存放进程id

//构造button_fasync结构体,结构体里存放进程id
static int gpio_drv_fasync(int fd, struct file *file, int on)
{if (fasync_helper(fd, file, on, &button_fasync) >= 0)return 0;elsereturn -EIO;
}

 驱动程序中的中断触发函数:按键消抖+修改了定时器超时时间

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc = dev_id;printk("gpio_key_isr key %d irq happened\n", gpio_desc->gpio);//定时器 用来消除抖动mod_timer(&gpio_desc->key_timer, jiffies + HZ/5);//修改定时器的超时时间= jiffies(当前时间) + 赫兹/5return IRQ_HANDLED;//成功处理
}

 定时器超时函数:最后一行发送信号SIGIO给进程,button_fasync结构体中有进程信息,发送信号后,应用程序中收到信号会打断while循环并先执行对应的信号处理函数,再回到while循环。

static void key_timer_expire(unsigned long data)
{struct gpio_desc *gpio_desc = (struct gpio_desc *)data;int val;int key;val = gpio_get_value(gpio_desc->gpio);key = (gpio_desc->key) | (val<<8);put_key(key);//按键值放入环形缓冲区wake_up_interruptible(&gpio_wait);//唤醒队列里的线程kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

相关文章:

【IMX6ULL驱动开发学习】04.应用程序和驱动程序数据传输和交互的4种方式:非阻塞、阻塞、POLL、异步通知

一、数据传输 1.1 APP和驱动 APP和驱动之间的数据访问是不能通过直接访问对方的内存地址来操作的&#xff0c;这里涉及Linux系统中的MMU&#xff08;内存管理单元&#xff09;。在驱动程序中通过这两个函数来获得APP和传给APP数据&#xff1a; copy_to_usercopy_from_user …...

day-21 代码随想录算法训练营(19)二叉树part07

530.二叉搜索树的最小绝对差 思路一&#xff1a;二叉搜索树的中序遍历必为升序数组&#xff0c;加入数组后计算相邻两个数差值&#xff0c;即可求出最小绝对差 思路二&#xff1a;同样的思路&#xff0c;中序遍历&#xff0c;直接使用指针记录上一个节点&#xff0c;同时更新…...

【Vue3】依赖注入

provide 和 inject 是 Vue.js 中用于实现依赖注入的两个关联功能。它们允许你在祖先组件中提供数据&#xff0c;然后在子孙组件中注入这些数据&#xff0c;实现组件之间的数据共享和传递。 provide&#xff1a;provide 是一个选项&#xff0c;你可以在父组件中通过它来提供数据…...

Vue 引入 Element-UI 组件库

Element-UI 官网地址&#xff1a;https://element.eleme.cn/#/zh-CN 完整引入&#xff1a;会将全部组件打包到项目中&#xff0c;导致项目过大&#xff0c;首次加载时间过长。 下载 Element-UI 一、打开项目&#xff0c;安装 Element-UI 组件库。 使用命令&#xff1a; npm …...

照耀国产的星火,再度上新!

国产之光&#xff0c;星火闪耀 ⭐ 新时代的星火⭐ 多模态能力⭐ 图像生成与虚拟人视频生成⭐ 音频生成与OCR笔记收藏⭐ 助手模式更新⭐ 插件能力⭐ 代码能力⭐ 写在最后 ⭐ 新时代的星火 在这个快速变革的时代&#xff0c;人工智能正迅猛地催生着前所未有的革命。从医疗到金融…...

大语言模型LLM的一些点

LLM发展史 GPT模型是一种自然语言处理模型&#xff0c;使用Transformer来预测下一个单词的概率分布&#xff0c;通过训练在大型文本语料库上学习到的语言模式来生成自然语言文本。 GPT-1(117亿参数)&#xff0c;GPT-1有一定的泛化能力。能够用于和监督任务无关的任务中。GPT-2(…...

leetcode810. 黑板异或游戏(博弈论 - java)

黑板异或游戏 lc 810 - 黑板异或游戏题目描述博弈论 动态规划 lc 810 - 黑板异或游戏 难度 - 困难 原题链接 - 黑板异或游戏 题目描述 黑板上写着一个非负整数数组 nums[i] 。 Alice 和 Bob 轮流从黑板上擦掉一个数字&#xff0c;Alice 先手。如果擦除一个数字后&#xff0c;剩…...

算法练习Day48|198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

LeetCode: 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 1.思路 边界思维&#xff0c;只有一个元素和两个元素的初始化考虑 当元素数大于3个时&#xff0c; 逆向思维&#xff0c;是否偷最后一个元素&#xff0c;倒序得出递推公式dp[i] Math.max(dp[i - 1], dp[i …...

什么是设计模式?常用的设计有哪些?

单例模式工厂模式代理模式&#xff08;proxy&#xff09; 一、设计模式 设计模式是前辈们经过无数次实践所总结的一些方法&#xff08;针对特定问题的特定方法&#xff09; 这些设计模式中的方法都是经过反复使用过的。 二、常用的设计模式有哪些&#xff1f; 1、单例模式&…...

clickHouse部署

docker仓库地址 https://hub.docker.com/ 1、docker环境搭建 # 1.先安装yml yum install -y yum-utils device-mapper-persistent-data lvm2 # 2.设置阿里云镜像 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo# 3.查…...

Flutter实现倒计时功能,秒数转时分秒,然后倒计时

Flutter实现倒计时功能 发布时间&#xff1a;2023/05/12 本文实例为大家分享了Flutter实现倒计时功能的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 有一个需求&#xff0c;需要在页面进行显示倒计时&#xff0c;倒计时结束后&#xff0c;做相应的逻辑处理。 实…...

【hadoop】windows上hadoop环境的搭建步骤

文章目录 前言基础环境下载hadoop安装包下载hadoop在windows中的依赖配置环境变量 Hadoop hdfs搭建创建hadfs数据目录修改JAVA依赖修改配置文件初始化hdfs namenode启动hdfs 前言 在大数据开发领域中&#xff0c;不得不说说传统经典的hadoop基础计算框架。一般我们都会将hadoo…...

一周在榜9本计算机专业新书

本周在榜计算机专业新书9本。 1、扩散模型从原理到实战 开启AI绘画新时代&#xff01;AIGC大模型来临&#xff0c;配套赠送Diffusion视频课程&#xff01; HuggingFace平台学习实战&#xff0c;常春藤盟校数据科学硕士与算法工程师带你从理论到实战&#xff0c;了解、掌握扩散…...

CSS变形与动画(二):perspctive透视效果 与 preserve-3d 3d效果(奥运五环例子)

文章目录 perspective 3d透视效果preserve-3d 3d嵌套效果例子 奥运五环 backface-visibility 背面效果 perspective 3d透视效果 perspective 指定了观察者与 z0 平面的距离&#xff0c;使具有三维位置变换的元素产生透视效果。z>0 的三维元素比正常大&#xff0c;而 z<0 …...

[论文笔记]Glancing Transformer for Non-Autoregressive Neural Machine Translation

引言 这是论文Glancing Transformer for Non-Autoregressive Neural Machine Translation的笔记。 传统的非自回归文本生成速度较慢,因为需要给定之前的token来预测下一个token。但自回归模型虽然效率高,但性能没那么好。 这篇论文提出了Glancing Transformer,可以只需要一…...

视觉学习(七)---Flask 框架下接口调用及python requests 实现json字符串传输

在项目实施过程中需要与其他系统进行接口联调&#xff0c;将图像检测的结果传递给其他系统接口&#xff0c;进行逻辑调用。这中间的过程可以通过requests库进行实现。 1.安装requests库 pip install requests2.postman 接口测试 我们先通过postman 了解下接口调用&#xff0…...

unity编写树形结构的文件管理页面

项目中需要实现点击“”按钮展开对应分类下的所有训练科目&#xff0c;再次点击“–”按钮将对应分类下的训练科目隐藏并收起整个面板。对此&#xff0c;编写一个类&#xff0c;将其挂载到树形结构的父类上&#xff0c;代码如下&#xff1a; using UnityEngine; using UnityEn…...

基于单片机的家用智能浇灌系统

1、开发环境 keil5&#xff0c;STM32CubeMX、Altium Designer 2、硬件清单 单片机&#xff1a;STM32F051K8Ux 土壤湿度传感器&#xff1a;TL - 69 温度传感器&#xff1a;DS18B20&#xff08;数字传感器直接输出数字信号&#xff09; OLED屏幕&#xff1a;OLED12864、 水…...

Solr的入门使用

Solr是Apache下的一个顶级开源项目&#xff0c;采用Java开发&#xff0c;它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言&#xff0c;同时实现了可配置、可扩展&#xff0c;并对索引、搜索性能进行了优化&#xff0c;被很多需要搜索的网站中广泛使用。…...

css鼠标样式 cursor: pointer

cursor: none; cursor:not-allowed; 禁止选择 user-select: none; pointer-events:none;禁止触发事件, 该样式会阻止默认事件的发生&#xff0c;但鼠标样式会变成箭头...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...