Linux 阻塞和非阻塞 IO 实验

阻塞和非阻塞 IO 是 Linux 驱动开发里面很常见的两种设备访问模式,在编写驱动的时候一定要考虑到阻塞和非阻塞。本章我们就来学习一下阻塞和非阻塞 IO,以及如何在驱动程序中处理阻塞与非阻塞,如何在驱动程序使用等待队列和 poll 机制。

阻塞和非阻塞简介

这里的“IO”并不是我们学习 STM32 或者其他单片机的时候所说的“GPIO”(也就是引脚)。这里的 IO 指的是 Input/Output,也就是输入/输出,是应用程序对驱动设备的输入/输出操作。当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式 IO 就会将应用程序对应的线程挂起,直到设备资源可以获取为止。对于非阻塞 IO,应用程序对应的线程不会挂起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃。阻塞式 IO 如图 52.1.1.1所示:

图 52.1.1.1 中应用程序调用 read 函数从设备中读取数据,当设备不可用或数据未准备好的时候就会进入到休眠态。等设备可用的时候就会从休眠态唤醒,然后从设备中读取数据返回给应用程序。非阻塞 IO 如图 52.1.2 所示:

从图 52.1.1.2 可以看出,应用程序使用非阻塞访问方式从设备读取数据,当设备不可用或数据未准备好的时候会立即向内核返回一个错误码,表示数据读取失败。应用程序会再次重新读取数据,这样一直往复循环,直到数据读取成功。应用程序可以使用如下所示示例代码来实现阻塞访问:

从示例代码 52.1.1.1 可以看出,对于设备驱动文件的默认读取方式就是阻塞式的,所以我们前面所有的例程测试 APP 都是采用阻塞 IO。

如果应用程序要采用非阻塞的方式来访问驱动设备文件,可以使用如下所示代码:

第 4 行使用 open 函数打开“/dev/xxx_dev”设备文件的时候添加了参数“O_NONBLOCK”,表示以非阻塞方式打开设备,这样从设备中读取数据的时候就是非阻塞方式的了。

阻塞IO的驱动实现机制:等待队列

1、等待队列的队列头

阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU 资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里面完成唤醒工作。Linux 内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作,如果我们要在驱动中使用等待队列,必须创建并初始化一个等待队列头,等待队列头使用结构体wait_queue_head_t 表示,wait_queue_head_t 结构体定义在文件 include/linux/wait.h 中,结构体内容如下所示:

定义好等待队列头以后需要初始化,使用 init_waitqueue_head 函数初始化等待队列头,函数原型如下:

void init_waitqueue_head(wait_queue_head_t *q)

参数 q 就是要初始化的等待队列头。

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

2、等待队列的队列项

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

使用宏 DECLARE_WAITQUEUE 定义并初始化一个等待队列项,宏的内容如下:

DECLARE_WAITQUEUE(name, tsk)

name 就是等待队列项的名字,tsk 表示这个等待队列项属于哪个任务(进程),一般设置为current , 在 Linux 内 核 中 current 相 当 于 一 个 全 局 变 量 , 表 示 当 前 进 程 。 因 此 宏DECLARE_WAITQUEUE 就是给当前正在运行的进程创建并初始化了一个等待队列项。

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:要删除的等待队列项。

返回值:无。

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 状态的进程。

5、等待事件

除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程,和等待事件有关的 API 函数如表 52.1.2.1 所示:

阻塞IO的驱动实现机制:poll 操作函数

如果用户应用程序以非阻塞的方式访问设备,可以通过 select、epoll 或 poll 函数来查询设备是否可以操作,如果可以操作的话就从设备读取或者向设备写入数据。当应用程序调用 select、epoll 或 poll 函数的时候设备驱动程序中的 poll 函数就会执行,因此需要在设备驱动程序中编写 poll 函数。

关于应用程序中使用的 select、poll 和 epoll 这三个函数,参考我的另一篇文章:

Linux-IO多路复用_linux io多路复用方式-CSDN博客

当应用程序调用 select 或 poll 函数来对驱动程序进行非阻塞访问的时候,驱动程序file_operations 操作集中的 poll 函数就会执行。所以驱动程序的编写者需要提供对应的 poll 函数,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 中,poll_wait 函数原型如下:

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 参数。

使用示例如下所示:

阻塞 IO 实验

在上一章 Linux 中断实验中,我们直接在应用程序中通过 read 函数不断的读取按键状态,当按键有效的时候就打印出按键值。这种方法有个缺点,那就是 imx6uirqApp 这个测试应用程序拥有很高的 CPU 占用率,大家可以在开发板中加载上一章的驱动程序模块 imx6uirq.ko,然后以后台运行模式打开 imx6uirqApp 这个测试软件,命令如下:

./imx6uirqApp /dev/imx6uirq &

