Linux驱动开发——(七)Linux阻塞和非阻塞IO

目录

一、阻塞和非阻塞IO简介

二、等待队列

2.1 等待队列头

2.2 等待队列项

2.3 将队列项添加/移除等待队列头 

2.4 等待唤醒

2.5 等待事件

三、轮询

四、驱动代码

4.1 阻塞IO

4.2 非阻塞IO


一、阻塞和非阻塞IO简介

IO指的是Input/Output,也就是输入/输出,是应用程序对驱动设备的输入/输出操作

当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式IO就会将应用程序对应的线程挂起,直到设备资源可以获取为止

对于非阻塞IO,应用程序对应的线程不会挂起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃,当设备不可用或数据未准备好的时候会立即向内核返回一个错误码,表示数据读取失败。应用程序会再次重新读取数据,这样一直往复循环,直到数据读取成功:

 应用程序使用如下代码来实现阻塞访问驱动设备文件:

int fd; 
int data = 0; fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞方式打开 */ 
ret = read(fd, &data, sizeof(data)); /* 读取数据 */

应用程序使用如下代码来实现非阻塞访问驱动设备文件:

int fd; 
int data = 0; fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞方式打开 */ 
ret = read(fd, &data, sizeof(data)); /* 读取数据 */

二、等待队列

2.1 等待队列头

阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里完成唤醒工作

Linux内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作,如果在驱动中使用等待队列,必须创建并初始化一个等待队列头,等待队列头使用结构体wait_queue_head_t表示,wait_queue_head_t结构体定义在文件include/linux/wait.h中:

struct __wait_queue_head { spinlock_t lock; struct list_head task_list; 
}; 
typedef struct __wait_queue_head wait_queue_head_t;

使用init_waitqueue_head函数初始化等待队列头:

void init_waitqueue_head(wait_queue_head_t *q)

q:初始化的等待队列头。
也可以使用宏DECLARE_WAIT_QUEUE_HEAD来一次性完成等待队列头的定义和初始化。

2.2 等待队列项

每个访问设备的进程都是一个队列项,当设备不可用时要将这些进程对应的等待队列项添加到等待队列里面。结构体wait_queue_t表示等待队列项:

struct __wait_queue { unsigned int flags; void *private; wait_queue_func_t func; struct list_head task_list;
}; 
typedef struct __wait_queue wait_queue_t;

使用宏DECLARE_WAITQUEUE来一次性完成等待队列项的定义和初始化:

DECLARE_WAITQUEUE(name, tsk)

name:等待队列项的名字;

tsk:表示这个等待队列项属于哪个任务 (进程),一般设置为current,在 Linux内核中current相当于一个全局变量,表示当前进程。

因此宏DECLARE_WAITQUEUE就是给当前正在运行的进程创建并初始化一个等待队列项。

2.3 将队列项添加/移除等待队列头 

当设备不可访问的时候需要将进程对应的等待队列项添加到前面创建的等待队列头中,添加到等待队列头中以后进程进入休眠态。当设备可访问后再将进程对应的等待队列项从等待队列头中移除。

等待队列项添加API函数:

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

q:等待队列项要加入的等待队列头。
wait:要加入的等待队列项。
返回值:无。

等待队列项移除API函数:

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) 

q:要删除的等待队列项所处的等待队列头。
wait:要删除的等待队列项。
返回值:无。

2.4 等待唤醒

当设备可使用时就要唤醒进入休眠态的进程,唤醒可以使用如下两个函数:

void wake_up(wait_queue_head_t *q) 
void wake_up_interruptible(wait_queue_head_t *q)

q:要唤醒的等待队列头,这两个函数会将该等待队列头中的所有进程都唤醒。
wake_up函数可以唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进
程;wake_up_interruptible函数只能唤醒处于TASK_INTERRUPTIBLE状态的进程。

2.5 等待事件

除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程:

函数描述
wait_event(wq, condition)等待以wq为等待队列头的等待队列被唤醒,前提是condition条件必须满足 (为真 ),否则一直阻塞。此函数会将进程设置为TASK_UNINTERRUPTIBLE状态。
wait_event_timeout(wq, condition, timeout)功能和wait_event类似,但是此函数可以添加超时时间,以jiffies为单位。此函数有返回值,如果返回0的话表示超时时间到,而且condition为假,返回1的话表示condition为真,也就是条件满足了。
wait_event_interruptible(wq, condition)与wait_event函数类似,但此函数将进程设置为TASK_INTERRUPTIBLE,即可以被信号打断。
wait_event_interruptible_timeout(wq, condition, timeout)与wait_event_timeout函数和wait_event_interruptible函数类似。

三、轮询

如果用户应用程序非阻塞访问设备,设备驱动程序就要提供非阻塞的处理方式,即轮询。

当应用程序通过selectepollpoll函数来查询设备是否可以操作时,设备驱动程序中的poll操作函数就会执行,如果可以操作的话就从设备读取或者向设备写入数据:

unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)

filp:要打开的设备文件(文件描述符)。
wait结构体poll_table_struct类型指针,由应用程序传递进来的。一般将此参数传递给poll_wait函数。
返回值:向应用程序返回设备或者资源状态,可以返回的资源状态如表:

