Linux下非阻塞IO实验一

一.  简介

前面文章学习了 Linux内核提供的针对应用程序阻塞与非阻塞访问设备的处理方法。文章地址如下:

Linux内核中处理非阻塞访问的方法:轮询-CSDN博客

Linux内核中轮询对应于应用层的函数之一:poll函数-CSDN博客

Linux内核中轮询对应于应用层的函数之一:epoll函数-CSDN博客

如果用户应用程序以非阻塞的方式访问设备,设备驱动程序就要提供非阻塞的处理方式,也就是轮询。Linux内核提供了轮询的方法,来处理应用层以非阻塞方式访问设备。

本文通过编写Linux驱动代码(轮询函数的实现),来处理 Linux下应用程序以非阻塞方式访问设备。

二.  Linux下非阻塞IO实验一

应用层的 select()函数,poll()函数,epoll()函数,这三个函数对应了 Linux 驱动 file_operations 结构体中的成员函数 .poll的函数实现。所以,本实验就是实现 file_operations结构体函数集中 .poll函数的实现。

本实验是在 按键中断实验工程 14_block _io 的基础上进行更改。接下来进行轮询函数的实现。

1.  创建工程

(1)首先,创建 14_non_block_io 工程目录:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers$ mkdir 14_non_block_io

