poll机制总结、模板、要点:
一、代码介绍: 代码①为共用key驱动(独立四个按键驱动,同一份代码)
代码②和③为用两种方式调用底层poll机制的代码,poll()和select()两种方式
二、要点:
(1)每次调用,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)它们支持的文件描述符数量太小了,默认是1024
(4)select与poll原理基本一样,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多。驱动代码共用一份,大家只需对比应用层代码,结构体不一样,一个是case形式的遍历,一个是for形式的遍历
三、要产生一下观念:
poll机制是一种同步机制!
poll机制是驱动和应用程序结合使用的一种机制!
poll机制是一个应用程序同时管理多个事件的时候,使用到的一种机制!
/*********************底层驱动*********************/
/*功能介绍:配合poll机制,编写了四个独立的key驱动,如下为其中一个key的驱动代码,代码基本一样。
key按下--->
产生中断进入①----->
mod_timer()计时开始---->
进入定时器中断②---->
condition置1为真,wake_up()唤醒等待------>
wait_event()阻塞解除,③读操作进行,记住此时condition为真,然后进入应用层判断
*/
#include "poll.h"
static int i = 0;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static bool condition = 0;
static struct timer_list g_ttimer;
//①外部中断服务函数
static irqreturn_t key_irq_handler(int irq, void * arg)
{
struct keyinfo* pkey = (struct keyinfo*)arg;
g_ttimer.data = (unsigned long)pkey;
mod_timer(&g_ttimer, jiffies + HZ / 200);
return IRQ_HANDLED;
}
//②定时器中断服务函数
static void key_timer_handler(unsigned long arg)
{
struct keyinfo* pk = (struct keyinfo*)arg;
printk("timer_handler: %s pressed!\n", pk->name);
condition = 1;
wake_up_interruptible(&wq);
}
// ③ read(); 阻塞读
static ssize_t key_read(struct file * filp, char __user * buff, size_t count, loff_t * offset)
{
char buf[32] = {0};
int ret = 0;
wait_event_interruptible(wq, condition);
condition = 0;
if(!gpio_get_value(g_arrkey[i].gpio))
{
sprintf(buf, "%s is pressed!", g_arrkey[i].name);
}
ret = copy_to_user(buff, buf, min(strlen(buf), count));
return min(strlen(buf), count);
}
static unsigned int key_poll(struct file * filp, struct poll_table_struct * table)
{
unsigned int mask = 0;
poll_wait(filp, &wq, table); //阻塞 1.当前设备触发中断 2.超时时间到来 3.其他事件触发 都会导致阻塞消失
if(condition == 1)
{
mask = POLLIN | POLLRDNORM;
return mask;
}
return 0;
}
static struct file_operations g_tfops = {
.owner = THIS_MODULE,
.read = key_read,
.poll = key_poll,
};
static struct miscdevice g_tmiscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "key1",
.fops = &g_tfops,
};
static int __init xyd_irq_init(void)
{
int ret = 0;
gpio_request(g_arrkey[i].gpio, g_arrkey[i].name);
s3c_gpio_cfgpin(g_arrkey[i].gpio, S3C_GPIO_INPUT);
g_arrkey[i].irq = gpio_to_irq(g_arrkey[i].gpio);
ret = request_irq(g_arrkey[i].irq, key_irq_handler,
IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
g_arrkey[i].name, &g_arrkey[i]);
//初始化定时器
setup_timer(&g_ttimer, my_timer_handler, 0);
misc_register(&g_tmiscdev);
return 0;
}
static void __exit my_irq_exit(void)
{
misc_deregister(&g_tmiscdev);
del_timer_sync(&g_ttimer);
free_irq(g_arrkey[i].irq, &g_arrkey[i]);
gpio_free(g_arrkey[i].gpio);
}
module_init(my_irq_init);
module_exit(my_irq_exit);
MODULE_LICENSE("GPL");
/************************应用层代码poll()********************/
#include <stdio.h>
#include <poll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char* argv[])
{
int fd1 = 0;
int fd2 = 0;
int fd3 = 0;
int fd4 = 0;
fd1 = open("/dev/key1", O_RDWR);
fd2 = open("/dev/key2", O_RDWR);
fd3 = open("/dev/key3", O_RDWR);
fd4 = open("/dev/key4", O_RDWR);
if((fd1 < 0) || (fd2 < 0) || (fd3 < 0) || (fd4 < 0))
{
printf("open failed!\n");
return -1;
}
struct pollfd keyinfo[] = {
{fd1, POLLIN, 0}, //{文件描述符,事件类型,返回事件类型}
{fd2, POLLIN, 0},
{fd3, POLLIN, 0},
{fd4, POLLIN, 0},
};
int i = 0;
char buf[32] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
poll(keyinfo, sizeof(keyinfo)/sizeof(keyinfo[0]), -1);
//一方面,传递结构体keyinfo信息,将四个加入等待队列
//另一方阻塞,等待事件触发(condition=1时,接受到底层驱动mask返回值,解除阻塞)
for(i = 0; i < 4; i++)
{
if(keyinfo[i].revents & keyinfo[i].events)
{
read(keyinfo[i].fd, buf, sizeof(buf));
printf("%s\n", buf);
}
}
}
close(fd1);
close(fd2);
close(fd3);
close(fd4);
return 0;
}
/************************应用层代码select()********************/
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char* argv[])
{
int ret = 0;
int fd1 = 0;
int fd2 = 0;
int fd3 = 0;
int fd4 = 0;
fd1 = open("/dev/key1", O_RDWR);
fd2 = open("/dev/key2", O_RDWR);
fd3 = open("/dev/key3", O_RDWR);
fd4 = open("/dev/key4", O_RDWR);
if((fd1 < 0) || (fd2 < 0) || (fd3 < 0) || (fd4 < 0))
{
printf("open failed!\n");
return -1;
}
fd_set rdfds; //声明fd_set结构体
FD_ZERO(&rdfds); //清空结构体
FD_SET(fd1, &rdfds); //将fd加入到rdfds集合
FD_SET(fd2, &rdfds);
FD_SET(fd3, &rdfds);
FD_SET(fd4, &rdfds);
fd_set tmp = rdfds; //用tmp保存rdfds集合
char buf[32] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
rdfds = tmp; //每次都需要拷贝一次fd集合
select(fd4+1, &rdfds, NULL, NULL, NULL); //阻塞,等待驱动从内核返回mask
//调用select,内核开始遍历传入fd集合
if(FD_ISSET(fd1, &rdfds)) //判断fd1是否存在于rdfds集中
{
read(fd1, buf, sizeof(buf)) ;
printf("fd1: %s\n", buf);
}
if(FD_ISSET(fd2, &rdfds))
{
read(fd2, buf, sizeof(buf)) ;
printf("fd2: %s\n", buf);
}
if(FD_ISSET(fd3, &rdfds))
{
read(fd3, buf, sizeof(buf)) ;
printf("fd3: %s\n", buf);
}
if(FD_ISSET(fd4, &rdfds))
{
read(fd4, buf, sizeof(buf)) ;
printf("fd4: %s\n", buf);
}
}
close(fd1);
close(fd2);
close(fd3);
close(fd4);
return 0;
}