POLLIN有数据可以读取。
POLLPRI有紧急的数据需要读取。
POLLOUT可以写数据。
POLLERR指定的文件描述符发生错误。
POLLHUP指定的文件描述符挂起。
POLLNVAL无效的请求。
POLLRDNORM等同于 POLLIN,普通数据可读。

需要在驱动程序的poll函数中调用poll_wait函数,poll_wait函数不会引起阻塞,只是将应用程序添加到poll_table中:

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

wait_address:要添加到poll_table中的等待队列头;

p:poll_table,即file_operations中poll操作函数的wait参数。


四、驱动代码

以Linux驱动开发——(六)按键中断实验的驱动代码为模板修改。

4.1 阻塞IO

添加宏:

#define IMX6UIRQ_NAME "blockio"

在imx6uirq设备结构体内添加变量:

wait_queue_head_t r_wait;

在定期器服务函数添加:

/* 唤醒进程 */
if(atomic_read(&dev->releasekey)) { /* 完成一次按键过程 */ /* wake_up(&dev->r_wait); */ wake_up_interruptible(&dev->r_wait); 
}

在按键初始化函数添加:

init_waitqueue_head(&imx6uirq.r_wait);

在read操作函数添加: 

DECLARE_WAITQUEUE(wait, current); /* 定义一个等待队列 */ if(atomic_read(&dev->releasekey) == 0) { /* 没有按键按下 */ add_wait_queue(&dev->r_wait, &wait); /* 添加到等待队列头 */ __set_current_state(TASK_INTERRUPTIBLE);/* 设置任务状态 */ schedule(); /* 进行一次任务切换 */ if(signal_pending(current)) { /* 判断是否为信号引起的唤醒 */ ret = -ERESTARTSYS; goto wait_error; } __set_current_state(TASK_RUNNING); /*设置为运行状态 */ remove_wait_queue(&dev->r_wait, &wait); /*将等待队列移除 */ 
}wait_error: set_current_state(TASK_RUNNING); /* 设置任务为运行态 */ remove_wait_queue(&dev->r_wait, &wait); /* 将等待队列移除 */return ret;

4.2 非阻塞IO

添加宏:

#define IMX6UIRQ_NAME "noblockio"

在read操作函数添加:

if (filp->f_flags & O_NONBLOCK) { /* 非阻塞访问 */ if(atomic_read(&dev->releasekey) == 0) /* 没有按键按下 */ return -EAGAIN; 
} else { /* 阻塞访问 */ /* 加入等待队列,等待被唤醒,也就是有按键按下 */ ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) { goto wait_error; } 
} wait_error: return ret; 
data_error: return -EINVAL;

添加poll操作函数:

unsigned int imx6uirq_poll(struct file *filp, 
struct poll_table_struct *wait) 
{ unsigned int mask = 0; struct imx6uirq_dev *dev = (struct imx6uirq_dev *) filp->private_data; poll_wait(filp, &dev->r_wait, wait); if(atomic_read(&dev->releasekey)) { /* 按键按下 */ mask = POLLIN | POLLRDNORM; /* 返回PLLIN */ }return mask; 
}static struct file_operations imx6uirq_fops = { .poll = imx6uirq_poll, 
};

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

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

相关文章

如何解决冲突性需求,看看TRIZ怎么做

​本田公司的产品经理(本田的产品经理被称为是大型产品领导人,large product leader)在设计第三代雅阁的时候,面临的需求主要集中在三个方面:1、视野要好;2、空间要大;3、发动机要强劲。 每一个…

TCP关闭连接时的一些思考

TCP协议是TCP/IP栈中最复杂的协议,它最大的优点是传输的可靠性,这通过面向连接、按序传输、超时重传、流量控制等机制保证其传输的可靠性。但这并不是我们今天要讨论的重点! TCP通信的过程分别是三个阶段:建立连接、传输数据、关…

图论基础知识 深度优先(Depth First Search, 简称DFS),广度优先(Breathe First Search, 简称DFS)

图论基础知识 学习记录自代码随想录 dfs 与 bfs 区别 dfs是沿着一个方向去搜,不到黄河不回头,直到搜不下去了,再换方向(换方向的过程就涉及到了回溯)。 bfs是先把本节点所连接的所有节点遍历一遍,走到下…

从单按键状态机思维扫描引申到4*4矩阵按键全键无冲扫描,一步一步教,超好理解,超好复现(STM32程序例子HAL库)

目前大部分代码存在的问题 ​ 单次只能对单个按键产生反应;多个按键按下就难以修改;并且代码耦合度较高,逻辑难以修改,对于添加长按,短按,双击的需求修改困难。 解决 16个按键按下无冲,并且代…

如何在CentOS本地搭建DataEase数据分析服务并实现远程查看数据分析

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具,帮助用户快速分析数据并洞察业务趋势,从而实现业务…

【项目分享】用 Python 写一个桌面倒计日程序!

