linux驱动之等待队列

        阻塞和非阻塞 IO Linux 驱动开发里面很常见的两种设备访问模式,在编写驱动的时候一定要考虑到阻塞和非阻塞。

一.阻塞和非阻塞 IO

   (1)阻塞访问

        阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时,并不挂起,它要么放弃,要么不停地查询,直至可以进行操作为止。
        
        在阻塞访问时,不能获取资源的进程将进入休眠,它将 CPU 资源“礼让”给其他进程。因为阻塞的进程会进入休眠状态,所以必须确保有一个地方能够唤醒休眠的进程,否则,进程就真的“寿终正寝”了。唤醒进程的地方最大可能发生在中断里面,因为在硬件资源获得的同时往往伴随着一个中断。而非阻塞的进程则不断尝试,直到可以进行 I/O

1.图解表示

2.代码表示 

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

(2)非阻塞

        若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的 xxx_read() 、 xxx_write () 等操作应立即返回,read() 、write() 等系统调用也随即被返回,应用程序收到-EAGAIN 返回值。

1.图解表示

 2.代码表示

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

二.等待队列五大组合

(1)等待队列头

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

(2)等待队列项

        等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可
用的时候就要将这些进程对应的等待队列项添加到等待队列里面。
DECLARE_WAITQUEUE(name, tsk)
name 就是等待队列项的名字,tsk 表示这个等待队列项属于哪个任务 ( 进程 ) ,一般设置为 current ,在Linux 内核中 current 相 当 于 一 个 全 局 变 量 ,表 示 当 前 进 程 。因 此 DECLARE_WAITQUEUE 就是给当前正在运行的进程创建并初始化了一个等待队列项。

(3)将队列项添加/移除等待队列头

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

 

(4)等待唤醒

        当设备可以使用的时候就要唤醒进入休眠态的进程,唤醒可以使用如下两个函数:
        void wake_up(wait_queue_head_t *q) //功能:唤醒所有休眠进程
        void wake_up_interruptible(wait_queue_head_t *q)//功能:唤醒可中断的休眠进程

(5)等待事件

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

三.代码案例

        1.驱动代码 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/io.h>
//定义结构体表示我们的节点
struct device_node *test_device_node;
struct property *test_node_property;
//要申请的中断号
int irq;
int gpio_nu;
//用来模拟管脚的状态
int value = 0;
/**
* @description: 中断处理函数 test_key
* @param {int} irq :要申请的中断号
* @param {void} *args :
* @return {*}IRQ_HANDLED
*/
irqreturn_t test_key(int irq, void *args)
{
    value = !value;
    return IRQ_RETVAL(IRQ_HANDLED);
}
int misc_open(struct inode *node, struct file *file)
{
    printk("hello misc_open \n");
    return 0;
}
int misc_release(struct inode *node, struct file *file)
{
    printk("hello misc_release bye bye\n");
    return 0;
}
ssize_t misc_read(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
if (copy_to_user(ubuf, &value, sizeof(value)) != 0)
{
    printk("copy_to_user error\n");
    return -1;
    }
    return 0;
}
//文件操作集
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.read = misc_read};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "test_wq",
.fops = &misc_fops,
};
/**
* @brief beep_probe : 与设备信息层(设备树)匹配成功后自动执行此函数,
* @param inode : 文件索引
* @param file : 文件
* @return 成功返回 0
*/
int beep_probe(struct platform_device *pdev)
{
    int ret = 0;
    printk("beep_probe\n");
    //of_find_node_by_path 函数通过路径查找节点,/test_key 是设备树下的节点路径
    test_device_node = of_find_node_by_path("/test_key");
    if (test_device_node == NULL)
    {
        printk("of_find_node_by_path is error\n");
        return -1;
    }
    //of_get_named_gpio 函数获取 GPIO 编号
    gpio_nu = of_get_named_gpio(test_device_node, "gpios", 0);
    if (gpio_nu < 0)
    {
        printk("of_get_named_gpio is error\n");
        return -1;
    }
    //设置 GPIO 为输入模式
    gpio_direction_input(gpio_nu);
    //获取 GPIO 对应的中断号
    irq = irq_of_parse_and_map(test_device_node, 0);
    printk("irq is %d \n", irq);
    /*申请中断,irq:中断号名字
    test_key:中断处理函数
    IRQF_TRIGGER_RISING:中断标志,意为上升沿触发
    "test_key":中断的名字
    */
    ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);
    if (ret < 0)
    {
        printk("request_irq \n");
        return -1;
    }
    //注册杂项设备
    ret = misc_register(&misc_dev);
    if (ret < 0)
    {
        printk("misc_register is error\n");
        return -1;
    }
    printk("misc_register is successd \n");
    }
    int beep_remove(struct platform_device *pdev)
    {
        printk("beep_remove \n");
        return 0;
    }
