poll机制

在之前的按键驱动程序里分别使用了查询和中断的模式,
1、使用查询的时候程序需要在一个循环里不停的去,读取按键的状态,这个时候可以看到CPU的使用率是非常高的。
2、改进之后使用中断的模式进行查询,虽然在中断里可以让进城休眠,依次来降低CPU的占有率,但是若没有发生中断
程序会一直在等待,很显然,这样的情况也不是我们想要的。
今天继续对这个程序进行改进,使用的是另一种机制-poll,在进行应用编程的时候,想必都多多少少接触过poll机制,
在网络编程里用的较多,在最简单的程序里我们只需要去监控一个网络套接字,这时候直接使用read/write就可以实现,
但是,当我们要去监控多个套接字的时候就可以使用poll函数去监控,当其中某一个满足条件时返回,应用进行读写。
先来熟悉一下应用空间的poll函数,
在这里插入图片描述

poll函数一共有三个参数,
1、第一个参数是一个描述符合集,也就是我们需要监控的描述符的集合,它是struct pollfd类型的指针
    struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
     }
     pollfd中包含一个描述符fd,以及这个描述符需要监控的事件events
2、第二个参数是第一个集合里描述符的个数
3、第三个参数是超时时间,单位为毫秒,

函数用起来很简单,我们来实际的编写一个程序看看,针对我们的按键驱动程序

//打开设备
 fd = open("/dev/buttons",O_RDWR);
  if (fd < 0) {
	printf("can not open\n");
	return 0;
 }
 //定义pollfd变量,在这里只需要监控一个描述符,所以数组个数是1
 struct pollfd fds[1];
 //将数组第一个元素的描述符赋值为fd,事件为对应的数据输入事件
 fds[0].fd = fd;
 fds[0].events = POLLIN;
 //超时时间为5秒
 ret = poll(fds,1,5000);
 if(ret == 0) {
 	printf("time out\n");
 	return 0;
 }else {
	//读取按键数据
	ret = read(fd,&key,1);
	if (ret < 0) {
		printf("read failed\n");
	}
	printf("key_val = 0x%x\n",key);
}

好了,上面就是监控一个按键描述符的poll函数,看起来很简单吧,接下来编写对应的驱动层程序,其他部分的程序和之前的一样,
下面只写和poll相关的部分

//文件操作函数集中实现poll函数
static struct file_operations key_ops = {
    .owner   =   THIS_MODULE,
 .open    = key_open,
 .read    = key_read,
 .release = key_close,
 .poll    = key_poll,
};

//poll函数的一般写法
unsigned int key_poll(struct file *file, poll_table *wait)
{
 unsigned int mask = 0;
 poll_wait(file, &button_waitq, wait); // 不会立即休眠
  if (ev_press)
  mask |= POLLIN | POLLRDNORM;
   return mask;
}
 //read函数
 ssize_t key_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
 copy_to_user(buf,&key_val,1);
 ev_press = 0;
  return 1;
}
//中断函数
static irqreturn_t button_irq(int irq,void * dev_id)
{
	 struct pin_desc *p = (struct pin_desc*)dev_id;
	 pin_val = s3c2410_gpio_getpin(p->pin);
	 if (pin_val) {
	  key_val = 0x80 | p->key_val;
	  } else {
	  	key_val = p->key_val; 
	  } 
	  ev_press = 1;
 	wake_up_interruptible(&button_waitq);
  return IRQ_RETVAL(IRQ_HANDLED);
}

总结:
1、要使用poll机制首先就要在驱动层实现poll函数,在文件操作函数集中给poll函数指针赋值,然后实现对应的函数
2、应用层的poll函数会调用系统的sys_poll函数,sys_poll函数又会调用do_sys_poll函数,函数传入的参数就是超时时间
在这里插入图片描述
do_sys_poll函数里最终会调用到do_poll函数,传入的参数分别是
a、上层pollfd中描述符的个数 b、pollfd的链表式结构,head头指向上层传下来的结构数组
c、等待队列 d、超时时间
再进入do_poll函数里看一下它的流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
首先是一个死循环,里边再对描述符集合进行循环,使用do_pollfd检测每一个描述符,do_pollfd实际上调用的是驱动函数里的
file->f_op->poll函数,当这个函数返回为一个非0值的时候计数值会加一,当遍历完集合之后如果计数值为0,也就是没有一个描述符满足条件的时候进程会调用上面的函数休眠,休眠结束超时时间也已经到达,再次循环的时候break退出,但是如果在循环的时候驱动函数poll返回一个非0值,下面的条件之一将会满足,poll函数退出,往下执行,应用程序可以进行读写操作。

回到驱动程序中,当应用程序调用poll函数的时候最终会调用到驱动层的Poll函数,在驱动的Poll函数里首先执行
"poll_wait(file, &button_waitq, wait);"函数初始化等待队列并将程序加入到等待队列中,但是这时不会立即休眠
程序会像上面分析的一样判断pollfd中的描述符是否满足条件,如果满足条件则会立即退出,如果不满足条件就会在等待队列中
等待。
所以,在驱动程序中,第一次执行poll函数的时候,ev_press的值是0,所以mask的值也是0,最终返回的是0,poll函数返回的是0,
do_pollfd得到的返回值是0,计数值就不会增加,不会跳出循环,程序继续执行就会休眠,当有按键按下的时候,先把ev_press的值设置为1
然后唤醒等待队列,do_poll函数重新开始下一次的循环,这个时候又会进入do_pollfd中判断状态,最终调用到驱动层的poll函数
由于在中断里把ev_press的值设置为1,所以这里返回的mask将不再是0,dp_poll函数里的计数值加一,退出,返回应用空间,最终结果就是没有
达到超时时间检测到有数据可读,
1、在使用poll机制的时候不需要我们主动的将该进程加入到等待队列中去,因为在循环里判断条件的时候会去判断,如果条件不满足系统会自动的将
进程加入等待队列中休眠,我们只需要在中断函数里将等待队列唤醒即可
2、设置驱动poll函数里的返回值,0值和非0值,因为这会影响到do_pollfd函数,最终影响到循环是否结束
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值