POLL机制

news2025/1/12 23:44:20

文章目录

  • 一、POLL机制
    • 1、应用场景
    • 2、执行流程
  • 二、程序
    • 1、驱动程序
    • 2、测试应用程序
  • 三、总结

一、POLL机制

1、应用场景

使用休眠-唤醒的方式等待某个事件发生时,有一个缺点:等待的时间可能很久。我们可以加上一个超时时间,这时就可以使用POLL机制。
简单理解就是: 我在等待一个外设信号,但 POLL机制休眠-唤醒的死等 多了一个功能就是我可以设置超时时间,假如超时后,我的应用程序又应该去执行什么。相比之下,在应用程序中会多了一些灵活性。

2、执行流程

在这里插入图片描述
首先,我们熟悉当应用程序执行open()函数时,内核会调用驱动里对应的drv_open()函数。poll()函数也差不多,当应用程序执行poll()函数,内核最终会调用到驱动里对应的drv_poll()函数。
但是,应用程序中的poll() 和 驱动程序的drv_poll()中间的sys_poll()还会做些事情

如上图,我们从第三步开始分析(基于读取按键值的情景):
①、…
②、…
③、应用程序poll()执行后,进入到sys_poll(),里面的程序是内核开发者完成的;
④、sys_poll()里有个for循环,此时会调用驱动层里我们写的drv_poll(),drv_poll()只做两件事:
第一、drv_poll()要把自己这个线程挂入等待队列 wq 中,但未休眠;
第二、返回状态;
⑤、此时drv_poll()返回没有数据,则sys_poll()进入到else分支开始休眠;若休眠时间超过指定时间,则回到for循环开头再次调用drv_poll(),drv_poll()依旧返回没数据,但目前已经超时,sys_poll()开始返回到应用层,应用层收到的结果是超时;
⑥、假如在休眠时,按键按下且数据被记录,按键中断程序会唤醒线程;
⑦、此时sys_poll()重新回到for循环开头再次调用drv_poll(),drv_poll()知道有按键按下了,则返回有数据;⑧、sys_poll()随后经过if判断后返回到应用层,应用层收到的结果是有数据;
最后,应用程序中收到poll()返回的有数据,才会进一步调用read()函数读取按键值。

二、程序

1、驱动程序

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>

struct gpio_key{
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
} ;

static struct gpio_key *gpio_keys_100ask;

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_key_class;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);

/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	int err;
	int key;
	
	wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
	key = get_key();
	err = copy_to_user(buf, &key, 4);
	
	return 4;
}

static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(fp, &gpio_key_wait, wait);
	return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_key_drv_read,
	.poll    = gpio_key_drv_poll,
};

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key *gpio_key = dev_id;
	int val;
	int key;
	
	val = gpiod_get_value(gpio_key->gpiod);

	printk("key %d %d\n", gpio_key->gpio, val);
	key = (gpio_key->gpio << 8) | val;
	put_key(key);
	wake_up_interruptible(&gpio_key_wait);
	
	return IRQ_HANDLED;
}

/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int gpio_key_probe(struct platform_device *pdev)
{
	int err;
	struct device_node *node = pdev->dev.of_node;
	int count;
	int i;
	enum of_gpio_flags flag;
		
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	count = of_gpio_count(node);
	if (!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}

	gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
	for (i = 0; i < count; i++)
	{
		gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag);
		if (gpio_keys_100ask[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}
		gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);
		gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW;
		gpio_keys_100ask[i].irq  = gpio_to_irq(gpio_keys_100ask[i].gpio);
	}

	for (i = 0; i < count; i++)
	{
		err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys_100ask[i]);
	}

	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_key */

	gpio_key_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
	if (IS_ERR(gpio_key_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_gpio_key");
		return PTR_ERR(gpio_key_class);
	}

	device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio_key"); /* /dev/100ask_gpio_key */
        
    return 0;
}

static int gpio_key_remove(struct platform_device *pdev)
{
	//int err;
	struct device_node *node = pdev->dev.of_node;
	int count;
	int i;

	device_destroy(gpio_key_class, MKDEV(major, 0));
	class_destroy(gpio_key_class);
	unregister_chrdev(major, "100ask_gpio_key");

	count = of_gpio_count(node);
	for (i = 0; i < count; i++)
	{
		free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
	}
	kfree(gpio_keys_100ask);
    return 0;
}

