epoll源码分析(三)
2016-05-11 17:26
323 查看
epoll源码分析(三) http://blog.chinaunix.net/uid-20687780-id-2105159.html
分类: LINUX
epoll_wait系统实现如下:
asmlinkage long sys_epoll_wait(int epfd,struct
epoll_event __user *events,
int maxevents,int timeout)
{
int error;
struct file *file;
struct eventpoll *ep;
//#define EP_MAX_EVENTS (INT_MAX / sizeof(struct
epoll_event))
//178956970(1.7亿)
if(maxevents <=0 || maxevents > EP_MAX_EVETNS)
return -EINVAL;
//判断返回事件数组是否合法
if(!access_ok(VERIFY_WRITE,events,
maxevents * sizeof(struct epoll_event)))
{
error = -EFAULT;
goto error_return;
}
error = -EBADF;
file = fget(epfd);
if(!file)
goto error_return;
error = -EINVAL;
if(!is_file_epoll(file))
goto error_fput;
//将epoll注册时设置的数据结构取出来,开始进行判断
ep = file->private_data;
error = ep_poll(ep,events,maxevents,timeout);
….......
}
现在又转入了ep_poll函数中:
static int ep_poll(struct eventpoll *ep,struct
epoll_event __user *events,
int maxevents,long timeout)
{
int res,avail;
unsigned long flags;
long jtimeout;
wait_queue_t wait;
//注册的0ms按0.999 Jiffies处理,并非真正的0s,HZ=100,
//jiffies/HZ 为s
jtimeout = (timeout<0 || timeout >= EP_MAX_MSTIMEO)?
MAX_SCHEDULE_TIMEOUT:(timeout*HZ+999)/1000;
retry:
spin_lock_irqsave(&ep->lock,flags);
res = 0;
//事件就绪队列为空,就监听poll
if(list_empty(&ep->rdllist))
{
//让当前进程挂在等待队列wait上,并将该等待队列加入到ep->wq(epoll_wait的 专属队列中),
init_waitqueue_entry(&wait,current);
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue(&ep->wq,&wait);
for(;;){
//进程设置睡眠状态,等到信息时变唤醒
set_current_state(TASK_INTERRUPTIBLE);
if(!list_empty(&ep->rdllist) || !jtimeout)//只要事件到来,就返回
break;
if(signal_pending(current)) {//被信号中断就会返回
res = -EINTR;
break;
}
spin_unlock_irqrestore(&ep->lock,flags);
//进程进入睡眠状态直到规定的睡眠事件醒来或者注册的fd对应的poll驱动函数唤醒该 进程
jtimeout = schedule_timeout(jtimeout);
spin_lock_irqrestore(&ep->lock,flags);
}
//poll驱动唤醒了该进程,现在就将对应的poll从等待队列中清除出去,并设置为运行状态
__remove_wait_queue(&ep->wq,&wait);
set_current_state(TASK_RUNNING);
}
eavail = !list_empty(&ep->rdllist);
spin_unlock_irqrestore(&ep->lock,flags);
//没有被中断,有就绪事件,并且向用户空间发送成功,就返回
if(!res && eavail && !(res = ep_send_events(ep,events,maxevents))
&&jtimeout)
goto retry;
return res;
}
ep_send_events函数向用户空间发送就绪事件:
static int ep_send_events(struct eventpoll *ep,struct
epoll_event __user *events,int maxevents)
{
int eventcnt,error = -EFAULT,pwake = 0;
unsigned int revents;
unsigned long flags;
struct epitem *epi,*nepi;
struct list_head txlist;
INIT_LIST_HEAD(&txlist);
mutex_lock(&ep->mtx);
spin_lock_irqsave(&ep->lock,flags);
//将ep->rdllist链表加入到txlist链表中去,这样的话rdllist链表就为空了
list_splice(&ep->rdllist,&txlist);
INIT_LIST_HEAD(&ep->rdllist);
ep->ovflist = NULL;
spin_unlock_irqrestore(&ep->lock,flags);
//将rdllist链表中的每一项都发送至用户空间
for(eventcnt = 0; !list_empty(&txlist) && eventcnt < maxevents;) {
epi = list_first_entry(&txlist,struct
epitem,rdllink);
list_del_init(&epi->rdllink);
//立刻返回当前文件的就绪事件
revents = epi->ffd.file->f_op->poll(epi->ffd.file,NULL);
revents &= epi->event.events;
if(revents) {
//将就绪事件的poll_event发送至用户空间
if(__put_user(revents,&events[eventcnt.].events) ||
__put_user(epi->event.data,&events[eventcnt].data))
goto errxit;
//#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)
if(epi->event.events & EPOLLONESHOT)
epi->event.events &= EP_PRIVATE_BITS;
eventcnt++;
}
//非边缘触发,且事件就绪时,就将epi->rdllink加入到rdllist链表中,实际上就是将没有标记为ET模式的fd又放回到rdllist中,这样下次就绪时又能将其发送至用户空间了
if(!(epi->event.events & EPOLLET) && (revents &
epi->event.events))
list_add_tail(&epi->rdllink,&ep->rdllist);
}
error = 0;
errixt:
spin_lock_irqsave(&ep->lock,flags);
//在执行上面的代码期间,又有可能有就绪事件,这样的话就进入了ovflist队列,这样有需要再一次确认一次
for(nepi = ep->ovflist;(epi = nepi)!= NULL;
nepi = epi->next;epi->next = EP_UNACTIVE_PTR) {
//链表为空且没有ET事件发生,#define
EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET),这里也和上面的一样
if(!ep_is_linked(&epi->rdllink) && (epi->event.events &
~EP_PRIVATE_BITS))
//又将rdllink其加入到rdllist中
list_add_tail(&epi->rdllink,&ep->rdllist);
}
//#define EP_UNACTIVE_PTR ((void*) -1L)
ep->ovflist = EP_UNACTIVE_PTR;
list_spice(&txlist,&ep->rdllist);//现在又将txlist链表加入到rdllist链表中去
if(!list_empty(&ep->rdllist))
{
//等待的队列不为空
if(waitqueue_active(&ep->wq))
__wake_up_locked(&ep->wq,TASK_UNINTERRUPTIBLE |
TASK_INTERRUPTIBLE);
//如果poll队列不为空,则唤醒的次数加1
if(waitqueue_active(&ep->poll_wait))
pwake++;
}
spin_unlock_irqrestore(&ep->lock,flags);
mutex_unlock(&ep->mtx);
if(pwake)
ep_poll_safewake(&psw,&ep->poll_wait);
return eventcnt == 0?error:eventcnt;
}
这样epoll_wait的调用顺序为:
参考资料:
linux-2.6.24.3源代码
http://donghao.org/2009/08/linuxiapolliepollaueouaeaeeio.html
分类: LINUX
epoll_wait系统实现如下:
asmlinkage long sys_epoll_wait(int epfd,struct
epoll_event __user *events,
int maxevents,int timeout)
{
int error;
struct file *file;
struct eventpoll *ep;
//#define EP_MAX_EVENTS (INT_MAX / sizeof(struct
epoll_event))
//178956970(1.7亿)
if(maxevents <=0 || maxevents > EP_MAX_EVETNS)
return -EINVAL;
//判断返回事件数组是否合法
if(!access_ok(VERIFY_WRITE,events,
maxevents * sizeof(struct epoll_event)))
{
error = -EFAULT;
goto error_return;
}
error = -EBADF;
file = fget(epfd);
if(!file)
goto error_return;
error = -EINVAL;
if(!is_file_epoll(file))
goto error_fput;
//将epoll注册时设置的数据结构取出来,开始进行判断
ep = file->private_data;
error = ep_poll(ep,events,maxevents,timeout);
….......
}
现在又转入了ep_poll函数中:
static int ep_poll(struct eventpoll *ep,struct
epoll_event __user *events,
int maxevents,long timeout)
{
int res,avail;
unsigned long flags;
long jtimeout;
wait_queue_t wait;
//注册的0ms按0.999 Jiffies处理,并非真正的0s,HZ=100,
//jiffies/HZ 为s
jtimeout = (timeout<0 || timeout >= EP_MAX_MSTIMEO)?
MAX_SCHEDULE_TIMEOUT:(timeout*HZ+999)/1000;
retry:
spin_lock_irqsave(&ep->lock,flags);
res = 0;
//事件就绪队列为空,就监听poll
if(list_empty(&ep->rdllist))
{
//让当前进程挂在等待队列wait上,并将该等待队列加入到ep->wq(epoll_wait的 专属队列中),
init_waitqueue_entry(&wait,current);
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue(&ep->wq,&wait);
for(;;){
//进程设置睡眠状态,等到信息时变唤醒
set_current_state(TASK_INTERRUPTIBLE);
if(!list_empty(&ep->rdllist) || !jtimeout)//只要事件到来,就返回
break;
if(signal_pending(current)) {//被信号中断就会返回
res = -EINTR;
break;
}
spin_unlock_irqrestore(&ep->lock,flags);
//进程进入睡眠状态直到规定的睡眠事件醒来或者注册的fd对应的poll驱动函数唤醒该 进程
jtimeout = schedule_timeout(jtimeout);
spin_lock_irqrestore(&ep->lock,flags);
}
//poll驱动唤醒了该进程,现在就将对应的poll从等待队列中清除出去,并设置为运行状态
__remove_wait_queue(&ep->wq,&wait);
set_current_state(TASK_RUNNING);
}
eavail = !list_empty(&ep->rdllist);
spin_unlock_irqrestore(&ep->lock,flags);
//没有被中断,有就绪事件,并且向用户空间发送成功,就返回
if(!res && eavail && !(res = ep_send_events(ep,events,maxevents))
&&jtimeout)
goto retry;
return res;
}
ep_send_events函数向用户空间发送就绪事件:
static int ep_send_events(struct eventpoll *ep,struct
epoll_event __user *events,int maxevents)
{
int eventcnt,error = -EFAULT,pwake = 0;
unsigned int revents;
unsigned long flags;
struct epitem *epi,*nepi;
struct list_head txlist;
INIT_LIST_HEAD(&txlist);
mutex_lock(&ep->mtx);
spin_lock_irqsave(&ep->lock,flags);
//将ep->rdllist链表加入到txlist链表中去,这样的话rdllist链表就为空了
list_splice(&ep->rdllist,&txlist);
INIT_LIST_HEAD(&ep->rdllist);
ep->ovflist = NULL;
spin_unlock_irqrestore(&ep->lock,flags);
//将rdllist链表中的每一项都发送至用户空间
for(eventcnt = 0; !list_empty(&txlist) && eventcnt < maxevents;) {
epi = list_first_entry(&txlist,struct
epitem,rdllink);
list_del_init(&epi->rdllink);
//立刻返回当前文件的就绪事件
revents = epi->ffd.file->f_op->poll(epi->ffd.file,NULL);
revents &= epi->event.events;
if(revents) {
//将就绪事件的poll_event发送至用户空间
if(__put_user(revents,&events[eventcnt.].events) ||
__put_user(epi->event.data,&events[eventcnt].data))
goto errxit;
//#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)
if(epi->event.events & EPOLLONESHOT)
epi->event.events &= EP_PRIVATE_BITS;
eventcnt++;
}
//非边缘触发,且事件就绪时,就将epi->rdllink加入到rdllist链表中,实际上就是将没有标记为ET模式的fd又放回到rdllist中,这样下次就绪时又能将其发送至用户空间了
if(!(epi->event.events & EPOLLET) && (revents &
epi->event.events))
list_add_tail(&epi->rdllink,&ep->rdllist);
}
error = 0;
errixt:
spin_lock_irqsave(&ep->lock,flags);
//在执行上面的代码期间,又有可能有就绪事件,这样的话就进入了ovflist队列,这样有需要再一次确认一次
for(nepi = ep->ovflist;(epi = nepi)!= NULL;
nepi = epi->next;epi->next = EP_UNACTIVE_PTR) {
//链表为空且没有ET事件发生,#define
EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET),这里也和上面的一样
if(!ep_is_linked(&epi->rdllink) && (epi->event.events &
~EP_PRIVATE_BITS))
//又将rdllink其加入到rdllist中
list_add_tail(&epi->rdllink,&ep->rdllist);
}
//#define EP_UNACTIVE_PTR ((void*) -1L)
ep->ovflist = EP_UNACTIVE_PTR;
list_spice(&txlist,&ep->rdllist);//现在又将txlist链表加入到rdllist链表中去
if(!list_empty(&ep->rdllist))
{
//等待的队列不为空
if(waitqueue_active(&ep->wq))
__wake_up_locked(&ep->wq,TASK_UNINTERRUPTIBLE |
TASK_INTERRUPTIBLE);
//如果poll队列不为空,则唤醒的次数加1
if(waitqueue_active(&ep->poll_wait))
pwake++;
}
spin_unlock_irqrestore(&ep->lock,flags);
mutex_unlock(&ep->mtx);
if(pwake)
ep_poll_safewake(&psw,&ep->poll_wait);
return eventcnt == 0?error:eventcnt;
}
这样epoll_wait的调用顺序为:
参考资料:
linux-2.6.24.3源代码
http://donghao.org/2009/08/linuxiapolliepollaueouaeaeeio.html
相关文章推荐
- 第十二周项目一 阅读程序,请写出这些程序的运行结果(4)
- 如果二级控制器也是包含tabbar
- iCarousel效果
- SAP 创建工厂
- CSS重设(reset)方法总结
- xStream完美转换XML、JSON
- fragment
- Spring Boot + Gradle + Websocket 构建推送服务
- arcgis下载地址
- JDBC批处理---(java 对数据库的回滚) .
- [置顶] 内部类的应用
- Redis键值相关命令
- epoll源码分析----2
- 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选择问题