上一节应用程序的死循环里的读函数是一直在读的;在实际的应用场所里,有没有那么一种情况,偶尔有数据、偶尔没有数据,答案当然是有的。--》poll机制:Poll机制实现的是一定时间如果没有按键的话就返回。以前是如果没有按键不返回  一直处于休眠状态。

poll调用(应用层) :

#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

struct pollfd结构如下:【在源码文件poll.h文件中】

struct pollfd {
  int fd;
  short events;
  short revents;
};

这个结构中fd表示文件描述符,events表示请求检测的事件,revents表示检测之后返回的事件,如果当某个文件描述符有状态变化时,revents的值就不为空。

  1. fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
  2. nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
  3. timeout:poll函数调用阻塞的时间,单位是MS(毫秒)

return value:

  1. 大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的socket描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的socket描述符,然后判断这个里面有哪些事件以读取数据。
  2. 等于0:表示没有socket描述符有状态变化,并且调用超时。
  3. 小于0:此时表示有错误发生,此时全局变量errno保存错误码。

内核实现:

poll机制总结:

1. poll > sys_poll > do_sys_poll >poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。

2. 接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数

它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;

它还判断一下设备是否就绪。

3. 如果设备未就绪,do_sys_poll里会让进程休眠一定时间

4. 进程被唤醒的条件有2:一是上面说的“一定时间”到了,二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。

5. 如果驱动程序没有去唤醒进程,那么chedule_timeout(__timeou)超时后,会重复2、3动作,直到应用程序的poll调用传入的时间到达。

博客中http://blog.csdn.net/u012719256/article/details/52663292提到:

内核中poll实现非常固定,就以下两个步骤

static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0; // 1. 将button_waitq添加到等待队列
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press) // 如果有按键按下了,返回的掩码表示不休眠
mask |= POLLIN | POLLRDNORM; // 2. 返回掩码
return mask;
}

暂时做个标记吧,现在只知道这个格式