static const struct of_device_id ask100_keys[] = {
    { .compatible = "100ask,gpio_key" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver gpio_keys_driver = {
    .probe      = gpio_key_probe,
    .remove     = gpio_key_remove,
    .driver     = {
        .name   = "100ask_gpio_key",
        .of_match_table = ask100_keys,
    },
};

/* 2. 在入口函数注册platform_driver */
static int __init gpio_key_init(void)
{
    int err;
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
    err = platform_driver_register(&gpio_keys_driver); 
	
	return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit gpio_key_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&gpio_keys_driver);
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */
module_init(gpio_key_init);
module_exit(gpio_key_exit);

MODULE_LICENSE("GPL");

2、测试应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
	int fd;
	int val;
	struct pollfd fds[1];
	int timeout_ms = 5000;
	int ret;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	fds[0].fd = fd;
	fds[0].events = POLLIN;
	
	while (1)
	{
		/* 3. 读文件 */
		ret = poll(fds, 1, timeout_ms);
		if ((ret == 1) && (fds[0].revents & POLLIN))
		{
			read(fd, &val, 4);
			printf("get button : 0x%x\n", val);
		}
		else
		{
			printf("timeout\n");
		}
	}
	
	close(fd);
	
	return 0;
}

三、总结

1、以上专业术语或名词解释有个人理解,感谢指点纠错!
2、视频学习B站韦东山:【第5篇】嵌入式Linux驱动开发基础知识 - POLL机制

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

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

相关文章

在linux服务器安装anaconda3

下载anaconda3 wget https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh授权 chmod x Anaconda3-2023.09-0-Linux-x86_64.sh运行安装 ./Anaconda3-2023.09-0-Linux-x86_64.shenter yes 自定义路径&#xff0c;注意路径下的anaconda3文件夹不能已经存…

基于鸿蒙OS开发一个前端应用

创建JS工程&#xff1a;做鸿蒙应用开发到底学习些啥&#xff1f; 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。选择HarmonyOS模板库&#xff0c…

视频美颜SDK趋势畅想:未来发展方向与应用场景

当下&#xff0c;视频美颜SDK正不断演进&#xff0c;本文将深入探讨视频美颜SDK的发展趋势&#xff0c;探讨未来可能的方向和广泛的应用场景。 1.深度学习与视频美颜的融合 未来&#xff0c;我们可以期待看到更多基于深度学习算法的视频美颜SDK&#xff0c;为用户提供更高质量…

FinalShell连接虚拟机遇到的问题

在下载好VM后也安装好了虚拟机&#xff08;我这里使用Centos7.5&#xff09;&#xff0c;但是当使用FinalShell连接虚拟机的时候&#xff0c;一直提示连接超时。。。。 后来找了半天&#xff0c;发现是有次校园网和VM虚拟机冲突&#xff0c;就把虚拟机的网络连接给关了&#x…

Redis 核心知识总结

Redis 核心知识总结 认识 Redis 什么是 Redis&#xff1f; Redis 是一个由 C 语言开发并且基于内存的键值型数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 有以下几个特…

Python漂浮爱心完整代码

文章目录 环境需求完整代码详细分析环境需求 python3.11.4PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.blog.csdn.net/arti…

我的2023年,平淡中寻找乐趣

文章目录 两个满意我学会了自由泳。学习英语 一个较满意写博客 2024的期望 2023年&#xff0c;我有两个满意&#xff0c;一个较满意。 两个满意 我学会了自由泳。 开始练习自由泳是从2023年3月份&#xff0c;我并没有请教练&#xff0c;而是自己摸索。在抖音上看自由泳的视频…

im6ull学习总结(二)Framebuffer 应用编程

1 LCD操作原理 linux中通过framebuffer驱动程序来控制LCD。framebuffer中包含LCD的参数&#xff0c;大小为LCD分辨率xbpp。framebuffer 是一块内存 内存中保存了一帧图像。 关于图像的帧指的是在图像处理中&#xff0c;一帧&#xff08;Frame&#xff09;是指图像序列中的单个…

Unity中URP下精度修饰符real

文章目录 前言一、real是什么&#xff1f;1、我们在项目的Packages下找到如下文件&#xff1a;2、HAS_HALF(1代表有half精度&#xff0c;0代表没有half精度)3、PREFER_HALF4、REAL_IS_HALF5、如果 real is half6、否则为float 二、总结 前言 在使用雾效时&#xff0c;ComputeFo…

基于动态窗口的航线规划

MATLAB2016b可以运行 % ------------------------------------------------------------------------- % File : DWA 算法 % Discription : Mobile Robot Motion Planning with Dynamic Window Approach % Author :Yuncheng Jiang % License : Modified BSD Software License A…

【JAVA】OPENGL绕XYZ轴旋转立体图效果

JAVA-OPENGL绕XYZ轴旋转立体图效果_哔哩哔哩_bilibiliJAVA-OPENGL绕XYZ轴旋转立体图效果开始显示的是绕X轴、Y轴、Z轴旋转&#xff0c;后边是同时绕两个轴旋转&#xff0c;头有点晕&#xff0c;反应不过来了。, 视频播放量 1、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 0、转…

canal 数据同步组件

canal 数据异构组件 为啥要使用这个组件&#xff1f; 在更新DB的时候不同步更新到redis&#xff0c;es等数据库中&#xff0c;时间太久&#xff0c;而且可能会存在同步失败的问题&#xff0c;因此引入canal去拉取DB的数据&#xff0c;再去更新到redis&#xff0c;es等数据库中&…

【Harmony OS - 应用数据持久化】

概述 应用数据持久化就是应用将内存中的数据通过文件或者数据库的方式保存在设备本机上。HarmonyOS标准系统支持一下三种f方式进行持久化处理&#xff1a;包括用户首选项、键值型数据库、关系型数据库。 用户首选项 用户首选项(Preferences) 是通过将数据(Key-Value键值)保存…

【C++篇】讲解Vector容器的操作方法

文章目录 &#x1f354;vector容器概念&#x1f339;操作方法⭐赋值操作⭐容量和大小⭐插入和删除⭐数据存取 &#x1f354;vector容器概念 vector 是 C 标准库中的一个容器&#xff0c;它提供了一种动态数组的实现。vector 容器可以存储任意类型的元素&#xff0c;并且可以根…

【办公技巧】为什么有的pdf不能编辑

pdf文件大家应该都经常接触&#xff0c;但是不知道大家会遇到这种情况&#xff1a;有些PDF文件打开之后无法编辑&#xff1f;是什么原因呢&#xff1f;今天我们来分析一下都是那些原因导致的。 首先我们可以考虑一下&#xff0c;PDF文件中的内容是否是图片&#xff0c;如果确认…

【中小型企业网络实战案例 四】配置OSPF动态路由协议

【中小型企业网络实战案例 三】配置DHCP动态分配地址-CSDN博客 【中小型企业网络实战案例 二】配置网络互连互通-CSDN博客 【中小型企业网络实战案例 一】规划、需求和基本配置_大小企业网络配置实例-CSDN博客 配置OSPF 由于内网互联使用的是静态路由&#xff0c;在链路出…

软件测试/测试开发丨Git常用命令学习笔记

基于 Git 的远程仓库 远程仓库地址备注GitHubgithub.com/世界上最主流的远程开源仓库。Giteegitee.com/国内目前比较主流的开源仓库&#xff0c;也可以私有化部署。&#xff08;推荐&#xff09;GitLabgitlab.com/私有化部署&#xff0c;企业使用较多。 Git 远程仓库的应用场…

腾讯云服务器怎么购买?购买流程

腾讯云轻量应用服务器购买指南&#xff0c;有两个入口&#xff0c;一个是在特价活动上购买&#xff0c;一个是在轻量应用服务器官方页面购买&#xff0c;特价活动上购买价格更便宜&#xff0c;轻量2核2G3M带宽服务器62元一年起&#xff0c;阿腾云atengyun.com分享腾讯云轻量应用…

Codeforces Pinely Round 3 (Div. 1 + Div. 2) A~F

A.Distinct Buttons(思维) 题意&#xff1a; 你在开始时站在点 ( 0 , 0 ) (0,0) (0,0)&#xff0c;同时&#xff0c;手上有一个遥控器&#xff0c;上面有四个按钮&#xff1a; U:移动到 ( x , y 1 ) (x, y 1) (x,y1)的位置 R:移动到 ( x 1 , y ) (x 1, y) (x1,y)的位置 …

数据集介绍【02】CIFAR10

CIFAR10数据集共有60000个样本&#xff0c;每个样本都是一张32*32像素的RGB图像&#xff08;彩色图像&#xff09;&#xff0c;每个RGB图像又必定分为3个通道&#xff08;R通道、G通道、B通道&#xff09;。这60000个样本被分成了50000个训练样本和10000个测试样本。 CIFAR10数…