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.rhkb.cn/news/314328.html

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

相关文章

JavaWeb--06Vue组件库Element

Element 1 Element组件的快速入门1.1 Table表格 1 Element组件的快速入门 https://element.eleme.cn/#/zh-CN Element是饿了么团队开发的 接下来我们来学习一下ElementUI的常用组件,对于组件的学习比较简单,我们只需要参考官方提供的代码,然…

SSH新功能揭秘:远程工作提升指南【AI写作】

首先,这篇文章是基于笔尖AI写作进行文章创作的,喜欢的宝子,也可以去体验下,解放双手,上班直接摸鱼~ 按照惯例,先介绍下这款笔尖AI写作,宝子也可以直接下滑跳过看正文~ 笔尖Ai写作:…

同态加密原理解析

目录 1.数学介绍2.使用多项式环进行加密2.1 私钥和公钥的产生2.2 加密2.3 解密 3.同态计算3.1 同态加法3.2 同态乘法 1.数学介绍 同态加密方案基于一个难以计算的问题Ring Learning with Errorsred。这些方案中的数据在加密和未加密时都用多项式表示。 这里举一个简单的多项式…

什么是IIoT?

什么是IIoT? IIoT,即工业物联网(Industrial Internet of Things),是指将物联网技术应用到工业领域,通过微型低成本传感器、高带宽无线网络等技术手段,实现工业设备、系统和服务的互联互通,从而…

ChatGPT/GLM API使用

模型幻觉问题 在自然语言处理领域,幻觉(Hallucination)被定义为生成的内容与提供的源内容无关或不忠实,具体而言,是一种虚假的感知,但在表面上却似乎是真实的。产生背景 检索增强生成(RAG&…

基于MLP算法实现交通流量预测(Pytorch版)

在海量的城市数据中,交通流量数据无疑是揭示城市运行脉络、洞察出行规律的关键要素之一。实时且精准的交通流量预测不仅能为交通规划者提供科学决策依据,助力提升道路使用效率、缓解交通拥堵,还能为公众出行提供参考,实现个性化导…

算法 || 二分查找

目录 二分查找 在排序数组中查找元素的第一个和最后一个位置 搜索插入位置 一个数组经过划分后具有二段性的都可以用二分查找 二分查找 704. 二分查找 - 力扣(LeetCode) ​ 暴力解法:直接遍历数组,找到 target 便返回下标&am…

原型链prototype、__proto、constructor的那些问题整理

再了解原型链之前,我们先来看看构造函数和实例对象的打印结构 - 函数 这里我们定义一个构造函数Fn,然后打印它的结构吧 function Fn(){} console.dir(Fn)控制台得到结构 从上面结构我们能看的出来,函数有两种原型,一种是作为函数特有的原型:prototype,另一种是作为对象的__…

低代码技术的全面应用:加速创新、降低成本

引言 在当今数字化转型的时代,企业和组织面临着不断增长的应用程序需求,以支持其业务运营和创新。然而,传统的软件开发方法通常需要大量的时间、资源和专业技能,限制了企业快速响应市场变化和业务需求的能力。在这样的背景下&…

chrome浏览器安装elasticsearch的head可视化插件

head插件简介 elasticsearch-head被称为是弹性搜索集群的web前端,head插件主要是用来和elastic Cluster交互的Web前端 head插件历史 elasticsearch-head插件在0.x-2.x版本的时候是集成在elasticsearch内的,由elasticsearch的bin/elasticsearch-plugin…

区块链 | OpenSea 相关论文:Toward Achieving Anonymous NFT Trading(一)

​ 🥑原文: Toward Achieving Anonymous NFT Trading 🥑写在前面: 本文对实体的介绍基于论文提出的方案,而非基于 OpenSea 实际采用的方案。 其实右图中的 Alice 也是用了代理的,不过作者没有画出来。 正文…

基于SpringBoot + Vue实现的校园(通知、投票)管理系统设计与实现+毕业论文(12000字)+答辩PPT+指导搭建视频

目录 项目介绍 运行环境 技术栈 效果展示 论文展示 总结 项目介绍 本系统包含管理员、用户、院校管理员三个角色。 管理员角色:用户管理、院校管理、单位类别管理、院校管理员管理、单位管理、通知推送管理、投票信息管理、通知回复管理等。 用户角色&#…

sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?

🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…

数据结构—单链表

含义 1.顺序表的回顾 之前的文章已经谈到了顺序表,关于顺序表,有一下的几种特点 1.中间,头部的删除,复杂度为O(N); 2.增容会有申请新的空间,拷贝数据,释放旧的空间,会有不小的消耗&#xff…

数据结构:最小生成树(Prim算法和Kruskal算法)、图的最短路径(Dijkstra算法和Bellman-Ford算法)

什么是最小生成树?Prim算法和Kruskal算法是如何找到最小生成树的? 最小生成树是指在一个连通图中,通过连接所有节点并使得总权重最小的子图。 Prim算法和Kruskal算法是两种常用的算法,用于寻找最小生成树。 Prim算法的步骤如下&…

mysql的多表查询和子查询

多表查询:查询数据时,需要使用多张表来查询 多表查询分类: 1.内连接查询 2.外连接查询 3.子查询 笛卡尔积: create table class (id int primary key auto_increment,name varchar(10) ); create table student (id int primar…

学习STM32第十六天

RTC实时时钟 一、简介 RTC是一个独立的BCD格式定时器,提供一个时钟日历,两个可编程报警中断,一个具有中断功能周期性可编程唤醒标志,RTC和时钟配置系统处于后备区域。 通过两个32位寄存器以BCD格式实现秒、分钟、小时&#xff08…

stable-diffusion-webui安装与使用过程中的遇到的error合集

stable-diffusion-webui1.9.2踩坑安装 1. 安装过程1.1 stable-diffusion-webui1.2 在win11或win10系统安装,需修改两个启动脚本1.2.1 修改webui-user.bat1.2.2 修改webui.bat 1.3 双击 webui-user.bat 启动脚本1.3.1 no module xformers. Processing without on fre…

Grafana系列 | Grafana监控TDengine库数据 |Grafana自定义Dashboard

开始前可以去grafana官网看看dashboard文档 https://grafana.com/docs/grafana/latest/dashboards 本文主要是监控TDengine库数据 目录 一、TDengine介绍二、Grafana监控TDengine数据三、Grafana自定义Dashboard 监控TDengine库数据1、grafana 变量2、添加变量3、配置panel 一…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下: #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…