Linux下非阻塞IO实验二

一.  简介

前面一篇文章编写Linux驱动代码(轮询函数的实现),来处理 Linux下应用程序以非阻塞方式访问设备。文章地址:

Linux下非阻塞IO实验一-CSDN博客

本文编写另外一种驱动代码实现方式,与上面实现的区别主要是阻塞代码处理部分的不同。

二.   Linux下非阻塞IO实验二

1.  驱动代码实现

本实验在 14_block_io实验的基础上进行的更改。打开 14_block_io工程代码,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;DECLARE_WAITQUEUE(wait, current); //定义并初始化一个等待队列项//判断是否为非阻塞访问if(filp->f_flags & O_NONBLOCK) //非阻塞访问{if(atomic_read(&dev->key_release) == 0) {return -EAGAIN;}}else //阻塞访问{  /*加入等待队列,使进程进入休眠,等待被唤醒(当按键按下一次即可唤醒)*/add_wait_queue(&dev->wait_queue_head, &wait); //将等待队列项添加到等待队列头set_current_state(TASK_INTERRUPTIBLE); //当前进程设置为可被信号打断状态schedule();  //切换进程状态(进程进入休眠状态)//唤醒以后从这里开始执行if(signal_pending(current)) //判断是否是被信号打断的{ret = -EINVAL;goto signal_break;}set_current_state(TASK_RUNNING); //将进程设置为运行状态remove_wait_queue(&dev->wait_queue_head, &wait); //将队列项从等待队列头移除}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;}return ret;signal_break:set_current_state(TASK_RUNNING); //将进程设置为运行状态remove_wait_queue(&dev->wait_queue_head, &wait); //将队列项从等待队列头移除
data_error:return -EINVAL;
}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"); //作者

2.  驱动编译

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

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$

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

三.  驱动模块加载测试

这里可以测试两种访问功能,阻塞访问与非阻塞访问设备。

1.  阻塞访问测试

阻塞访问测试时,这里所使用的应用程序为 前面阻塞实验使用的应用程序。应用程序如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>/*
*测试按键是否按下
* 运行命令: ./irqpp /dev/key_irq
*/int main(int argc, char* argv[])
{int ret = 0;int fd = 0;char * device_name = NULL;int key_value = 0;if(argc != 2){printf("main's param number error!\n");return -1;}device_name = argv[1];fd = open(device_name, O_RDWR);if(fd < 0){printf("open led device failed!\n");return -1;}while(1){ret = read(fd, &key_value, sizeof(key_value));if(ret >= 0){if(key_value){printf("Key0 Press!\n");}}}return 0;
}

测试方法可以参考如下:

Linux下阻塞IO驱动实验三的测试-CSDN博客

2. 非阻塞访问测试

非阻塞访问测试时,这里所使用的应用程序为 前面非阻塞实验使用的应用程序。

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>/*
*测试按键是否按下
* 运行命令: ./irqpp /dev/key_irq
*/int main(int argc, char* argv[])
{int ret = 0;int fd = 0;char * device_name = NULL;int key_value = 0;fd_set readfds; /*读操作文件描述符集 */struct timeval timeout;if(argc != 2){printf("main's param number error!\n");return -1;}device_name = argv[1];fd = open(device_name, O_RDWR | O_NONBLOCK); //非阻塞式访问if(fd < 0){printf("open led device failed!\n");return -1;}while(1){FD_ZERO(&readfds);    //清 readfds读操作文件描述符集FD_SET(fd, &readfds); //将 fd添加到 readfds中timeout.tv_sec = 1; //设置超时为 1stimeout.tv_usec = 0;ret = select((fd+1), &readfds, NULL, NULL, &timeout);switch (ret){case 0:  //超时printf("select time_out!\n");break;case -1:  //出错printf("select error!\n");break;default: //有可变的操作符if(FD_ISSET(fd, &readfds)) //判断是否是 fd可读{ret = read(fd, &key_value, sizeof(key_value));if(ret >= 0){if(key_value){printf("Key0 Press!\n");}}}break;}}return 0;
}