(2)其次,进入14_non_block_io目录下,将 14_block _io工程目录下所有文件拷贝到 14_non_block_io 目录下:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/14_non_block_io$ cp ../14_block_io/* ./ -rf

将 .vscode及其下文件也拷贝到 14_block_io目录下:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/14_non_block_io$ cp ../14_block_io/.vscode/ ./ -rf

注意: .vscode目录下的文件,设置了驱动可能会调用到的 Linux内核源码(这里是NXP官方提供的Linux内核源码)的路径。

2.  驱动代码实现

因为应用程序是以阻塞的方式访问设备的。所以,这里主要实现也就是 file_operations结构体函数集中 .poll函数

key_irq.c文件添加 非阻塞处理后如下所示:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <asm/atomic.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <linux/poll.h>#define   IRQ_NAME          "key_irq"
#define   IRQ_CNT           1
#define   KEY_NUM           1 
#define   KEY0_VALUE        0x01  //key0按键值
#define   INVAL_KEY_VALUE   0xFF  //无效的按键值static int irq_dev_open(struct inode *inode, struct file *filp);
ssize_t irq_dev_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos);
static unsigned int irq_dev_poll(struct file *file, poll_table * wait);
int irq_dev_close(struct inode * inode, struct file * filp);static const struct file_operations irq_fops = {.open = irq_dev_open,.owner = THIS_MODULE,.read = irq_dev_read,.poll = irq_dev_poll,.release = irq_dev_close, 
};/*key按键结构体 */
struct key_dev{int gpio_number;        //IO编号int interrupt_number;   //中断号unsigned char value;    //键值unsigned char name[50]; //按键名字irqreturn_t (*handler)(int, void*); //中断处理函数
};/*imx6ull_irq设备结构体 */
struct irq_dev{dev_t devid;  //主设备号+次设备号int major; //主设备号int minor; //次设备号struct cdev cdev;struct class* class;struct device* device;struct device_node * dev_node;//设备节点struct key_dev key[KEY_NUM];struct timer_list timer; //定时器atomic_t key_value;      //按键值atomic_t key_release;    //按键释放标志wait_queue_head_t wait_queue_head; //等待队列头
};struct irq_dev irq;/* 打开设备函数*/
static int irq_dev_open(struct inode *inode, struct file *filp)
{filp->private_data = &irq;return 0;
}/*读取数据函数(这里读取按键值) */
ssize_t irq_dev_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos)
{int ret = 0;struct irq_dev* dev = filp->private_data;unsigned char key_release = 0;unsigned char key_value = 0;//判断是否为非阻塞访问if(filp->f_flags & O_NONBLOCK) //非阻塞访问{if(atomic_read(&dev->key_release) == 0) {return -EAGAIN;}}else //阻塞访问{    //等待(按键按下一次)事件wait_event_interruptible(dev->wait_queue_head, atomic_read(&dev->key_release));}key_release = atomic_read(&dev->key_release);key_value = atomic_read(&dev->key_value);if(key_release) //判断按键是否释放{if(key_value&0x80){key_value &= ~(0x80); //去掉标志位ret = copy_to_user(buf, &key_value, sizeof(key_value));if(ret != 0){printk("copy_to_user failed!\n");goto data_error;}}key_release = 0;atomic_set(&dev->key_release, 0);}else{goto data_error;}data_error:return ret;
}static unsigned int irq_dev_poll(struct file *file, poll_table * wait)
{int mask = 0;struct irq_dev* dev = file->private_data;poll_wait(file, &dev->wait_queue_head, wait);//判断是否可读if(atomic_read(&dev->key_release)){mask = POLLIN | POLLRDNORM;}return mask;
}/* 关闭设备函数 */
int irq_dev_close(struct inode * inode, struct file * filp)
{return 0;
}/*定时器处理函数:对按键进行消抖处理 */
void timer_handler(unsigned long data)
{int value = 0;struct irq_dev* dev = (struct irq_dev*)data;//读取按键值value = gpio_get_value(dev->key[0].gpio_number);if(value == 0) //按键按下{// printk("KEY0 Press!\n");atomic_set(&dev->key_value, dev->key[0].value);}else if(value == 1) //按键释放{// printk("KEY0 Release!\n");//按键值最高位置1,打上标志atomic_set(&dev->key_value, (0x80|dev->key[0].value));atomic_set(&dev->key_release, 1); //一次完整的按键标志}/*按键有效时,唤醒进入休眠状态的进程 */ if(atomic_read(&dev->key_release)) //完成一次按键过程{wake_up(&dev->wait_queue_head);} 
}/* 中断处理函数 */
static irqreturn_t key0_irq_handler(int irq, void *param)
{struct irq_dev* dev = param;dev->timer.data = (volatile unsigned long)param;//设置定时器超时时间,开启定时器mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20)); //按键按下后延时20msreturn IRQ_HANDLED;
}/*按键初始化 */
static int key_io_init(struct irq_dev* dev)
{int ret = 0;int i = 0;int n = 0;/*1.获取设备节点 */dev->dev_node = of_find_node_by_path("/key");if(NULL == dev->dev_node){printk("find dev_node failed!\n");goto find_dev_node;}/*2.获取IO编号 */for(i=0; i< KEY_NUM; i++){   dev->key[i].gpio_number = of_get_named_gpio(dev->dev_node, "key-gpio", i);if(dev->key[i].gpio_number < 0){printk("get gpio number failed!\n");goto get_gpio_number;}}/*3.申请IO */for(i = 0; i< KEY_NUM; i++){memset(dev->key[i].name, 0, sizeof(dev->key[i].name));sprintf(dev->key[i].name, "KEY%d", i);ret = gpio_request(dev->key[i].gpio_number, dev->key[i].name);if(ret != 0){printk("gpio request fail!\n");goto gpio_request;}/* 将GPIO设置为输入*/gpio_direction_input(dev->key[i].gpio_number);/* 获取中断号*/dev->key[i].interrupt_number = gpio_to_irq(dev->key[i].gpio_number);if(!dev->key[i].interrupt_number){printk("irq_of_parse_and_map fail!\n");goto get_irq_number;}}dev->key[0].handler = key0_irq_handler;dev->key[0].value = KEY0_VALUE;/*申请中断 */for(i=0; i<KEY_NUM; i++){ret = request_irq(dev->key[i].interrupt_number, dev->key[i].handler, (IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING), dev->key[i].name, dev);  printk("request_irq ret: %d\n", ret);if(ret != 0){printk("%d request interrupt fail!\n", i);goto irq_request;}                     }/*定时器初始化 */init_timer(&dev->timer);dev->timer.function = timer_handler;return 0;irq_request:
get_irq_number:for(i=0; i<KEY_NUM; i++){gpio_free(dev->key[i].gpio_number);}
gpio_request:for(n = 0; n<i; n++){gpio_free(dev->key[n].gpio_number);}
get_gpio_number:
find_dev_node:return ret;
}/*驱动入口函数 */
static int __init imx6ull_irq_init(void)
{int ret = 0;/*1. 注册/申请设备号 */irq.major = 0;  if(irq.major) //如果给出主设备号,则注册设备号{irq.devid = MKDEV(irq.major, 0);ret = register_chrdev_region(irq.devid, IRQ_CNT, IRQ_NAME);	}else //否则申请设备号{ret = alloc_chrdev_region(&irq.devid, 0, IRQ_CNT, IRQ_NAME);irq.major = MAJOR(irq.devid);irq.minor = MINOR(irq.devid);}if(ret < 0){printk("devid apply failed!\n");goto devid_failed;}printk("dev: major: %d minor: %d\n", irq.major,irq.minor);/*2. 设备初始化,添加设备*/irq.cdev.owner = THIS_MODULE;cdev_init(&irq.cdev, &irq_fops);ret = cdev_add(&irq.cdev, irq.devid, IRQ_CNT);if(ret < 0){printk("cdev_add failed!\n");goto cdev_init_failed;}/*3. 自动创建设备节点 */irq.class = class_create(THIS_MODULE, IRQ_NAME);if(IS_ERR(irq.class)){printk(KERN_ERR "class_create failed!\n");ret = PTR_ERR(irq.class);goto class_create_failed;}irq.device = device_create(irq.class, NULL, irq.devid, NULL, IRQ_NAME);if (IS_ERR(irq.device)) {printk(KERN_ERR "device_create failed!\n");ret = PTR_ERR(irq.device);goto device_create_failed;}/*4. 按键初始化*/ret = key_io_init(&irq);if(ret != 0){printk("key_io_init failed!\n");goto key_io_init_failed;}/*初始原子变量*/atomic_set(&irq.key_value, INVAL_KEY_VALUE);atomic_set(&irq.key_release, 0); /*初始化等待队列头 */init_waitqueue_head(&irq.wait_queue_head);return 0;key_io_init_failed:device_destroy(irq.class, irq.devid);
device_create_failed:class_destroy(irq.class);
class_create_failed:cdev_del(&irq.cdev);
cdev_init_failed: unregister_chrdev_region(irq.devid, IRQ_CNT);
devid_failed:return ret;
}/*驱动出口函数 */
static void __exit imx6ull_irq_exit(void)
{int i = 0;/*删除定时器 */del_timer(&irq.timer);/*释放中断 */for(i=0; i < KEY_NUM; i++){free_irq(irq.key[i].interrupt_number, &irq);}/*释放GPIO编号 */for(i=0; i < KEY_NUM; i++){gpio_free(irq.key[i].gpio_number);}/*2. 删除设备 */cdev_del(&irq.cdev);/*3. 注销设备号*/unregister_chrdev_region(irq.devid, IRQ_CNT);/*4. 销毁设备 & 类*/device_destroy(irq.class, irq.devid);class_destroy(irq.class);
}/*驱动入口与出口函数 */
module_init(imx6ull_irq_init);
module_exit(imx6ull_irq_exit);
MODULE_LICENSE("GPL"); //驱动 License
MODULE_AUTHOR("WeiYing"); //作者