测试驱动是否正常工作,如果驱动工作正常的话输入“top”命令查看 imx6uirqApp 这个应用程序的 CPU 使用率,结果如图 52.2.1 所示:

从图 52.2.1 可以看出,imx6uirqApp 这个应用程序的 CPU 使用率竟然高达 99.6%,这仅仅是一个读取按键值的应用程序,这么高的 CPU 使用率显然是有问题的!原因就在于我们是直接在 while 循环中通过 read 函数读取按键值,因此 imx6uirqApp 这个软件会一直运行,一直读取按键值,CPU 使用率肯定就会很高。最好的方法就是在没有有效的按键事件发生的时候,imx6uirqApp 这个应用程序应该处于休眠状态,当有按键事件发生以后 imx6uirqApp 这个应用程序才运行,打印出按键值,这样就会降低 CPU 使用率,本小节我们就使用阻塞 IO 来实现此功能。

也就是说,虽然应用层读写时默认是阻塞的,但是也要驱动层的支持,否则无法阻塞和唤醒。

具体参考正点原子《第五十二章 Linux 阻塞和非阻塞 IO 实验

使用等待队列实现阻塞访问重点注意两点:

①、将任务或者进程加入到等待队列头,

②、在合适的点唤醒等待队列,一般都是中断处理函数里面。

非阻塞 IO 实验

非阻塞方式需要驱动层poll函数的支持。

具体参考正点原子《第五十二章 Linux 阻塞和非阻塞 IO 实验

本文不赘述。

对poll非阻塞IO的理解

先理解阻塞与非阻塞。阻塞就是由于工作不能立刻完成,进程放弃cpu,非阻塞就是任务不能立刻完成,函数直接返回或者一直轮询操作。起初想不明白如果对文件的可读进行poll的话,如果没有数据,进程就会睡眠从而引起切换,这不是典型的阻塞吗?

从几个方面来理解下:

1、从read的层面来看,阻塞指的是read时阻塞,不阻塞也是指read时不阻塞,虽然poll时在poll这里阻塞了,但是随后的read并不会阻塞;

2、poll可以执行非阻塞的操作,并不是说不能执行阻塞的操作,poll函数有个timeout,当timeout为0时,就是完全的非阻塞;当timeout为-1时,就是完全的阻塞;当timeout为任意正数的时候,就是有条件地阻塞;

这里有个问题,poll因为是非阻塞的,所以需要在应用层轮询是否有文件描述符准备好,当timeout为0时,是否cpu占用也会很高呢?

有个epoll的说法可以参考:Epoll 的time_out参数引发的cpu占用问题_epoll timeout-CSDN博客

不过,对于poll来说,貌似并不会,参考:poll函数是否消耗CPU - 你的KPI完成了吗 - 博客园

这篇文章里的意思是,如果timeout为0,当循环执行到poll的时候,还没有文件描述符准备好的话,就会引起任务调度?所以不会导致cpu都用在调用poll的while(1)循环里。

具体待实操确定。

3、poll的主要作用是多路IO监测,所以并不需要特别关注它的阻塞性;

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

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

相关文章

【机器学习】聚类算法|KMeans实现流程|SSE误差平法和|SC轮廓系数法|顾客数据聚类分析案例

文章目录 聚类算法聚类算法简介聚类算法分类 聚类算法API案例 使用KMeans模型数据探索聚类 KMeans实现流程***模型评估方法误差平方和 SSE(The sum of squares due to error)“肘”方法 (Elbow method) - K值确定 SC轮廓系数法(Silhouette Coefficient)聚…

微服务--OpenFeign【重点】

如果哪天 我们硬编码写的接口变了,只要写过该接口的 都要改,太麻烦了, 所以 就用 OpenFeign 来解决这个麻烦 了解: SimpleClientHttpRequestFactory和 HttpComponentsClientHttpRequestFactory 都是Spring框架中用于创建ClientH…

sentinel原理源码分析系列(六)-统计指标

调用链和统计节点构建完成,进入统计指标插槽,统计指标在最后执行的,等后面的插槽执行完,资源调用完成了,根据资源调用情况累计。指标统计是最重要的插槽,所有的功能都依靠指标数据,指标的正确与…

尤雨溪都点赞的表单校验解决方案,到底多么强

尤雨溪都点赞的表单校验解决方案,到底多么强 如果你是 Vue 开发者,那么 Vorms 绝对是你不能错过的表单验证利器。本文将带你快速了解 Vorms 的基本功能、特点和如何简单使用它提升你的开发体验。 软件简介 Vorms 是一个基于 Vue 3 组合式 API 的表单验证…

STL——string类

