linux驱动编写之poll机制
一、概念
1、poll情景描述
以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值。这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行;倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生。
这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点。倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行,而不是没有按键按下,就永远休眠。这种例子其实还有很多,比方说两人相亲,男方等待女方给个确定相处的信,男方不可能因为女方不给信,就永远等待下去,双方需要一个时间节点。这个时间节点,就是说超过这个时间之后,不能再等了,程序还要继续运行,需要采取其他的行动来解决问题。
example:
单片机编程,等待IIC设备一个事件的发生,如果在允许的时间内发生了就返回1(SUCCESS),否则返回0(ERROR)。
uint8_t I2C_WaitForEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT,int32_t delay)
{ while(!I2C_CheckEvent(I2Cx, I2C_EVENT) && (delay-- > )); if(delay < ){
return ;
} return ;
}
此段函数代码可以这样来调用,如下:
int8_t I2C_EE_PageWrite(u8* pBuffer, u16 WriteAddr, u8 NumByteToWrite)
{
.............
if(I2C_WaitForEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED, ) != ){
return -;
}
............
}
这个例子是STM32单片机写i2cflash--AT24C02,可见上述的页写函数调用的等待字节传输完成函数(I2C_EVENT_MASTER_BYTE_TRANSMITTED)
,如果在限定的时间内(CPU将100000减到0),还没有成功写入,那么就将返回超时错误,页写函数也会返回写入失败的错误信息。之后,任务重新得到了运行。
对于单片机这样通常单任务运行的状况,必须采取这样的措施。如果没有超时限制,那么程序将陷入死机,不能再继续运行。
2、linux应用程序poll的使用
对于类似的场景,linux系统使用poll功能来解决这样的问题。而且,与上述单片机等待方式不同,linux系统再调用poll()函数时候,如果没有发生需要的事件,那么进程进入休眠。如果在限定的时间内得到需要的事件,那么成功返回,如果没有则返回超时错误信息。
可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。下面,以一个应用例程来说明poll的应用程序使用方法:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <poll.h> int main(int argc, char **argv)
{
int i;
int ret;
int fd;
unsigned char keys_val;
struct pollfd fds[]; fd = open("/dev/buttons", ); // 打开设备
if (fd < ) {
printf("Can't open /dev/buttons\n");
return -;
} fds[].fd = fd;
fds[].events = POLLIN; while () {
ret = poll(fds,, );
if(ret == )
{
printf("time out!\n");
}
else
{
read(fd, &keys_val, sizeof(keys_val));
printf("keys_val = 0x%x\n",keys_val);
}
} close(fd);
return ;
}
例程实现的功能是这样的:用poll()函数监测按键按下的事件,如果按下了就将键值打印出来;如果超过5S,还没有按键按下,就打印出超时信息。
3、poll()函数
函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
输入参数
fds 可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 请求的事件类型,监视驱动文件的事件掩码 */
short revents; /* 驱动文件实际返回的事件 */
} ;nfds 监测驱动文件的个数
timeout 超时时间,单位为ms
事件类型events 可以为下列值:
POLLIN 有数据可读
POLLRDNORM 有普通数据可读,等效与POLLIN
POLLPRI 有紧迫数据可读
POLLOUT 写数据不会导致阻塞
POLLER 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起事件
POLLNVAL 无效的请求,打不开指定的文件描述符
返回值
有事件发生 返回revents域不为0的文件描述符个数(也就是说事件发生,或者错误报告)
超时 返回0;
失败 返回-1,并设置errno为错误类型
二、驱动实现方法
/* 定义一个等待队列,这个等待队列实际上是由中断驱动的,当中断发生时,会令挂接到这个等待队列的休眠进程唤醒 */
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static unsigned drivers_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); /* 将进程挂接到button_waitq等待队列下 */
/* 根据实际情况,标记事件类型 */
if (ev_press)
mask |= POLLIN | POLLRDNORM;
/* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */
return mask;
}
上述代码展示了一个poll()函数功能,具体对应的底层驱动实现细节。利用这样的框架,我们可以写出类似驱动的poll功能。但是,这个框架很难理解,不知道为什么这样编写?为此,我们需要了解linux系统poll功能实现的机制。
三、linux内核poll实现机制
从应用程序调用poll()函数开始,一直到调用drivers_poll函数,期间的过程很复杂,捡主要的内容列出来:
app: poll
|
drv:sys_poll
|
— do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, struct timespec * end_time)
|
- poll_initwait(&table); > 实际效果:令函数指针 table.pt.qproc = __pollwait,这个函数指针最终会传递给poll_wait函数调用中的wait->qproc
|
- do_poll(nfds, head, &table, end_time);
|
_ for ( ; ; )
{
for (; pfd != pfd_end; pfd++) { /* 可以监测多个驱动设备所产生的事件 */
if (do_pollfd(pfd, pt)) {
|
_ mask = file->f_op->poll(file, pwait); > 实际效果:执行我们写的drivers_poll(file,pwait)
|
_ poll_wait(file, &button_waitq, wait); > 实际效果:执行__pollwait(file, &button_waitq, wait),也就是将
进程挂接到button_waitq等待队列下
|
— mask赋值 ; return mask; /* 返回事件类型 */
pollfd->revents = mask; /* 将实际事件类型返回 */
count++; pt = NULL;
}
}
if (count || timed_out) /* 如果有事件发生,或者超时,则跳出poll */
break;
if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)) /* 如果没有事件发生,那么陷入休眠状态 */
timed_out = ;
}
由此可见,我们的drivers_poll()函数,是系统在执行sys_poll()过程中的一个调用,调用的目的是“将进程挂接到等待队列下”和“返回事件类型mask”。当已经发生了请求事件,那么通过标记mask非0,if (do_pollfd(pfd, pt))判断为真,令count++,从而可以直接令poll()函数成功返回。如果还没有发生请求的事件,那么mask被标记为0,进程将通过函数poll_schedule_timeout()陷入休眠状态。一旦发生了请求的事件,因为之前已经将进程挂接到等待队列下,所以进程将被唤醒,重新执行drivers_poll(),而显然此时能够成功返回。
备注:分析的源码版本为linux-2.6.30.4。
参考资料:韦东山linux教学视频
linux驱动编写之poll机制的更多相关文章
- 字符设备驱动(六)按键poll机制
title: 字符设备驱动(六)按键poll机制 tags: linux date: 2018-11-23 18:57:40 toc: true --- 字符设备驱动(六)按键poll机制 引入 在字 ...
- Linux 驱动——Button驱动3(poll机制)
button_drv.c驱动文件: #include <linux/module.h>#include <linux/kernel.h>#include <linux/f ...
- 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖
文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...
- linux驱动编写(Kconfig文件和Makefile文件)
在Linux编写驱动的过程中,有两个文件是我们必须要了解和知晓的.这其中,一个是Kconfig文件,另外一个是Makefile文件.如果大家比较熟悉的话,那么肯定对内核编译需要的.config文件不陌 ...
- Ubuntu 16.04下Linux驱动编写第一步
源码环境的搭建 Install源码 >sudo apt-cache search linux-source linux-source - Linux kernel source with Ubu ...
- Linux驱动编写(块设备驱动代码)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 按照ldd的说法,linux的设备驱动包括了char,block,net三种设备.char设备 ...
- linux驱动编写之进程独占驱动
一.描述 嵌入式开发系统中,有各种硬件资源,而有些硬件资源使用时候是需要进程独占的.也就是说,同一时刻只有一个进程允许使用这个硬件资源,其他的进程只能放弃执行或者挂起等待.在设计其对应驱动的时候,就需 ...
- linux驱动编写之中断处理
一.中断 1.概念 学过单片机的应该非常清楚中断的概念,也就是CPU在正常执行程序过程中,出现了突发事件(中断事件),于是CPU暂停当前程序的执行,转去处理突发事件.处理完毕后,CPU又返回被中断的程 ...
- Linux驱动开发5——同步机制
上一章讲到了并发,指的是多个进程同时存取临界区资源的处理机制.这一章讲的同步机制,讲的是多个进程之间协同工作的处理机制,如临界区数据还没有准备好,A进程负责准备数据,B进程等待A进程完成之后读取数据. ...
随机推荐
- zabbix监控tomcat(使用jmx监控,但不使用系统自带模版)
一,zabbx使用jmx监控tomcat的原理分析 1.Zabbix-Server找Zabbix-Java-Gateway获取Java数据 2.Zabbix-Java-Gateway找Java程序(j ...
- 如何在HTTP客户端与服务器端之间保持状态(转)
HTTP协议与状态保持 HTTP协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都 ...
- js实现进度条
不多说,直接上代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- 前后端分离djangorestframework——版本控制组件
什么是版本控制 在实际开发中,随着时间的更新迭代,我们维护的项目可能会有很多个版本,所以我们写的API也有很多个版本,但是迭代到高版本,不可能以前的版本就不用了,比如一个手机端的app,不定期发布新版 ...
- visual studio 的git插件推荐
TGit 支持vs 2013/15/17及更高版本,需要搭配 TortoiseGit 和 MSysGit 一起使用,当然这两者也是我在windows下使用git的推荐组合. 历史版本的diff查看使用 ...
- php学习----文件系统
PHP文件系统之读取文件内容 PHP具有丰富的文件操作函数,最简单的读取文件的函数为file_get_contents,可以将整个文件全部读取到一个字符串中. $content = file_get_ ...
- 【转载】failed to initialize nvml driver/library version mismatch ubuntu
英伟达驱动版本是384.130 显示的NVRM version: NVIDIA UNIX x86_64 Kernel Module是:384.130. 若是旧的版本就会出现如下问题. 这个问题出现的原 ...
- puppet 横向扩展(三)
Table of Contents 1. 概述 2. 实验环境 3. 实验步骤 3.1. 机器B 的配置 3.2. 机器A 的配置 3.3. 测试配置结果 概述 横向扩展实验之三 – 将CA 认证服务 ...
- 《Java大学教程》—第19章 改进用户界面
用户与程序交互的媒介称为用户界面(user interface)或人机界面(human-computer interface). 19.2 Border接口8个实现Border接口的标准边框类: ...
- Win7 下安装ubuntu14.04双系统
下面介绍一下利用wubi在Windows中安装Ubuntu 14.04的教程,或者说安装方法和注意事项. 方法一:直接下载wubi.exe 方法二:直接下载ubuntu-14.04-desktop- ...