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.hqwc.cn/news/651837.html

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

相关文章

Ubuntu16.04搭建webrtc服务器

本人查阅无数资料,历时3周搭建成功 一、服务器组成 AppRTC 房间+Web服务器 https://github.com/webrtc/apprtcCollider 信令服务器,在AppRTC源码里CoTurn coturn打洞+中继服务器 Nginx 服务器,用于Web访问代理和Websocket代理。AppRTC 房间+Web服务器使用python+js语言 App…

数字文旅重塑旅游发展新格局:以数字化转型为突破口,提升旅游服务的智能化水平,为游客带来全新的旅游体验

随着信息技术的迅猛发展,数字化已成为推动各行各业创新发展的重要力量。在旅游业领域,数字文旅的兴起正以其强大的驱动力,重塑旅游发展的新格局。数字文旅以数字化转型为突破口,通过提升旅游服务的智能化水平,为游客带…

硬件21、接线端子XH2.54、2.54排针排母、2510接插件、PH2.0、町洋接线端子5.08、ISP接口JTAG插座

XH2.54端子的间距为2.54毫米,2.54排针排母的间距也是2.54mm,2510接插件也是2.54、而PH2.0端子的间距为2.0毫米,町洋接线端子插针间的距离是5.08mm,ISP接口JTAG插座针脚的间距一般也是2.54mm XH2.54 针脚间距为2.54mm 插头 接线…

【算法基础实验】图论-UnionFind连通性检测之quick-find

Union-Find连通性检测之quick-find 理论基础 在图论和计算机科学中,Union-Find 或并查集是一种用于处理一组元素分成的多个不相交集合(即连通分量)的情况,并能快速回答这组元素中任意两个元素是否在同一集合中的问题。Union-Fin…

实验7:路由冗余协议HSRP配置管理(课内实验以及解答)

实验目的及要求: 理解首跳冗余协议(FHRP)的工作原理,掌握热备份路由器协议 (HSRP)(思科私有协议)原理和配置。能够实现网络终端设备虚拟网关的配置和网络故障的灵活切换,完成相应网络的联通性测…

【教程】Google AdSense填报美国和新加坡免税税务信息

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,请不吝给个[点赞、收藏、关注]哦~ 美国免税 如何填写 Google Adsense 税务信息可以免税 | W-8BEN | 紫竹的博客 新加坡免税 中国区AdSense如何添加新加坡税务信息?税…

光伏无人机:巡检无人机解决巡检难题

随着科技的飞速发展,无人机技术已经广泛应用于各个领域,其中光伏无人机在解决光伏电站巡检难题方面发挥了重要作用。光伏无人机以其高效、精准、安全的特点,为光伏电站的巡检工作带来了革命性的变革。 光伏电站通常位于广阔的户外场地&#x…

SpringCloudAlibaba:2.1nacos

概述 概述 简介 Nacos是阿里巴巴开源的服务注册中心以及配置中心 Nacos注册中心Eureka 服务配置Config 服务总线Bus 官网 Nacos官网 | Nacos 官方社区 | Nacos 下载 | Nacos 名字由来 Naming:名字 Configurations:配置 Service:服务 功能…

hive启动beeline报错

问题一在zpark启动集群报错 出现上面的问题执行以下代码 chmod 777 /opt/apps/hadoop-3.2.1/logs 问题二启动beeline报错 执行 cd /opt/apps/hadoop-3.2.1 bin/hadoop dfsadmin -safemode leave 问题三执行查询语句报错 执行 set hive.exec.mode.local.autotrue;

uniapp 微信小程序 获取openid,手机号进行登录,配合后端

流程&#xff1a;登录注册功能,通过uni.getUserProfile获取wxcode,通过wxcode传给后端获取openid,sessionkey,unionid。 通过<u-button type"success" open-type"getPhoneNumber" getphonenumber"decryptPhoneNumber">一键登录</u-butt…

linux系统-FTP服务配置

目录 一、FTP简介 1.什么是FTP&#xff1f;&#xff1f;&#xff1f; 2.FTP的两种模式 二、安装配置FTP服务 1.关闭防火墙和核心防护 2.安装VSFTPD 3.修改配置文件 4.黑白名单设置 一、FTP简介 1.什么是FTP&#xff1f;&…

分布式WEB应用中会话管理的变迁之路

Session一词直译为“会话”&#xff0c;意指有始有终的一系列动作&#xff0f;消息。Session是Web应用蓬勃发展的产物之一&#xff0c;在Web应用中隐含有“面向连接”和“状态保持”两个含义&#xff0c;同时也指代了Web服务器与客户端之间进行状态保持的解决方案。 在Web应用…