const struct platform_device_id beep_idtable = {
    .name = "beep_test",
    };
const struct of_device_id of_match_table_test[] = {
    {.compatible = "keys"},
    {},
};
struct platform_driver beep_driver = {
    //3. 在 beep_driver 结构体中完成了 beep_probe 和 beep_remove
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
    .owner = THIS_MODULE,
    .name = "beep_test",
    .of_match_table = of_match_table_test
    },
    //4 .id_table 的优先级要比 driver.name 的优先级要高,优先与.id_table 进行匹配
    .id_table = &beep_idtable
};
static int beep_driver_init(void)
{
    //1.我们看驱动文件要从 init 函数开始看
    int ret = 0;
    //2. 在 init 函数里面注册了 platform_driver
    ret = platform_driver_register(&beep_driver);
    if (ret < 0)
    {
        printk("platform_driver_register error \n");
        return ret;
    }
    printk("platform_driver_register ok \n");
    return 0;
}
static void beep_driver_exit(void)
{
    printk("gooodbye! \n");
    free_irq(irq, NULL);
    misc_deregister(&misc_dev);
    platform_driver_unregister(&beep_driver);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");

        2.应用代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
    int fd;
    int value;
    //打开设备节点
    fd = open("/dev/test_wq",O_RDWR);
    if(fd < 0)
    {
        //打开设备节点失败
        perror("open error \n");
        return fd;
    }
    while(1)
    {
        read(fd,&value,sizeof(value));
        printf("value is %d \n",value);
    }
    close(fd);
    return 0;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

048-第三代软件开发-数据回放

第三代软件开发-数据回放 文章目录 第三代软件开发-数据回放项目介绍数据回放 关键字&#xff1a; Qt、 Qml、 Data、 play back、 数据 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#xff09;和 C 的…

2023.11.9 IDEA 配置 Lombok

目录 什么是 Lombok 如何使用 Lombok Lombok 的 Data 注解 什么是 Lombok Lombok 是一个 Java 库&#xff0c;能自动插入编译器并构建工具&#xff0c;简化 Java 开发它通过注解实现这一目的&#xff0c;可用来帮助开发人员消除 Java 的冗长代码&#xff0c;尤其是对于简单…

华为取消6000万订单影响在扩大,高通嘴硬强调不受影响

高通公布了2023年第三季度的业绩&#xff0c;业绩显示营收下滑24%&#xff0c;净利润下滑36%&#xff0c;不过高通强调预计今年四季度业绩将回升&#xff0c;意思是说华为取消订单带来的影响较小。 一、高通处境不利已延续4年时间 2019年美国对华为采取措施&#xff0c;众多中国…

go-sync-mutex

Sync ​ Go 语言作为一个原生支持用户态进程&#xff08;Goroutine&#xff09;的语言&#xff0c;当提到并发编程、多线程编程时&#xff0c;往往都离不开锁这一概念。锁是一种并发编程中的同步原语&#xff08;Synchronization Primitives&#xff09;&#xff0c;它能保证多…

Django之三板斧的使用,全局配置文件介绍,request对象方法,pycharm链接数据库,Django链接数据库,ORM的增删改查

【1】三板斧(3个方法)的使用 Httpresponse() 括号内写什么字符串&#xff0c;返回的就是什么字符串返回的是字符串 render(request&#xff0c; 静态文件 ) request是固定的静态文件是写在templates文件夹里面的&#xff0c;如&#xff0c;HTML文件 redirect( 重定向的地址 ) 重…

利用三次样条插值调整鱼眼扭曲程度

本文利用三次样条插值算法&#xff0c;改变鱼眼扭曲程度。效果如下图所示&#xff1a; 源码下载地址&#xff1a;利用三次样条插值算法更改鱼眼特效的扭曲程度资源-CSDN文库 &#xff08;说明&#xff1a;源码基于QT和opencv &#xff09; 主要代码 鱼眼扭曲 void fisheye(…

在Windows 10上安装单机版的hadoop-3.3.5

1、Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以不需要了解分布式底层细节的情况下&#xff0c;开发分布式程序。充分利用集群进行高速运算和存储。 2、下载Hadoop&#xff0c;我们在清华大学的镜像站下载 Index of /apache/hadoop/core/hadoop-3.3.6 (t…

Django文件配置、request对象、连接MySQL、ORM

文章目录 Django静态文件及相关配置静态文件前言静态文件相关配置 form表单request对象request请求结果GET请求POST请求 pycharm连接数据库Django连接MySQLDjango ORM简介 Django静态文件及相关配置 在此篇博客我将以一个用户登录页面来引入相关知识 首先我们先编写一个html页面…

一种libuv实现websockets服务的解决方案

方法是libuv用多事件循环来驱动。说起来容易&#xff0c;做起来还是比下面的方法更容易&#xff1a; 上图是某位网友的方法代表子大部分网络资料。此方法对部署不友好&#xff0c;因为软件仓库提供的libwebsockets是不能用了。如何简化部署&#xff0c;利用好现有的软件仓库呢&…

vue开发环境搭建部署(mac版)

前言 目前后端工作越来越少了&#xff0c;年底了&#xff0c;为了先过验收。项目负责人、产品、需求制定的方案就是先做假页面&#xff0c;所以前端的活多点。 其实现在不喜欢搞前端&#xff0c;原因很多&#xff0c;但是感觉现在似乎流行的码林绝学又是九九归一的瓶颈期…

【Kurbernetes资源管理】声明式资源管理+配置清单文件详解(附实例)

声明式 一、声明式资源管理方式1.1 简介1.2 基本语法1.3 子命令详解1.3.1 获取资源配置清单1.3.2 创建/更新资源补充&#xff1a;creat和apply的区别 1.3.3 删除资源----- delete1.3.4 编辑资源配置 -----edit1.3.5 获取资源的解释-----explain 二、资源清单格式详解2.1 yaml语…

Flutter 第三方 flutter_screenutil(屏幕适配)

一直觉得自己写的不是技术&#xff0c;而是情怀&#xff0c;一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的&#xff0c;希望我的这条路能让你们少走弯路&#xff0c;希望我能帮你们抹去知识的蒙尘&#xff0c;希望我能帮你们理清知识的脉络&#xff0…

Git的基本使用

目录 一.Git的简介 1.1 Git与SVN的区别&#xff08;优势与劣势&#xff09; 1.2 Git的工作流程 二.Git的安装及常用命令 2.1 使用前准备 ​编辑 ​编辑 2.2 在Windows中安装Git 官网链接 Git - Downloadshttps://git-scm.com/downloads 2.3 Git的常用命令 三、Git命令…

人工智能-卷积神经网络之多输入多输出通道

多输入多输出通道 每个图像的多个通道和多层卷积层。例如彩色图像具有标准的RGB通道来代表红、绿和蓝。 但是到目前为止&#xff0c;我们仅展示了单个输入和单个输出通道的简化例子。 这使得我们可以将输入、卷积核和输出看作二维张量。 当我们添加通道时&#xff0c;我们的输…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(二)

新增员工功能开发 1. 新增员工1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.1.3 表设计 1.2 代码开发1.2.1 设计DTO类1.2.2 Controller层1.2.3 Service层接口1.2.4 Service层实现类1.2.5 Mapper层 1.3 功能测试1.3.1 接口文档测试 1.4 代码完善1.4.1 问题一1.4.2 问题二1.…

H5网页漫画小说苹果cms模板源码/支持对接公众号/支持三级分销

H5网页漫画小说苹果cms模板源码&#xff0c;支持对接公众号、支持三级分销&#xff0c;评论、收藏、历史记录、三级分销。 独有的模板搜索功能&#xff0c;微信、qq防红&#xff0c;站外采集接口、记录阅读章节&#xff0c;SEO优化&#xff08;后台配置&#xff09;&#xff0…

16.字符连接

#include<stdio.h> #include <cstring> int main(){char s1[44];char s2[33];scanf("%s",s1);scanf("%s",s2);strcat(s1,s2) ;printf("连接两个字符为&#xff1a;%s ",s1); return 0;}

Java数据的基本(原始)类型和引用类型的特点差别

本文作为“Java数据类型”一文的补充https://blog.csdn.net/cnds123/article/details/110517272 Java的数据类型可以分为基本类型&#xff08;primitive types&#xff09;和引用类型&#xff08;reference types&#xff09;两大类。在实际编程中&#xff0c;要根据需求选择合…

MySQL json相关函数详解

MySQL提供了一系列的JSON函数&#xff0c;用于解析、提取、修改和操作JSON数据。以下是一些常用的JSON函数及其功能。 以下所有操作都使用该表&#xff08;zone_test&#xff09;用来演示&#xff1a; 一&#xff1a;JSON_OBJECT(key1,value1,key2,value2) 1、作用&#xff1a;…

​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第1章-绪论-思维导图】 课本里章节里所有蓝色字体的思维导图