epoll源码分析----2
2016-05-11 17:25
393 查看
来自:http://blog.chinaunix.net/uid-20687780-id-2105157.html
上面的原型是epoll的fd所维护的主结构,下面是每一个具体的fd结构.
以后每一个fd加入到epoll中,就会创建一个struct epitem结构,并插入至红黑树中。
接着是epoll_ctl函数原型:
asmlinkage long sys_epoll_ctl(int epfd,int op,int fd,struct
epoll_event __user *event)
{
int error;
struct file *file,*tfile;
struct eventpoll *ep;
struct epoll_event epds;
error = -FAULT;
//判断行参的合法性
if(ep_op_has_event(op) && copy_from_user(&epds,event,sizeof(struct
epoll_event)))
goto error_return;
error = -EBADF;
file = fget (epfd);
if(!file) goto
error_return;
tfile = fget(fd);
if(!tfile) goto
error_fput;
error = -EPERM;
//不能没有poll驱动
if(!tfile->f_op || !tfile->f_op->poll)
goto error_tgt_fput;
error =-EINVAL;
//防止自己监听自己
if(file == tfile || !is_file_poll(file))
goto error_tgt_fput;
//在create时存入进去的,现在将其拿出来
ep = file->private->data;
mutex_lock(&ep->mtx);
//防止重复添加
epi = epi_find(ep,tfile,fd);
error = -EINVAL;
switch(op)
{
….....
case EPOLL_CTL_ADD:
if(!epi)
{
epds.events |=EPOLLERR | POLLHUP;
error = ep_insert(ep,&epds,tfile,fd);
} else
error = -EEXIST;
break;
…....
}
return error;
}
下面就是插入代码:
static int ep_insert(struct
eventpoll *ep,struct epoll_event *event,
struct file *tfile,int fd)
{
int error ,revents,pwake = 0;
unsigned long flags ;
struct epitem *epi;
/*
struct ep_queue{
poll_table pt;
struct epitem *epi;
}
*/
struct ep_pqueue epq;
//分配一个epitem结构体来保存每个加入的fd
error = -ENOMEM;
if(!(epi = kmem_cache_alloc(epi_cache,GFP_KERNEL)))
goto error_return;
//初始化该结构体
ep_rb_initnode(&epi->rbn);
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
INIT_LIST_HEAD(&epi->pwqlist);
epi->ep = ep;
ep_set_ffd(&epi->ffd,tfile,fd);
epi->event = *event;
epi->nwait = 0;
epi->next = EP_UNACTIVE_PTR;
epq.epi = epi;
//安装poll回调函数
init_poll_funcptr(&epq.pt,ep_ptable_queue_proc);
//调用poll函数来获取当前事件位,其实是利用它来调用注册函数ep_ptable_queue_proc
revents = tfile->f_op->poll(tfile,&epq.pt);
if(epi->nwait < 0)
goto error_unregister;
spin_lock(&tfile->f_ep_lock);
list_add_tail(&epi->fllink,&tfile->f_ep_lilnks);
spin_unlock(&tfile->f_ep_lock);
ep_rbtree_insert(ep,epi);
spin_lock_irqsave(&ep->lock,flags);
if((revents & event->events) && !ep_is_linked(&epi->rdllink))
{
list_add_tail(&epi->rdllink,&ep->rdllist);
if(waitqueue_active(&ep->wq))
__wake_up_locked(&ep->wq,TAKS_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE);
if(waitqueue_active(&ep->poll_wait))
pwake++;
}
spin_unlock_irqrestore(&ep->lock,flags);
if(pwake)
ep_poll_safewake(&psw,&ep->poll_wait);
…....
return 0;
…...
}
//当poll醒来时就回调用该函数
static void ep_ptable_queue_proc(struct file *file,wait_queue_head_t *whead,
poll_table *pt)
{
//从注册时的结构中struct ep_pqueue中获取项epi
struct epitem *epi = ep_item_from_epqueue(pt);
/*//epitem的私有项,通过pwqlist来进行链接
*struct eppoll_entry
{
struct list_head llink;
void *base;
wait_queue_t wait;
wait_queue_head_t *whead;
}
*/
struct eppoll_entry *pwq;//struct
epitem的私有项,为每一个fd保存内核poll
//为每一个等待的结构分配一项
if(epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache,
GFP_KERNEL)))
{
//醒来就调用ep_poll_callback,这里才是真正意义上的poll醒来时的回调函数
init_waitqueue_func_entry(&pwq->wait,ep_poll_callback);
pwq->whead = whead;
pwq->base = epi;
//加入到该驱动的等待队列
add_wait_queue(whead,&pwq->wait);
//将等待链接也放入到epitem链表中去
list_add_tail(&pwq->llink,&epi->pwqlist);
epi->nwait ++;
} else {
epi->nwait = -1;
}
}
//当poll监听的事件到达时,就会调用下面的函数
static int ep_poll_callback(wait_queue_t *wait,unsigned
mode,int sync,void *key)
{
int pwake = 0;
unsigned long flags;
struct epitem *epi = ep_item_from_wait(wait);
struct eventpoll *ep = epi->ep;
spin_lock_irqsave(&ep->lock,flags);
//判断注册的感兴趣事件
//#define
EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)
//有非EPOLLONESHONT或EPOLLET事件
if(!(epi->event.events & ~EP_PRIVATE_BITS))
goto out_unlock;
if(unlikely(ep->ovflist != EP_UNACTIVE_PTR))
{
if(epi->next == EP_UNACTIVE_PTR) {
epi->next = ep->ovflist;
ep->ovflist = epi;
}
goto out_unlock;
}
if(ep_is_linked(&epi->rdllink))
goto is_linked;
//关键是这一句,将该fd加入到epoll监听的就绪链表中
list_add_tail(&epi->rdllink,&ep->rdllist);
is_linked:
if(waitqueue_active(&ep->wq))
__wake_up_locked(&ep->wq,TASK_UNINTERRUPTIBLE
| TASK_INTERRUPTIBLE);
if(waitqueue_active(&ep->poll_wait))
pwake++;
out_unlock:
spin_unlock_irqrestore(&ep->lock,flags);
if(pwake)
ep_poll_safewake(&psw,&ep->poll_wait);
return 1;
}
这里采用了两级回调方式,流程如下:
目前为止,整个数据结构就可以描述如下:
上面的原型是epoll的fd所维护的主结构,下面是每一个具体的fd结构.
以后每一个fd加入到epoll中,就会创建一个struct epitem结构,并插入至红黑树中。
接着是epoll_ctl函数原型:
asmlinkage long sys_epoll_ctl(int epfd,int op,int fd,struct
epoll_event __user *event)
{
int error;
struct file *file,*tfile;
struct eventpoll *ep;
struct epoll_event epds;
error = -FAULT;
//判断行参的合法性
if(ep_op_has_event(op) && copy_from_user(&epds,event,sizeof(struct
epoll_event)))
goto error_return;
error = -EBADF;
file = fget (epfd);
if(!file) goto
error_return;
tfile = fget(fd);
if(!tfile) goto
error_fput;
error = -EPERM;
//不能没有poll驱动
if(!tfile->f_op || !tfile->f_op->poll)
goto error_tgt_fput;
error =-EINVAL;
//防止自己监听自己
if(file == tfile || !is_file_poll(file))
goto error_tgt_fput;
//在create时存入进去的,现在将其拿出来
ep = file->private->data;
mutex_lock(&ep->mtx);
//防止重复添加
epi = epi_find(ep,tfile,fd);
error = -EINVAL;
switch(op)
{
….....
case EPOLL_CTL_ADD:
if(!epi)
{
epds.events |=EPOLLERR | POLLHUP;
error = ep_insert(ep,&epds,tfile,fd);
} else
error = -EEXIST;
break;
…....
}
return error;
}
下面就是插入代码:
static int ep_insert(struct
eventpoll *ep,struct epoll_event *event,
struct file *tfile,int fd)
{
int error ,revents,pwake = 0;
unsigned long flags ;
struct epitem *epi;
/*
struct ep_queue{
poll_table pt;
struct epitem *epi;
}
*/
struct ep_pqueue epq;
//分配一个epitem结构体来保存每个加入的fd
error = -ENOMEM;
if(!(epi = kmem_cache_alloc(epi_cache,GFP_KERNEL)))
goto error_return;
//初始化该结构体
ep_rb_initnode(&epi->rbn);
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
INIT_LIST_HEAD(&epi->pwqlist);
epi->ep = ep;
ep_set_ffd(&epi->ffd,tfile,fd);
epi->event = *event;
epi->nwait = 0;
epi->next = EP_UNACTIVE_PTR;
epq.epi = epi;
//安装poll回调函数
init_poll_funcptr(&epq.pt,ep_ptable_queue_proc);
//调用poll函数来获取当前事件位,其实是利用它来调用注册函数ep_ptable_queue_proc
revents = tfile->f_op->poll(tfile,&epq.pt);
if(epi->nwait < 0)
goto error_unregister;
spin_lock(&tfile->f_ep_lock);
list_add_tail(&epi->fllink,&tfile->f_ep_lilnks);
spin_unlock(&tfile->f_ep_lock);
ep_rbtree_insert(ep,epi);
spin_lock_irqsave(&ep->lock,flags);
if((revents & event->events) && !ep_is_linked(&epi->rdllink))
{
list_add_tail(&epi->rdllink,&ep->rdllist);
if(waitqueue_active(&ep->wq))
__wake_up_locked(&ep->wq,TAKS_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE);
if(waitqueue_active(&ep->poll_wait))
pwake++;
}
spin_unlock_irqrestore(&ep->lock,flags);
if(pwake)
ep_poll_safewake(&psw,&ep->poll_wait);
…....
return 0;
…...
}
//当poll醒来时就回调用该函数
static void ep_ptable_queue_proc(struct file *file,wait_queue_head_t *whead,
poll_table *pt)
{
//从注册时的结构中struct ep_pqueue中获取项epi
struct epitem *epi = ep_item_from_epqueue(pt);
/*//epitem的私有项,通过pwqlist来进行链接
*struct eppoll_entry
{
struct list_head llink;
void *base;
wait_queue_t wait;
wait_queue_head_t *whead;
}
*/
struct eppoll_entry *pwq;//struct
epitem的私有项,为每一个fd保存内核poll
//为每一个等待的结构分配一项
if(epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache,
GFP_KERNEL)))
{
//醒来就调用ep_poll_callback,这里才是真正意义上的poll醒来时的回调函数
init_waitqueue_func_entry(&pwq->wait,ep_poll_callback);
pwq->whead = whead;
pwq->base = epi;
//加入到该驱动的等待队列
add_wait_queue(whead,&pwq->wait);
//将等待链接也放入到epitem链表中去
list_add_tail(&pwq->llink,&epi->pwqlist);
epi->nwait ++;
} else {
epi->nwait = -1;
}
}
//当poll监听的事件到达时,就会调用下面的函数
static int ep_poll_callback(wait_queue_t *wait,unsigned
mode,int sync,void *key)
{
int pwake = 0;
unsigned long flags;
struct epitem *epi = ep_item_from_wait(wait);
struct eventpoll *ep = epi->ep;
spin_lock_irqsave(&ep->lock,flags);
//判断注册的感兴趣事件
//#define
EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)
//有非EPOLLONESHONT或EPOLLET事件
if(!(epi->event.events & ~EP_PRIVATE_BITS))
goto out_unlock;
if(unlikely(ep->ovflist != EP_UNACTIVE_PTR))
{
if(epi->next == EP_UNACTIVE_PTR) {
epi->next = ep->ovflist;
ep->ovflist = epi;
}
goto out_unlock;
}
if(ep_is_linked(&epi->rdllink))
goto is_linked;
//关键是这一句,将该fd加入到epoll监听的就绪链表中
list_add_tail(&epi->rdllink,&ep->rdllist);
is_linked:
if(waitqueue_active(&ep->wq))
__wake_up_locked(&ep->wq,TASK_UNINTERRUPTIBLE
| TASK_INTERRUPTIBLE);
if(waitqueue_active(&ep->poll_wait))
pwake++;
out_unlock:
spin_unlock_irqrestore(&ep->lock,flags);
if(pwake)
ep_poll_safewake(&psw,&ep->poll_wait);
return 1;
}
这里采用了两级回调方式,流程如下:
目前为止,整个数据结构就可以描述如下:
相关文章推荐
- cdn是什么
- Android自定义控件(特效三) 自定义View实现图片验证码
- [原创]java WEB学习笔记01:javaWeb之tomcat的安装和配置
- How to Use Android ADB Command Line Tool on mac
- 【HUSTOJ】1109: 求a+b
- [置顶] 计算器
- Maven学习之18使用jetty:run时jre选择问题
- Sum of Numbers
- 使用Spring进行切面(AOP)编程
- chmod命令详解
- UINavigationBar设置
- 关于Xcode上的Other linker flags
- 用 Python 和 OpenCV 检测图片上的条形码Detecting Barcodes in Images with Python and OpenCV
- java jaxbContext convert bean to xml
- 关于android视频或其他全屏反复切换的问题
- solr占用CPU持续过高原因查询
- C#中使用设置(Settings.settings) Properties.Settings.Default
- C#如何控制方法的执行时间,超时则强制退出方法执行
- epoll源码分析---1
- 【Reproductive Research】