三. 编译驱动

对驱动代码进行模块编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/14_non_block_io$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/14_non_block_io modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”CC [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/14_non_block_io/key_irq.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/wangtian/zhengdian_Linux/Linux_Drivers/14_non_block_io/key_irq.mod.oLD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/14_non_block_io/key_irq.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/14_non_block_io$

可以看出,驱动模块可以正常编译。

接下来对驱动模块进行测试,确定是否实现了非阻塞处理。

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

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

相关文章

Python常用新特性记录

Python常用新特性记录 Python3.8PEP 572 &#xff1a;赋值表达式f-字符串支持 用于自动记录表达式和调试文档 Python3.9PEP 584&#xff1a;字典合并与更新运算符PEP 616&#xff1a;新增用于移除前缀和后缀的字符串方法PEP 585&#xff1a;标准多项集中的类型标注泛型 Python…

python面向对象的三大特性:封装,继承,多态

1、面向对象有哪些特性 三种&#xff1a;封装性、继承性、多态性 2、Python中的封装 在Python代码中&#xff0c;封装有两层含义&#xff1a; ① 把现实世界中的主体中的属性和方法书写到类的里面的操作即为封装 ② 封装可以为属性和方法添加为私有权限&#xff0c;不能直…

Midjourney新功能:角色参照指南

基本概念 角色参照&#xff08;Character Reference&#xff09;&#xff1a;这个功能允许用户在不同的图像生成中保持给定参照角色的一致性。适用模型&#xff1a;适用于Midjourney V6和Niji6型号。 功能亮点 跨风格一致性&#xff1a;可以在不同风格&#xff08;如动漫风、…

数据泄露态势(2024年2月)

监控说明&#xff1a;以下数据由零零信安0.zone安全开源情报系统提供&#xff0c;该系统监控范围包括约10万个明网、深网、暗网、匿名社交社群威胁源。在进行抽样事件分析时&#xff0c;涉及到我国的数据不会选取任何政府、安全与公共事务的事件进行分析。如遇到影响较大的伪造…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的安全帽检测系统(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;开发先进的安全帽识别系统对提升工作场所的安全性至关重要。本文详细介绍了使用深度学习技术创建此类系统的方法&#xff0c;并分享了完整的实现代码。系统采用了强大的YOLOv8算法&#xff0c;并对其与YOLOv7、YOLOv6、YOLOv5的性能进行了详细比较&#xff0c;…

Windows主机多网卡访问内外网

1&#xff1a;在实际生产环境有可能需要某台机器既能访问公司的内部网络也要能够访问外网。 2&#xff1a;首先机器要有两块网卡根据实际情况分别设置内外网的IP地址&#xff0c;掩码&#xff0c;网关&#xff0c;DNS等信息。设置完成时会出现下面的提示。 3&#xff1a;打开命…

空间计算综合指南

空间计算&#xff08;spatial computing&#xff09;是指使人类能够在三维空间中与计算机交互的一组技术。 该保护伞下的技术包括增强现实&#xff08;AR&#xff09;和虚拟现实&#xff08;VR&#xff09;。 这本综合指南将介绍有关空间计算所需了解的一切。 你将了解 AR、VR…

.NET后端返回File文件,及前端处理直接在浏览器下载

后端代码 [AllowAnonymous] public System.Web.Mvc.ActionResult ExportByteExcel(string datatab, string columnnames, string schemecode) { 返回excel。 string ReportName "ExcelTemplete" DateTime.Now.Ticks.ToString(); …

漏洞复现-红帆OA系列

漏洞复现-红帆OA GetWorkUnit.asmx存在SQL注入iOffice ioDesktopData存在SQL注入list接口存在SQL注入漏洞ioffice wssrtfile sql注入任意⽤户登录(2个)后台多处⽂件上传(7个)后台密码修改(1个)⽂件读取(2个)SQL注⼊(15个)红帆OA任意文件上传漏洞红帆HF Office系统SQL…

QComboBox相关的qss学习

QT有关QCobobox控件的样式设置&#xff08;圆角、下拉框&#xff0c;向上展开、可编辑、内部布局等&#xff09;_qcombobox样式-CSDN博客 原始图&#xff1a; 红色边框&#xff1a; QComboBox{ border:2px solid rgb(255, 85, 0); } 绿色背景&#xff1a; QComboBox{ border…

备战蓝桥杯Day27 - 省赛真题-2023

题目描述 大佬代码 import os import sysdef find(n):k 0for num in range(12345678,98765433):str1 ["2","0","2","3"]for x in str(num) :if x in str1:if str1[0] x:str1.pop(0)if len(str1) ! 0:k1print(k)print(85959030) 详…

火山翻译相关介绍

官网&#xff1a;火山翻译 - 在线翻译 (volcengine.com) 火山翻译当前支持文本翻译、语音翻译、图像翻译、视频翻译和直播翻译 火山引擎机器翻译文本翻译的API接口的QPS限制为10&#xff0c;图片翻译的API接口的限制为10 语言支持 语言支持--机器翻译-火山引擎 (volcengine…

C语言指针与数组(不适合初学者版):一篇文章带你深入了解指针与数组!

&#x1f388;个人主页&#xff1a;JAMES别扣了 &#x1f495;在校大学生一枚。对IT有着极其浓厚的兴趣 ✨系列专栏目前为C语言初阶、后续会更新c语言的学习方法以及c题目分享. &#x1f60d;希望我的文章对大家有着不一样的帮助&#xff0c;欢迎大家关注我&#xff0c;我也会回…

大模型笔记:吴恩达 ChatGPT Prompt Engineering for Developers(1) prompt的基本原则和策略

1 intro 基础大模型 VS 用指令tune 过的大模型 基础大模型 只会对prompt的文本进行续写 所以当你向模型发问的时候&#xff0c;它往往会像复读机一样续写几个问题这是因为在它见过的语料库文本&#xff08;通常大多来自互联网&#xff09;中&#xff0c;通常会连续列举出N个问…

linux_aarch64_qt环境搭建

平台环境&#xff1a; ubuntu 16.04&#xff1a; gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12) aarch64 gnu gcc版本&#xff1a; gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu.tar.xz Qt交叉编译版本: qt-everywhere-src-5.12.9.tar.xz 一、aarch64编…

BMW配送流程:通过EDI对接VDLP

BMW的汽车配送流程始于汽车“生产结束”&#xff0c;结束于“交付给宝马经销商”。BMW与其物流服务供应商之间没有直接的接口&#xff0c;EDI信息将会通过BMW的EDI供应商提供的VDLP&#xff08;车辆分销物流平台&#xff09;进行交换。 近期我们收到来自国内某汽车行业供应商L公…

ISIS多区域实验简述

为支持大型路由网络&#xff0c;IS-IS在路由域内采用两级分层结构。 IS-IS网络中三种级别的路由设备&#xff1a;将Level-1路由设备部署在区域内&#xff0c;Level-2路由设备部署在区域间&#xff0c;Level-1-2路由设备部署在Level-1和Level-2路由设备的中间。 实验拓扑图&…

Spring中的注释

Resource注释 Resource private AService aService;This annotation may be applied to an application component class, or to fields or methods of the component class. When the annotation is applied to a field or method, the container will inject an instance of…

Linux字符设备与I2C驱动结合使用

引言 在Linux操作系统中&#xff0c;设备驱动程序充当硬件和软件之间的桥梁。字符设备驱动是一种特殊类型的驱动&#xff0c;它允许用户以字节流的形式访问硬件设备。这些设备包括键盘、鼠标、串口等。在本博客中&#xff0c;我们将探讨Linux字符设备驱动的基础知识&#xff0…

学生时期学习资源同步-JavaSE理论知识

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载 选择题 &#xff08;针对以下题目&#xff0c;请选择最符合题目要求的答案&#xff0c;针对每一道题目&#xff0c;所有答案都选对&#x…