事情是这样的,我们班主任想委托我做一个程序,能显示还有几天考试。我立即理解了这个意思,接下了这个项目。 话不多说,来看看这个项目吧—— 项目简介 仓库地址:https://gitee.com/yaoqx/desktop-countdown-day 这是 …

幻兽帕鲁中文怎么设置 游戏中文修改方法 《幻兽帕鲁》宠物指定配种显示英文解决方法 幻兽帕鲁Steam游戏解说合集 Mac玩Windows游戏

在广阔的世界中收集神奇的生物“帕鲁”,派他们进行战斗、建造、做农活,工业生产等,这是一款支持多人游戏模式的全新开放世界生存制作游戏。幻兽帕鲁支持多人在线捕捉“帕鲁”,展开丰富的冒险玩法;不同的关卡具有不同的…

Bellman Ford算法:解决负权边图的最短路径问题

Bellman Ford算法的介绍 在计算机科学的世界中,Bellman Ford算法是一种解决单源最短路径问题的算法,它可以处理有负权边的图。这个算法的名字来源于两位科学家Richard Bellman和Lester Randolph Ford,他们是这个算法的发明者。 这个算法的主…

AI图书推荐:2024年ChatGPT副业搞钱指南

本书《2024年ChatGPT副业搞钱指南》(ChatGPT Side Hustles 2024)由Alec Rowe撰写,旨在指导读者如何利用ChatGPT技术来提升被动收入、创造新的现金流,并在数字化时代保持领先。 本书是深入了解被动收入未来的综合指南。本书揭示了超…

【算法基础实验】图论-基于DFS的连通性检测

基于DFS的连通性检测 理论基础 在图论中,连通分量是无向图的一个重要概念,特别是在处理图的结构和解析图的组成时。连通分组件表示图中的一个子图,在这个子图中任意两个顶点都是连通的,即存在一条路径可以从一个顶点到达另一个顶…

Flutter应用下拉菜单设计DropdownButtonFormField控件介绍

文章目录 DropdownButtonFormField介绍使用方法重点代码说明属性解释 注意事项 DropdownButtonFormField介绍 Flutter 中的 DropdownButtonFormField 是一个用于在表单中选择下拉菜单的控件。它是 DropdownButton 和 TextFormField 的组合,允许用户从一组选项中选择…

井字棋游戏

1. 游戏创建 1.1导包 from tkinter import * import numpy as np import math import tkinter.messagebox 1.2 窗口内容 1.2.1创建一个窗口 root Tk() # 窗口名称 root.title("井字棋 from Sun") 1.2.2 创建一个框架,将其放置在窗口中 Frame1 F…

汽车底盘域的学习笔记

前言:底盘域分为传统车型底盘域和新能源车型底盘域(新能源系统又可以分为纯电和混动车型,有时间可以再研究一下) 1:传统车型底盘域 细分的话可以分为四个子系统 传动系统 行驶系统 转向系统 制动系统 1.1传动系…

什么样的内外网文档摆渡,可以实现安全高效传输?

内外网文档摆渡通常指的是在内网(公司或组织的内部网络)和外网(如互联网)之间安全地传输文件的过程。这个过程需要特别注意安全性,因为内网往往包含敏感数据,直接连接内网和外网可能会带来安全风险。因此会…

设计模式——终止模式之两阶段终止模式

文章目录 1. 错误思路2. 两阶段终止模式2.1 利用 isInterrupted2.2 利用停止标记interrupt-打断park Two Phase Termination 在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。 1. 错误思路 使用线程对象的 stop() 方法停…

GEM TSU Interface Details and IEEE 1588 Support

摘要:Xilinx ZNYQ ULTRASCALE MPSOC的GEM和1588的使用 对于FPGA来说,只需要勾选一些znyq的配置就行了,其余的都是软件的工作; 所有配置都勾选之后,最终会露出来的接口如下: GEM需要勾选的配置如下&#xf…

15.Blender Eevee和Cycles渲染引擎对比

初步介绍 Eevee是实时渲染的引擎,会省略一些解算方式,尤其对光线和阴影 Cycles会考虑这些因素,所以会对光线和阴影的表达更加真实,有一个实时光线追踪的功能 Cycles渲染完之后,每移动一次画面,都会重新渲染…

政安晨:【Keras机器学习示例演绎】(十九)—— 可视化网络学习内容

目录 简介 设置 建立特征提取模型 设置梯度上升过程 设置端到端滤波器可视化回路 可视化目标层中的前 64 个滤波器 政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&…

基于Rust的多线程 Web 服务器

构建多线程 Web 服务器 在 socket 上监听 TCP 连接解析少量的 HTTP 请求创建一个合适的 HTTP 响应使用线程池改进服务器的吞吐量优雅的停机和清理注意:并不是最佳实践 创建项目 ~/rust ➜ cargo new helloCreated binary (application) hello package~/rust ➜ma…

kaggle之皮肤癌数据的深度学习测试

kaggle之皮肤癌数据的深度学习测试 近期一直在肝深度学习 很久之前,曾经上手搞过一段时间的深度学习,似乎是做轮胎花纹的识别,当初用的是TensorFlow,CPU版本的,但已经很长时间都没弄过了 现在因为各种原因&#xff…