测试方法可以参考如下:

Linux下非阻塞IO实验一:测试-CSDN博客

总结:

这里驱动实现上,阻塞部分的处理上,与前面一篇 "非阻塞IO实验一" 的代码相比,这里使用了等待队列项,所以,比较繁琐。不过也是一种实现方式。

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

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

相关文章

Selenium WebDriver类的常用属性和方法汇总

WebDriver类是 Selenium WebDriver 提供的用于控制浏览器的核心类之一&#xff0c;它提供了许多属性和方法来管理浏览器会话、导航到不同的网页、定位和操作页面元素等。下面分别归纳其属性和方法&#xff1a; **属性&#xff1a;** 1. capabilities: 返回当前会话的浏览器的…

rsync+inotify-tools文件传输

目录 rsync rsync概述 rsync优缺点 rsync参数 rsync命令 rsync同步源 linux 安装rsync 安装rsync服务端 安装rsync客户端 windows 安装rsync rsync实践 inotify-tools inotify-tools概述 innotify-tools 命令、参数、事件 rsync客户端安装inotify-tools innotif…

鸿蒙-自定义组件的生命周期

目录 自定义组件的生命周期 1.aboutToAppear 2.aboutToDisappear 3.onPageShow 4.onPageHide 5.onBackPress 日志输出 1.显示页面 2.页面点击返回按钮 3.页面跳转 4.页面返回 自定义组件的生命周期 先来一段列子 import router from ohos.router Entry Component…

虚拟机VMware上 centos7 的网络配置

第一步&#xff1a;权限的切换 由普通用户切换到超级用户 用户名为&#xff1a;root 密码为&#xff1a;自己安装 linux 时第一次设置的密码 su -root超级用户的命令提示符是“#”&#xff0c;普通用户的命令提示符是“$”。当看到你的命令提示符为“$”时&#xff0c;证明切…

华为配置敏捷分布式SFN漫游实验

配置敏捷分布式SFN漫游示例 组网图形 图1 配置敏捷分布式SFN漫游示例组网图 组网需求数据规划配置思路配置注意事项操作步骤配置文件 组网需求 某医院通过部署敏捷分布式网络给医护人员提供WLAN接入服务&#xff0c;以满足医护人员办公的最基本需求。管理员希望终端在覆盖区域内…

pytorch之诗词生成6--eval

先上代码&#xff1a; import tensorflow as tf from dataset import tokenizer import settings import utils# 加载训练好的模型 model tf.keras.models.load_model(r"E:\best_model.h5") # 随机生成一首诗 print(utils.generate_random_poetry(tokenizer, model)…

k8s admin 用户生成token

k8s 版本 1.28 创建一个admin的命名空间 admin-namespce.yaml kind: Namespace apiVersion: v1 metadata: name: admin labels: name: admin 部署进k8s kubectl apply -f admin-namespce.yaml 查看k8s namespace 的列表 kubectl get namespace查看当前生效的…

WRF模型教程(ububtu系统)-WPS(WRF Pre-Processing System)概述

