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

news2024/12/23 11:59:07

目录

一、阻塞和非阻塞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.coloradmin.cn/o/1627332.html

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

相关文章

Docker常见问题排查思路与实战

Docker作为一种流行的容器化技术,已经在众多场景中得到广泛应用。然而,在使用过程中,我们难免会遇到各种问题。本文将介绍一些常见的Docker问题及其排查思路,并通过实战案例帮助大家更好地理解和应对这些挑战。 1. Docker容器启动…

第⑰讲:Ceph集群各组件的配置参数调整

文章目录 1.Ceph集群各组件的配置文件1.1.Ceph各组件配置方式1.2.ceph临时查看、修改配置参数的方法 2.调整Monitor组件的配置参数删除Pool资源池2.1.临时调整配置参数2.2.永久修改配置参数 1.Ceph集群各组件的配置文件 1.1.Ceph各组件配置方式 Ceph集群中各个组件的默认配置…

【linux高性能服务器编程】项目实战——仿QQ聊天程序源码剖析

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之项目实战——仿QQ聊天程序源码剖析,在这篇文章中,你将会学习到如何利用Linux网络编程技术来实现一个简单的聊天程序,并且我会给出源码进行剖析&#xff…

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

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

使用工具速记

文章目录 一、sqlyoy登录账号信息迁移二、idea导入之前的已配置的idea信息三、设置windows UI大小四、其他 提示:以下是本篇文章正文内容,下面案例可供参考 一、sqlyoy登录账号信息迁移 工具(sqlyog上面菜单栏)->导入导出详情->选择要导出的账号…

day03-(docker)

文章目录 DockerDocker和虚拟机的差别docker在linux安装配置镜像命令容器命令介绍Docker-容器(基本操作)docker基本操作(数据卷)数据卷挂载直接挂载四.Dockerfile自定义镜像五.Docker-Compose 安装修改权限镜像仓库![在这里插入图…

Vscode上使用Clang,MSVC, MinGW, (Release, Debug)开发c++完全配置教程(包含常见错误),不断更新中.....

1.VSCode报错头文件找不到 clang(pp_file_not_found) 在Fallback Flags中添加 -I(是-include的意思,链接你的编译器对应头文件地址,比如我下面的是MSVC的地址) 问题得到解决~

Docker基本操作 容器相关命令

docker run:运行镜像; docker pause:暂停容器,会让该容器暂时挂起; docker unpauser:从暂停到运行; docker stop:停止容器,杀死进程; docker start:重新创建进程。 docker ps:查看所有运行的容器及其状态,默认只展…

城市建筑轮廓矢量边界、建设用地数据、城市道路网分布、城市土地利用规划分布、土地利用数据、城市绿地分布

数据下载链接:数据下载链接 中国主要城市建筑底面轮廓和建筑高度空间分布数据,包括省会城市、地级市及县级市等主要城市。城市建筑底面轮廓和建筑高度数据,数据坐标为 WGS84地理坐标, 数据格式为 SHP 文件。数据范围基本覆盖城市…

OceanBase开发者大会实录 - 阳振坤:云时代的数据库

本文来自2024 OceanBase开发者大会,OceanBase 首席科学家阳振坤的演讲实录——《云时代的数据库》。完整视频回看,请点击这里 >> 在去年的开发者大会中,我跟大家分享了我对数据库产品和技术一些看法,包括单机分布式一体化&…

openjudge_2.5基本算法之搜索_200:Solitaire

题目 200:Solitaire 总时间限制: 5000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB 描述 Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left to right resp…

maven-idea新建和导入项目

全局配置 新建项目 需要新建的文件夹 src/testsrc/test/javasrc/main/java 注:1、新建Java-class,输入.com.hello.hellomaven 2、快捷键psvm显示 public static void main(String[] args) {.... } package com.hello;public class hellomaven {publ…

Java-字符集和字符编码-roadmap

1 需求 2 接口 3 示例 4 参考资料 「烫烫屯屯锟斤拷」揭秘ASCII、GBK、UTF-8,B站独家,一听就懂_哔哩哔哩_bilibili 非常详细的字符编码讲解,ASCII、GB2312、GBK、Unicode、UTF-8等知识点都有_哔哩哔哩_bilibili 你懂乱码吗?锟斤…

Feign负载均衡

Feign负载均衡 概念总结 工程构建Feign通过接口的方法调用Rest服务(之前是Ribbon——RestTemplate) 概念 官网解释: http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign Feign是一个声明式WebService客户端。使用Feign能让…

Vitis HLS 学习笔记--Syn Report解读(1)

目录 1. 介绍 2. 示例一 2.1 HLS 代码 2.2 Report 解读 2.2.1 General Information 2.2.2 Timing Estimate 2.2.3 Performance & Resource Estimates 2.2.4 HW interfaces 2.2.4.1 硬件接口报告 2.2.4.2 导出至 Vivado 中的 IP 2.2.4.3 Port-Level Protocols 端…

【小梦C嘎嘎——启航篇】C++四大类型转换

😎 前言🙌C四大类型转换什么是类型转换C语言中的类型转换为什么C要嫌弃C语言的类型转换?自行搞一套呢?C强制类型转换1、static_cast2、reinterpret_cast3、const_cast4、dynamic_cast为什么要支持向下转呢? RTTI 总结撒…

C++之STL-list+模拟实现

目录 一、list的介绍和基本使用的方法 1.1 list的介绍 1.2 list的基本使用方法 1.2.1 构造方法 1.2.2 迭代器 1.2.3 容量相关的接口 1.2.4 增删查改的相关接口 1.3 关于list迭代器失效的问题 二、模拟实现list 2.1 节点类 2.2 迭代器类 2.3 主类list类 2.3.1 成员变…

yolov8 dll 编译

1. 每次用yolo v8 都要用python ,对于我这种写软件的太不方便了,下面尝试编译dll 调用, 我已经有做好的模型.best.pt 参考视频方法: yolov8 TensorRT C 部署_哔哩哔哩_bilibili 【yolov8】tensorrt部署保姆级教程,c版_哔哩哔哩_bilibili 需…

C语言基础知识笔记——万字学习记录

Hi,大家好,我是半亩花海。本文主要参考浙大翁恺老师的C语言讲解以及其他博主的C语言学习笔记,进而梳理C语言的基础知识,为后续系统性学习数据结构和其他语言等知识夯实一定的基础。(其他博主学习笔记的链接包括&#x…

陕西省人力资源和社会保障厅 陕西省住房和城乡建设厅 关于开展2023年度全省建设工程专业高级工程师评审工作的通知

陕西工程系列建设工程专业工程师评审工作要求链接陕西省人力资源和社会保障厅 陕西省住房和城乡建设厅 关于开展2023年度全省建设工程专业高级工程师评审工作的通知 - 陕西省住房和城乡建设厅类别基本条件业绩成果备注助理工程师 最新公告http://www.snhrm.com/zxggao2/597358…