前言 从本篇博客开始,就正式来介绍STL的正式内容,STL在C中的学习中非常重要,具有举足轻重的地位,这块儿内容会给我们提供很多现成的接口,可以大大简化我们的代码,我们之前用C语言写的代码将会被极大地简化…

【数据分享】全国能源-电力平衡表(2000-2020年)

数据介绍 一级标题指标名称单位能源电力可供量亿千瓦小时能源电力生产量亿千瓦小时能源水电生产电力量亿千瓦小时能源火电生产电力量亿千瓦小时能源核电生产电力量亿千瓦小时能源风电生产电力量亿千瓦小时能源电力进口量亿千瓦小时能源电力出口量亿千瓦小时能源电力能源消费总量…

007、链表的回文结构

0、题目描述 链表回文结构 1、法1 一个复杂的问题可以拆解成几个简单的问题,找中间节点和逆置链表(翻转链表)之前都做过。 class PalindromeList { public://1、找中间节点ListNode* FindMid(ListNode* A){if (A nullptr || A->next …

设计模式03-装饰模式(Java)

4.4 装饰模式 1.模式定义 不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。 2.模式结构 抽象构件角色 :定义一个抽象接口以规范准备接收附加责任的对象。客户端可以方便调用装饰类和被装饰类…

基于STM32的电动汽车遥控器设计

引言 本项目设计了一个基于STM32的电动汽车遥控器,能够通过无线通信(如蓝牙或射频模块)控制电动汽车的前进、后退、左右转向等动作。该遥控器采用按键或摇杆操作,并通过无线模块将控制指令发送给汽车控制端,实现远程操…

吴恩达深度学习笔记(8)

计算机视觉 包括:图像分类也叫做图像识别、目标检测等 一个小的图像可能1M,但是他的像素是一个超级大向量,如果直接深度学习那么运算量会很大,因此需要运用卷积运算。 卷积运算是卷积神经网络的基础单元之一。下面用边缘检测理…

FastGPT本地开发 之 通过Navicat管理MongoDB、PostgreSQL数据库

1. 背景 前期已经完成FastGPT的本地化部署工作,通过Docker启动FastGPT的相关容器即可运行。(共6个容器) 2.本地化开发 2.1 前置依赖 2.2 源码拉取 git clone gitgithub.com:labring/FastGPT.git2.3 数据库管理 本地化运行的FastGPT使用…

leetcode73矩阵置零

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例 2: 输入&…

【含开题报告+文档+PPT+源码】基于SpringBoot和Vue的编程学习系统

开题报告 随着信息技术的迅猛发展和数字化转型的深入推进,编程技能已经成为现代社会中不可或缺的一项基本能力。无论是软件开发、数据分析还是人工智能等领域,编程都扮演着至关重要的角色。因此,培养和提高编程技能对于个人职业发展和社会创…

eNSP静态路由

1、实现全网通,考虑环形拓扑的优势。 R12: [Huawei]interface GigabitEthernet 0/0/0 [Huawei-GigabitEthernet0/0/0]ip address 192.168.1.1 24[Huawei]interface GigabitEthernet 0/0/1 [Huawei-GigabitEthernet0/0/1]ip address 192.168.2.1 24[Huawei]interfa…

软件设计模式------工厂方法模式

工厂方法模式(Factory Method Pattern),又称工厂模式,也叫虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Pactory Pattern),属于类创建型模式。 我们知道…

Spring数据源对象管理:基于XML配置文件的第三方bean管理

前言 第三方资源配置管理 ioc容器和依赖管理,我们大多数管理的是自己创建的bean,如果是第三方提供的bean是如何管理,我们以数据源对象的ioc管理为例子进行说明。 步骤 第一步: 需要导入相应的依赖包(导入坐标&#xf…

解构OpenAI swarm:利用Cursor进行框架分析与示例运行

解构OpenAI SWARM:利用Cursor进行框架分析与示例运行 1. 引言 在AI技术日新月异的今天,OpenAI再次为我们带来了惊喜。SWARM框架作为其最新研究成果,正在开创多智能体协作的新纪元。本文将带您深入探索这一框架,通过Cursor工具进行代码分析,并手把手教您安装运行SWARM。无论您…

Data+AI下的数据湖和湖仓一体发展史

DataAI下的数据湖和湖仓一体发展史 前言数据湖的“前世今生”AI时代的救星:湖仓一体湖仓一体实践演进未来趋势:智能化、实时化结语 前言 数据湖?湖仓一体?这是什么高科技新名词? 别急,我们慢慢聊。想象一…

机器学习:opencv--风格迁移

目录 前言 一、代码及步骤解释 1.图片与处理 2.加载模型 3.输出图像 前言 风格迁移(Style Transfer)是一种计算机视觉技术,旨在将一种图像的艺术风格应用到另一种图像上,同时保持其内容。 一、代码及步骤解释 1.图片与处理 …