一、WPS简介 WRF 预处理系统 (WRF Pre-Processing System&#xff0c;WPS) &#xff0c;集成了基于Fortran和C编写的程序&#xff0c;这些程序主要用于处理输入到real.exe的数据。WPS主要有三个程序和一些辅助程序。 二、各程序介绍 主要的程序为geogrid.exe、ungrib.exe、met…

paddle ocr识别文字

paddle使用 # pip install paddlepaddle2.5.2 -i https://mirror.baidu.com/pypi/simple # pip install paddleocr2.7.0.3 -i https://mirror.baidu.com/pypi/simplefrom paddleocr import PaddleOCR from PIL import Image import numpy as npimage Image.open(./2.png) ocr…

Spring Boot Actuator介绍

大家在yaml中经常见到的这个配置 management: endpoints: web: exposure: #该配置线上需要去掉&#xff0c;会有未授权访问漏洞 include: "*" 他就是Actuator&#xff01; 一、什么是 Actuator Spring Boot Actuator 模块提供了生产级别…

el-table按钮获取当前行元素

el-table按钮获取当前行元素 vue2 <el-table-column label"操作" width"240px"><template slot-scope"scope"><el-button size"mini" click"toItem(scope.row)">用户详情</el-button><el-butto…

【Unity】详细介绍

Unity讲解 Unity是一个广泛使用的游戏开发平台&#xff0c;由Unity Technologies开发。它提供开发者一个强大的集成环境来创建2D和3D游戏及交互式内容。Unity自身包含一个图形引擎、物理引擎、声音处理系统、动作捕捉系统、网络系统等多个模块&#xff0c;使得开发者能够实现丰…

C++高级面试题:什么是 C++ 中的多态指针(Polymorphic Pointers)?

什么是 C 中的多态指针&#xff08;Polymorphic Pointers&#xff09;&#xff1f; 在 C 中&#xff0c;多态指针&#xff08;Polymorphic Pointers&#xff09;通常指向基类&#xff08;Base Class&#xff09;的指针&#xff0c;但它可以指向派生类&#xff08;Derived Clas…

Linux------JAVA项目发布

启动jar TODO: 直接指定jar包外的yml 用户导出配置文件 jar xf wisdom-classroom.jar BOOT-INF/classes/application.yml jar xf wisdom-classroom.jar BOOT-INF/classes/application-druid.yml用于导入配置文件 jar uf wisdom-classroom.jar BOOT-INF/classes/application.y…

微信小程序--分享如何与ibeacon蓝牙信标建立联系

ibeacon蓝牙设备 iBeacon是苹果公司2013年9月发布的移动设备用OS&#xff08;iOS7&#xff09;上配备的新功能。其工作方式是&#xff0c;配备有 低功耗蓝牙&#xff08;BLE&#xff09;通信功能的设备使用BLE技术向周围发送自己特有的ID&#xff0c;接收到该ID的应用软件会根…

Laravel Class ‘Facade\Ignition\IgnitionServiceProvider‘ not found 解决

Laravel Class Facade\Ignition\IgnitionServiceProvider not found 问题解决 问题 在使用laravel 更新本地依赖环境时&#xff0c;出现报错&#xff0c;如下&#xff1a; 解决 这时候需要更新本地的composer&#xff0c;然后在更新本地依赖环境。 命令如下&#xff1a; co…

19113133262(微信同号)【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024)

【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024) 大会主题: (主题包括但不限于, 更多主题请咨询会务组苏老师) 区块链&#xff1a; 区块链技术和系统 分布式一致性算法和协议 块链性能 信息储存系统 区块链可扩展性 区块…

How to manage Python environment based on virtualenv in Ubuntu 22.04

How to manage Python environment based on virtualenv in Ubuntu 安装使用创建环境激活环境安装软件包退出环境移除环境 安装 pip3 install virtualenv使用 创建环境 lwkqwfys:~$ mkdir ~/project/harbin lwkqwfys:~$ cd ~/project/harbin lwkqwfys:~/project/harbin$ vir…

css背景从上到下颜色渐变、css背景从左到右颜色渐变、 css框线展示外阴影、css框线展示内阴影

1. css背景从上到下颜色渐变 body {background: linear-gradient(to bottom, #ff0000, #ffff00); /* 这里的#ff0000表示红色&#xff0c;#ffff00表示黄色 */ }2. css背景从左到右颜色渐变 要实现CSS背景从左到右的颜色渐变&#xff0c;可以使用linear-gradient函数。以下是一…

微信小程序将高德地图转为腾讯地图的自行车路线规划

微信小程序后台首页开发设置 相关文档 腾讯后台 微信小程序接入JDK JDK腾讯地图文档 腾讯路线规划文档 核心代码 <map id"myMap" ref"myMap" style"width: 100%; height: calc(100vh - 80px)":latitude"latitude" :scale&qu…