(一)poll轮询的作用
以阻塞的方式打开文件,那么对多个文件读写时,若某个文件未准备好,则系统会处于读写阻塞,并影响其他文件的读写,poll轮训就是实现既可使用输入输出流又不想阻塞在任何一个设备的读写操作
调用poll函数返回时,会返回一个文件是否可读写的标志状态,用户程序根据不同的标志状态来读写相应的文件,实现阻塞方式打开但是非阻塞方式读写的结果
(二)poll轮询相关的接口
- 系统层接口:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd *fds: 轮训描述符的集合数据结构
struct pollfd {
int fd; /* file descriptor */轮训的文件描述符
short events; /* requested events */对于轮训的文件描述所检测的事件类型
short revents; /* returned events */对于轮训的文件描述所返回的事件类型
};
nfds_t nfds:轮训描述符的个数
int timeout:轮询时间
请求事件和返回事件类型:
POLLIN There is data to read.
POLLOUT Writing is now possible, though a write larger that the available space
in a socket or pipe will still block (unless O_NONBLOCK is set).
POLLPRI: There is urgent data to read
POLLERR: Error condition (only returned in revents; ignored in events).
POLLHUP: Hang up (only returned in revents; ignored in events).
POLLNVAL: Invalid request: fd not open (only returned in revents; ignored in events).
POLLRDNORM Equivalent to POLLIN.
POLLRDBAND Priority band data can be read (generally unused on Linux).
POLLWRNOR Equivalent to POLLOUT.
POLLWRBAND Priority data may be written.
RETURN VALUE
On success, a positive number is returned; this is the number of structures which have
nonzero revents fields (in other words, those descriptors with events or errors reported).
A value of 0 indicates that the call timed out and no file descriptors were ready. On
error, -1 is returned, and errno is set appropriately.
- 内核层接口:
unsigned int (*poll) (struct file *, struct poll_table_struct *);//file_operations结构体里
设置轮训的等待队列poll_wait:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && p->_qproc && wait_address)
p->_qproc(filp, wait_address, p);
}
唤醒等待队列节点wake_up_interruptible或wake_up:
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
/**
* __wake_up - wake up threads blocked on a waitqueue.
* @q: the waitqueue
* @mode: which threads
* @nr_exclusive: how many wake-one or wake-many threads to wake up
* @key: is directly passed to the wakeup function
*
* It may be assumed that this function implies a write memory barrier before
* changing the task state if and only if any tasks are woken up.
*/
(三)poll使用流程
1.内核层编写poll相关的函数
unsigned int cdev_poll (struct file * fp, struct poll_table_struct * table)
2.在合适cdev_poll函数中调用poll_wait
poll_wait(fp,&waithead,table);//不会进行阻塞,添加等待队列到轮询列表中
3.在合适的地方调用wake_up_interruptible唤醒poll等待队列
wake_up_interruptible(&waithead)
(四)实例代码
假设要求创建多个设备节点,每个设备节点对应一个设备,在系统调用多次read接口来读取底层设备的状态。例如4个按键 ----- 4个设备节点 ----分别读取4个设备节点文件的数据获取按键的状态-----不确定哪一个按键被按下
分析:
若采用阻塞方式操作:四个读写操作会相互阻塞,如按键1未被按下,则读取不到数据,既是按键4被按下,也会被按键1的操作阻塞,获取不到数据
若采用非阻塞操作:既是读取不到数据,也会直接返回,对于监测按键的状态,按键随时可能被按下,所以需要的是循环检测,采用非阻塞就会导致一直在进行无用功
采用多路io轮询操作:检测是文件描述符是否发生变化,变化就去操作对应的文件,否则阻塞在指定的操作
chrdev.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#define CDEVCOUNT 5
#define CDEVNAME "cdevdevice"
#define INODENAME "mycdev"
int count=0;
dev_t dev=0;
int keyflag;//等待队列第二个参数
char keyvalue[4]={-1,-1,-1,-1};
struct cdev * cdev =NULL;
struct class * cdevclass =NULL;
wait_queue_head_t waithead;
struct Key
{
unsigned int gpios;
char * name;
int num;
unsigned int irq;
};
struct Key key[]={
{EXYNOS4_GPX3(2),"K1",0},
{EXYNOS4_GPX3(3),"K2",1},
{EXYNOS4_GPX3(4),"K3",2},
{EXYNOS4_GPX3(5),"K4",3},
};
irqreturn_t key_handler(int irq, void * dev)
{
struct Key * tmp =(struct Key *)dev;
int value[4];
value[tmp->num]= gpio_get_value(tmp->gpios);
gpio_set_value(EXYNOS4X12_GPM4(tmp->num),value[tmp->num]);
keyflag = 1;
keyvalue[tmp->num] = value[tmp->num];
wake_up_interruptible(&waithead);
return IRQ_HANDLED;
}
int cdev_open (struct inode *node, struct file *file)
{
printk("cdev_open is install\n");
return 0;
}
ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{
int ret =0;
if((fp->f_flags & O_NONBLOCK)== O_NONBLOCK)//非阻塞
{
if(!keyflag)
return -EAGAIN;
}
else//阻塞
{
wait_event_interruptible(waithead,keyflag);
}
keyflag =0;
ret = copy_to_user(buf,keyvalue,4);
if(ret <0)
return -EFAULT;
printk("cdev_read is install\n");
return 0;
}
ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)
{
printk("cdev_write is install\n");
return 0;
}
unsigned int cdev_poll (struct file * fp, struct poll_table_struct * table)
{
unsigned int bitmask =0;
printk("this is poll_wait before\n");
poll_wait(fp,&waithead,table);//不会进行阻塞,添加等待队列到轮询列表中
if(keyflag)//必须有判断标志
bitmask = POLLIN;
printk("this is poll_wait after\n");
return bitmask;
}
int cdev_release (struct inode *node, struct file *fp)
{
printk("cdev_release is install\n");
return 0;
}
struct file_operations fop={
.open=cdev_open,
.read=cdev_read,
.write=cdev_write,
.release=cdev_release,
.poll = cdev_poll,
};
void mycdev_add(void)
{
//1.申请设备号--动态
int ret =alloc_chrdev_region(&dev,0, CDEVCOUNT, CDEVNAME);
if(ret)return ;
//初始化cdev结构体
cdev = cdev_alloc();
if(!cdev)
{
goto out;
}
cdev_init(cdev,&fop);
//添加字符设备到系统中
ret =cdev_add(cdev,dev, CDEVCOUNT);
if(ret)
{
goto out1;
}
//创建设备类
cdevclass = class_create(THIS_MODULE, INODENAME);
if(IS_ERR(cdevclass))
{
goto out2;
}
for (count=0;count<CDEVCOUNT;count++)
device_create(cdevclass, NULL, dev+count, NULL, "mydevice%d",count);
out:
unregister_chrdev_region(dev,CDEVCOUNT);
return ;
out1:
unregister_chrdev_region(dev,CDEVCOUNT);
kfree(cdev);
return ;
out2:
cdev_del(cdev);
unregister_chrdev_region(dev,CDEVCOUNT);
kfree(cdev);
return ;
}
static int __init dev_module_init(void)
{
int ret=0,i=0;
unsigned long flags= IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING|IRQF_SHARED;
for(i=0;i<4;i++)
{
key[i].irq = gpio_to_irq(key[i].gpios);
ret =request_irq(key[i].irq, key_handler,flags,key[i].name,(void *) &key[i]);
}
init_waitqueue_head(&waithead);//创建等待队列头
mycdev_add();
printk("this is dev_module_init \n");
return 0;
}
static void __exit dev_module_cleanup(void)
{
int i=0;
for(i=0;i<4;i++)
{
key[i].irq = gpio_to_irq(key[i].gpios);
free_irq(key[i].irq,(void *)&key[i]);
}
for(count=0;count<CDEVCOUNT;count++)
{
device_destroy(cdevclass, dev+count);
}
class_destroy(cdevclass);
cdev_del(cdev);
unregister_chrdev_region(dev, CDEVCOUNT);
kfree(cdev);
printk("this is dev_module_cleanup\n");
}
module_init(dev_module_init);
module_exit(dev_module_cleanup);
MODULE_LICENSE("GPL");
chr_app.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
int ret=0;
struct pollfd fds[1];
char status[]={-1,-1,-1,-1};
char key[]={-1,-1,-1,-1};
int main(int argc, char *argv[])
{
int i=0,fd= open(argv[1],O_RDWR/*|O_NONBLOCK*/);
if(fd== -1)
{
perror("open");
return -1;
}
while(1)
{
fds[0].fd = fd;
fds[0].events = POLLIN;
ret = poll(fds,1,5000);//只有错误、满足条件、超时才会返回,不满足条件在这死等
if(ret == -1)
{
perror("poll\n");
return -1;
}
else if(ret == 0)
{
printf("timeout\n");
}
else if(ret & POLLIN)
{
printf("ret >0\n");
if(read(fd,status,4)<0)/*疑问:把read屏蔽掉打印出错*/
{
printf("按键状态未改变\n");
sleep(1);
continue;
}
for(i=0;i<4;i++)
{
if(status[i] != key[i] )
{
printf("key%d is %s\n",i,status[i]?"up":"down");
status[i]=key[i];
}
}
}
}
close(fd);
return 0;
}
Makefile
CFLAG =-C
TARGET = chrdev
TARGET1 = chr_app
KERNEL = /mydriver/linux-3.5
obj-m += $(TARGET).o
all:
make $(CFLAG) $(KERNEL) M=$(PWD)
arm-linux-gcc -o $(TARGET1) $(TARGET1).c
clean:
make $(CFLAG) $(KERNEL) M=$(PWD) clean
本文章仅供学习交流用禁止用作商业用途,文中内容来水枂编辑,如需转载请告知,谢谢合作
微信公众号:zhjj0729
微博:文艺to青年