一、是什么?
当多路复用文件数量大 ,IO频繁时候,一般不使用select()和poll()
使用epoll
int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核要监听多个个fd,
当创建好epoll句柄后,它本身占用一个fd值,使用epoll后,需要close掉.
int epoll_create(int size);
int epoll_ctrl(int epfd,int op,int fd,struct epoll_event*event);
第二个参数表示动作:
EPOLL_CTL_ADD:注册新的fd到efd中.
EPOLL_CTL_MOD:修改已经注册的fd监听事件.
EPOLL_CTL_DEL:从epfd删除一个fd.
第三个参数是需要监听的fd.
第四个参数是告诉内核需要监听的事件类型.
struct epoll_event
{
_uint32_t events; // epoll_event
epoll_data data; // user data variable
};
events:或事件
EPOLLIN:文件描述符可读
EPOLLOUT:文件描述符可写
EPOLLPRI:对应的文件描述符可读.
EPOLLERR:对应的文件描述符发生错误.
EPOLHUP:对应描述符被挂断
EPOLLET:将epoll设为边缘触发模式.
EPOLLONESHOT:意味着一次监听,当监听完这次事件之后,如果还需要继续监听,需要
再次把这个fd加入到epoll队列里。
int epoll_wait(int epfd,struct epoll_event*events,int maxevents,int timeout);
timeout参数是ms为单位 0 立即返回 -1 永久等待
该函数返回值需要处理的事件数目,如返回0,表示超时。
设备驱动中轮询编程
设备驱动中poll()函数原型
unsigned int (*poll)(struct file*file,struct poll_table*wait);
file结构体指针
wait表示轮询表指针
将等待队列调用Poll_wait函数,将对应的等待队列头部添加到poll_table
返回表示是否对设备进行无阻塞读/写访问的掩码.
函数原型:
void poll_wait(struct file*filp,wait_queue_head_t*queue,poll_table*wait);
返回设备资源的可获取状态POLLIN POLLOUT POLLPRI POLLERR POLLVAL等宏位的或结果。
static unsigned int xxx_poll(struct file*filp,poll_table*wait)
{
unsigned int mask =0;
struct xxx_dev*dev = filp->private_data;//
poll_wait(filp,&dev->r_wait,wait); //加入读等待队列
poll_wait(filp,&dev->w_wait,wait); //加入写等待队列
if(xxx)
mask |= POLLIN | POLLRNORM; //可读
if(xxx)
mask |= POLLOUT | POLLWRNORM; // 可写
return mask;
}
//通过判断dev->current_len是否等于0表示设备可读状态
//通过判断dev->current_len是否等于GLOBALFIFO_SIZE来获得设备可写状态.
二、使用步骤
1.引入库
代码如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#define FIFO_CLEAR 0x1
#define BUFFER_LEN 20
int main(int argc,char*argv[])
{
int fd,num;
char rd_ch[BUFFER_LEN];
fd_set rfds,wfds; //读/写文件描述符集
//以非阻塞方式打开
fd = open("/dev/global_dev0",O_RDONLY|O_NONBLOCK);
if(fd!=-1)
{
//FIFO清0
if(ioctl(fd,FIFO_CLEAR,0)<0)
{
printf("ioctl command failure\n");
}
while(1)
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd,&rfds);
FD_SET(fd,&wfds);
select(fd+1,&rfds,&wfds,NULL,NULL);
//数据可读
if(FD_ISSET(fd,&rfds))
{
printf("poll monitor can be read\n");
}
if(FD_ISSET(fd,&wfds))
{
printf(" poll monitor:can be write\n");
}
}
}
else
{
printf("Device open failure\n");
}
return 0;
}
poll monitor can be read
poll monitor can be read
poll monitor can be read
poll monitor can be read
同步队列可用于同步驱动中事件发生的先后的顺序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/epoll.h>
#include<strings.h>
#include<sys/epoll.h>
#define FIFO_CLEAR 0x1
#define BUFFER_LEN 20
int main(int argc,char*argv[])
{
int fd;
struct epoll_event ev_globalfifo;
int err;
int epfd;
fd = open("/dev/global_dev0",O_RDONLY|O_NONBLOCK);
if(fd!=-1)
{
if(ioctl(fd,FIFO_CLEAR,0)<0)
{
printf("ioctl command failured\n");
}
epfd = epoll_create(1);
if(epfd<0)
{
perror("epoll_create failure\n");
return;
}
bzero(&ev_globalfifo,sizeof(struct epoll_event));
ev_globalfifo.events = EPOLLIN|EPOLLPRI;
err = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev_globalfifo);
if(err<0)
{
perror("epoll_crtl()");
return;
}
err = epoll_wait(epfd,&ev_globalfifo,1,10000);
if(err<0)
perror("epfd wait\n");
else if(err == 0)
printf("no data input fifo with 10s \n");
else
{
printf("FIFO is not empty");
}
err = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev_globalfifo);
if(err)
perror("epoll_ctrl\n");
}
return 0;
}
root@ubuntu:/nfsroot/system# ./a.out
no data input fifo with 10s
驱动增加poll函数实现
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include<linux/device.h>
#include<linux/wait.h>
#include<linux/poll.h>
static int debug = 1;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debuging infomation");
#define dprintk(args...)\
if(debug){ \
printk(KERN_DEBUG args);\
}
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x01
#define GLOBALMEM_MAJOR 230
#define DEVICE_NUM 1
static int globalmem_major = GLOBALMEM_MAJOR;
module_param(globalmem_major,int,S_IRUGO);
struct globalmem_dev{
struct cdev cdev;
unsigned int current_len;// 表示当前FIFO中有效数据的长度 0 当前FIFO为空,current_len等于GLOBALFIFO_SIZE意味着满
unsigned char mem[GLOBALMEM_SIZE];
struct mutex mutex;
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
};
struct globalmem_dev*globalmem_devp;
static struct class*global_mem_class;
static ssize_t globalmem_read(struct file*filp,char __user*buf,size_t size,
loff_t*ppos)
{
unsigned int count = size;
int ret = 0;
struct globalmem_dev*dev = filp->private_data;
DECLARE_WAITQUEUE(wait,current);
mutex_lock(&dev->mutex);
add_wait_queue(&dev->r_wait,&wait);
while(dev->current_len == 0)
{
if(filp->f_flags&O_NONBLOCK)
{
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
schedule();
if(signal_pending(current)) //信号打断
{
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);
}
if(count > dev->current_len)
{
count = dev->current_len;
}
if(copy_to_user(buf,dev->mem,count))
{
ret = -EFAULT;
goto out;
}
else
{
memcpy(dev->mem,dev->mem+count,dev->current_len-count);
dev->current_len -=count;
printk(KERN_INFO"read %d bytes(s),current_len:%d\n",count,dev->current_len);
wake_up_interruptible(&dev->w_wait);
ret = count;
}
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->r_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
}
static int globalmem_open(struct inode*inode,struct file*filp)
{
//filp->private_data = globalmem_devp;
struct globalmem_dev*dev = container_of(inode->i_cdev,struct globalmem_dev,cdev);
filp->private_data = dev;
return 0;
}
static ssize_t globalmem_write(struct file*filp,const char __user*buf,
size_t size,loff_t*ppos)
{
unsigned int count = size;
int ret = 0;
struct globalmem_dev*dev = filp->private_data;
DECLARE_WAITQUEUE(wait,current);
mutex_lock(&dev->mutex);
add_wait_queue(&dev->w_wait,&wait);
while(dev->current_len == GLOBALMEM_SIZE)
{
if(filp->f_flags&O_NONBLOCK) // f_flags
{
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
schedule();
if(signal_pending(current))
{
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex);
}
if(count > GLOBALMEM_SIZE - dev->current_len)
{
count = GLOBALMEM_SIZE - dev->current_len;
}
if(copy_from_user(dev->mem+dev->current_len,buf,count))
{
ret = -EFAULT;
goto out;
}
else
{
dev->current_len +=count;
printk(KERN_INFO"written %d bytes,corrent_len %d\n",count,dev->current_len);
wake_up_interruptible(&dev->r_wait);
ret = count;
}
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->w_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
}
static long globalmem_ioctl(struct file*filp,unsigned int cmd,unsigned long arg)
{
struct globalmem_dev*dev = filp->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(dev->mem,0,GLOBALMEM_SIZE);
printk(KERN_INFO"globalmem is set to zero\n");
break;
default:
return -EINVAL;
}
return 0;
}
//从文件开头和当前位置的偏移
static loff_t globalmem_llseek(struct file*filp,loff_t offset,int orig)
{
loff_t ret = 0;
switch(orig)
{
case 0:
if(offset<0)
ret = -EINVAL;
break;
if((unsigned int)offset>GLOBALMEM_SIZE){
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int )offset;
ret = filp->f_pos;
break;
case 1:
if((filp->f_pos+offset)>GLOBALMEM_SIZE)
{
ret = -EINVAL;
break;
}
if((filp->f_pos+offset)<0)
{
ret = -EINVAL;
break;
}
filp->f_pos +=offset;
ret=filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static unsigned int globalmem_poll(struct file*filp,poll_table*wait)
{
unsigned int mask = 0;
struct globalmem_dev*dev = filp->private_data;
mutex_lock(&dev->mutex);
poll_wait(filp,&dev->r_wait,wait);
poll_wait(filp,&dev->w_wait,wait);
if(dev->current_len!=0)
{
mask |= POLLIN|POLLRDNORM;
}
if(dev->current_len != GLOBALMEM_SIZE)
{
mask |= POLLOUT|POLLWRNORM;
}
mutex_unlock(&dev->mutex);
return mask;
}
static const struct file_operations globalmem_fops =
{
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.unlocked_ioctl = globalmem_ioctl,
.poll = globalmem_poll,
.open = globalmem_open,
.release = NULL,
};
static void globalmem_setup_cdev(struct globalmem_dev*dev,int index)
{
char buf[20];
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&dev->cdev,&globalmem_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev,devno,1);
if(err)
{
printk(KERN_NOTICE"Error%d adding globalmem_mem%d",err,index);
}
sprintf(buf,"global_dev%d",index); //整形转换字符串
device_create(global_mem_class,NULL,devno,NULL,buf);
}
static int __init globalmem_init(void)
{
int ret;
int i = 0;
dev_t devno = MKDEV(globalmem_major,0);
if(globalmem_major)
ret = register_chrdev_region(devno,DEVICE_NUM,"globalmem");
else
{
ret = alloc_chrdev_region(&devno,0,DEVICE_NUM,"globalmem");
globalmem_major = MAJOR(devno);
}
if(ret<0)
return ret;
globalmem_devp = kzalloc((sizeof(struct globalmem_dev)*DEVICE_NUM),GFP_KERNEL);
if(!globalmem_devp)
{
ret = -ENOMEM;
goto fail_malloc;
}
global_mem_class = class_create(THIS_MODULE,"globalclass");
if(IS_ERR(global_mem_class))
{
printk(KERN_INFO"create class error\n");
return -1;
}
for(i=0;i<DEVICE_NUM;i++)
globalmem_setup_cdev(globalmem_devp+i,i);
mutex_init(&globalmem_devp->mutex);
init_waitqueue_head(&globalmem_devp->r_wait);
init_waitqueue_head(&globalmem_devp->w_wait);
return 0;
fail_malloc:
unregister_chrdev_region(devno,DEVICE_NUM);
return ret;
}
static void __exit globalmem_exit(void)
{
int i = 0;
for(i=0;i<DEVICE_NUM;i++)
cdev_del(&(globalmem_devp+i)->cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major,0),DEVICE_NUM);
class_destroy(global_mem_class);
}
module_init(globalmem_init);
module_exit(globalmem_exit);
MODULE_LICENSE("GPL");