代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#define DEVICE_NAME "mybutton" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
static struct class *button_class;
static struct class_device *button_dev_class;
int major; static volatile int press_cnt=;/* 按键被按下的次数(准确地说,是发生中断的次数) */ static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//定义等待队列 /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
static volatile int ev_press = ; static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
// volatile int *press_cnt = (volatile int *)dev_id;
press_cnt =press_cnt + ; /* 按键计数加1 */
ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
return IRQ_RETVAL(IRQ_HANDLED);//中断处理程序应该返回一个值,用来表明是否真正处理了一个中断,如果中断例程发现其设备的确要处理,则应该返回IRQ_HANDLED, //否则应该返回IRQ_NONE,我们可以通过这个宏来产生返回值,不是本设备的中断应该返回IRQ_NONE }
/* 应用程序对设备文件/dev/xxx 执行open(...)时,
* 就会调用button_open函数
* 就会调用button_open函数
*/
static int button_open (struct inode *inode, struct file *filep)
{
int err;
err=request_irq(IRQ_EINT2,buttons_interrupt,IRQF_TRIGGER_FALLING,"KEY3",NULL); if (err) {
// 释放已经注册的中断
free_irq(IRQ_EINT2, NULL);
return -EBUSY;
} return ;
} /* 应用程序对设备文件/dev/buttons执行close(...)时,
* 就会调用buttons_close函数
*/
static int buttons_close(struct inode *inode, struct file *file)
{ free_irq(IRQ_EINT2, NULL);
return ;
} ssize_t button_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long err; /* 如果ev_press等于0,休眠 */
wait_event_interruptible(button_waitq, ev_press);//阻塞
/* 执行到这里时,ev_press等于1,将它清0 */
ev_press = ;
/* 将按键状态复制给用户,并清0 */
err = copy_to_user(buf, (const void *)&press_cnt, count);
//memset((void *)&press_cnt, 0, sizeof(press_cnt));
return err ? -EFAULT : ;
} static unsigned buttons_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 将进程挂接到button_waitq队列中
/* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0
* 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1
*/
if(ev_press)
{
mask |= POLLIN | POLLRDNORM; /* POLLIN表示有数据可读 POLLRDNORM表示有普通数据可读*/
}
/* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */
return mask;
} /* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations button_ops=
{
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = button_open,
.read = button_read,
.release = buttons_close,
.poll = buttons_poll,
}; /*
* 执行insmod命令时就会调用这个函数
*/ static int button_init(void)
{ /* 注册字符设备
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
* LED_MAJOR可以设为0,表示由内核自动分配主设备号
*/
major = register_chrdev(, DEVICE_NAME, &button_ops);
if (major < )
{
printk(DEVICE_NAME " can't register major number number::%d\n",major);
return ;
}
printk(DEVICE_NAME " initialized1\n");
button_class = class_create(THIS_MODULE, "button");
if (IS_ERR(button_class))
return PTR_ERR(button_class);
button_dev_class = class_device_create(button_class, NULL, MKDEV(major, ), NULL, "my_button"); /* /dev/my_button */ return ; } /*
* 执行rmmod命令时就会调用这个函数
*/
static void button_exit(void)
{
class_device_unregister(button_dev_class);
class_destroy(button_class);
/* 卸载驱动程序 */
unregister_chrdev(major, DEVICE_NAME);
} /* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(button_init);
module_exit(button_exit); /* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net");// 驱动程序的作者
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");// 一些描述信息
MODULE_LICENSE("GPL"); // 遵循的协议

test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h> int main(int argc,char**argv)
{
int fd;
int cnt = 0;
int ret; struct pollfd fds[1];/*每一个pollfd结构体指定了一个被监视的文件描述符*/ //打开设备
fd = open("/dev/my_button",O_RDWR); if(fd<0)
{
perror("open fail \n");
return -1;
} fds[0].fd=fd;
fds[0].events=POLLIN;
while(1)
{
/*poll函数返回0时,表示5s时间到了,而这段时间里,没有事件发生"数据可读" */
ret=poll(fds,1,5000); if (ret==0)
{
printf("time out\n");
}
else /* 如果没有超时,则读出按键值 */
{
read(fd,&cnt, sizeof(cnt)); //read()
printf("button has been pressed %d timea\n",cnt);
}
}
close(fd);
return 0;
}

  

结果:

参考:

http://blog.csdn.net/lwj103862095/article/details/17536069

http://blog.csdn.net/u012719256/article/details/52663292

http://www.cnblogs.com/mylinux/p/5090264.html

http://blog.csdn.net/ruoyunliufeng/article/details/24188693

最新文章

  1. from表单提交数据之后,后台对象接受不到值
  2. Liferay 6.2 改造系列之二十四:修改liferay密码的加密方式
  3. Linux 进程退出后自动启动
  4. solr与.net系列课程(六)solr定时增量索引与安全
  5. 初探JAVA中I/O流(二)
  6. python多态
  7. MotionEvent的getX(),getY()与getRawX(),getRawY()区别
  8. 团队作业4——第一次项目冲刺 FiFtH DaY
  9. 表迁移工具的选型-复制ibd的方法
  10. [HNOI2010]PLANAR
  11. ansible基础-安装与配置
  12. Vjudge Code
  13. java同步和互斥【用具体程序说明】
  14. pip 安装 MySQL-python 报错
  15. Android 框架 Afinal使用
  16. 数据特征分析:3.统计分析 &amp; 帕累托分析
  17. nodejs 搭建自己的简易缓存cache管理模块
  18. (转)ASP.NET中常见文件类型及用途
  19. 【Android UI设计与开发】使用ViewPager实现欢迎引导页面
  20. vagrant简单学习使用

热门文章

  1. IOS UIButton用法详解
  2. ios系统crash文件分析
  3. spring入门教程——笔记
  4. Read excel and put cell data into HashMap
  5. 测试数学公式latex
  6. VMware下利用ubuntu13.04建立嵌入式开发环境之三
  7. 使用FWTools来导入shp数据到mysql
  8. js①
  9. javascript学习之【new操作符】
  10. Postgresql存储过程调试:PostgreSQL 之 Function NOTICE