`
sunzixun
  • 浏览: 76037 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

<Linux Kernel>eventepoll3

阅读更多

 

现在开始看看这个大家认为最应该阻塞的函数  从现在开始呢 我会通过源码分析一些

大家对 epoll 模糊的地方

 

SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,

int, maxevents, int, timeout)

{

int error;

struct file *file;

struct eventpoll *ep;

 

/* 这个最大值 你不用担心 你永远也不会有这个多连接 到那个时候不是epoll崩溃 而是你的系统别的地方先崩溃 */

if (maxevents <= 0 || maxevents > EP_MAX_EVENTS)

return -EINVAL;

 

/* 检查入参地址空间有效性 */

if (!access_ok(VERIFY_WRITE, events, maxevents * sizeof(struct epoll_event))) {

error = -EFAULT;

goto error_return;

}

 

/* 得到 传入的file fd对应的struct file  结构*/

error = -EBADF;

file = fget(epfd);

if (!file)

goto error_return;

 

/*

 * 检查 file fd的 file_operations结构是不是epoll ,  还记得anon_inode_getfd 

 */

error = -EINVAL;

if (!is_file_epoll(file))

goto error_fput;

 

/* 把保存的 "全局"struct eventpoll 结构扣出来 */

ep = file->private_data;

 

/* Time to fish for events ... */

error = ep_poll(ep, events, maxevents, timeout);

 

error_fput:

fput(file);

error_return:

 

return error;

}

 

好了现在来看看 ep_poll(ep, events, maxevents, timeout);  这个亲切的函数吧.

 

 

 

 

static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,

   int maxevents, long timeout)

{

int res, eavail;

unsigned long flags;

long jtimeout;

wait_queue_t wait;

 

/*-1 其实就是永久睡眠  */

jtimeout = (timeout < 0 || timeout >= EP_MAX_MSTIMEO) ?

MAX_SCHEDULE_TIMEOUT : (timeout * HZ + 999) / 1000;

 

retry:

/*这里用了全局的spin 来遍历 */

spin_lock_irqsave(&ep->lock, flags);

 

res = 0;

/*还没有就绪事件*/

if (list_empty(&ep->rdllist)) {

/*

 * We don't have any available event to return to the caller.

 * We need to sleep here, and we will be wake up by

 * ep_poll_callback() when events will become available.

 */

/*初始化*/

init_waitqueue_entry(&wait, current);

wait.flags |= WQ_FLAG_EXCLUSIVE;

__add_wait_queue(&ep->wq, &wait);

 

for (;;) {

/*期望接受到 ep_poll_callback() 的终端  告诉我们发生了什么*/

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);

jtimeout = schedule_timeout(jtimeout);

/*回来重新锁上继续傻等*/

spin_lock_irqsave(&ep->lock, flags);

}

/*终于有情况发生了 ,把自己从的等待队列摘下*/

__remove_wait_queue(&ep->wq, &wait);

 

set_current_state(TASK_RUNNING);

/*回头看看到底是不是 就绪队列有事件了 */

}

/* 作者很小心啊 ...趁着归还锁之前看看TMD 到底就绪事件有没有效 */

eavail = !list_empty(&ep->rdllist) || ep->ovflist != EP_UNACTIVE_PTR;

spin_unlock_irqrestore(&ep->lock, flags);

/*没机会了...*/

 

/*回到用户空间的最后一次自检测, */

if (!res && eavail &&

    !(res = ep_send_events(ep, events, maxevents)) && jtimeout)

goto retry;

 

return res;

}

 

 

 

接下来就看看这个 ep_send_events 到底干了什么

 

/*就是初始化了一个 私有的 struct ep_send_events_data, 记住ep_send_events_proc 

*这个回调方法中需要它*/

 

static int ep_send_events(struct eventpoll *ep,

  struct epoll_event __user *events, int maxevents)

{

struct ep_send_events_data esed;

 

esed.maxevents = maxevents;

esed.events = events;

 

return ep_scan_ready_list(ep, ep_send_events_proc, &esed);

 

}

 

/*作者给加的注释的说明这个函数的目的是

扫描 就绪链表(rdlist)  同时去调用 f_op->poll()函数,  要求在O(n)的复杂度内*/

 

static int ep_scan_ready_list(struct eventpoll *ep,
      int (*sproc)(struct eventpoll *,
   struct list_head *, void *),
      void *priv)
{
int error, pwake = 0;
unsigned long flags;
struct epitem *epi, *nepi;
LIST_HEAD(txlist);
 
/*
 * 使用全局互斥锁 防止被 eventpoll_release_file() and epoll_ctl(). 干扰
 */
mutex_lock(&ep->mtx);
 
/*
 * Steal the ready list, and re-init the original one to the
 * empty list. Also, set ep->ovflist to NULL so that events
 * happening while looping w/out locks, are not lost. We cannot
 * have the poll callback to queue directly on ep->rdllist,
 * because we want the "sproc" callback to be able to do it
 * in a lockless way.
 */
spin_lock_irqsave(&ep->lock, flags);
/*用 ep->rdlist 去填充 txlist */
list_splice_init(&ep->rdllist, &txlist);
/*初始化ep->ovflist 为空, 下面就知道原因了*/
ep->ovflist = NULL;
spin_unlock_irqrestore(&ep->lock, flags);
 
/* 这里回调了ep_send_events_proc ,这是一个重要的函数,建议先跳到最后看一下这个函数再回来
 */
error = (*sproc)(ep, &txlist, priv);
 
spin_lock_irqsave(&ep->lock, flags);
/*
 * During the time we spent inside the "sproc" callback, some
 * other events might have been queued by the poll callback.
 * We re-insert them inside the main ready-list here.
 */
/*因为没有屏蔽中断所以在上面的回调过程中 可能发生有新的就绪事件挂到了ovflist链表 */
for (nepi = ep->ovflist; (epi = nepi) != NULL;
     nepi = epi->next, epi->next = EP_UNACTIVE_PTR) {
/*处理一下新的  就绪file fd  ,链接到就绪链表上 */
if (!ep_is_linked(&epi->rdllink))
list_add_tail(&epi->rdllink, &ep->rdllist);
}
/*不需要它了*/
ep->ovflist = EP_UNACTIVE_PTR;
 
/*再次合并 */
list_splice(&txlist, &ep->rdllist);
 
if (!list_empty(&ep->rdllist)) {
/*唤醒 eventpoll (上层) 和 poll (底层) 的等待队列<记得吗, 这个要在归还锁之后哦>*  /
if (waitqueue_active(&ep->wq))
wake_up_locked(&ep->wq);
if (waitqueue_active(&ep->poll_wait))
pwake++;
}
spin_unlock_irqrestore(&ep->lock, flags);
 
mutex_unlock(&ep->mtx);
 
/* We have to call this outside the lock */
if (pwake)
ep_poll_safewake(&ep->poll_wait);
 
return error;
}
 

 

 

 

 

static int ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
       void *priv)

{
struct ep_send_events_data *esed = priv;
int eventcnt;
unsigned int revents;
struct epitem *epi;
struct epoll_event __user *uevent;
 

/*这里的数据结构 很安全 ,还记得 回调是在一个 全局互斥锁的保护下进行的么 */
/*uevent 就是用户空间epoll_wait 传下来的 很庞大的 struct epoll_event  数组
*head 就是上面那个 用rdlist 填充的txlist, 记住返回的eventcnt 数不能大于 用户空间要求的*/
for (eventcnt = 0, uevent = esed->events;
     !list_empty(head) && eventcnt < esed->maxevents;) {
/*获取就绪队列中对应的那个 struct epitem*/
epi = list_first_entry(head, struct epitem, rdllink);
/*处理过了就把它从就绪队列中删除*/
list_del_init(&epi->rdllink);
/*调用它对应的file fd 关联的那个poll , 获取发生了的事件(那些用户关心的)*/
revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) &
epi->event.events;
 
/*把改变了的结构 复制到用户空间*/
if (revents) {
if (__put_user(revents, &uevent->events) ||
    __put_user(epi->event.data, &uevent->data)) {
list_add(&epi->rdllink, head);
return eventcnt ? eventcnt : -EFAULT;
}
eventcnt++;
uevent++;
/*去掉自己用的标志位*/

if (epi->event.events & EPOLLONESHOT)
epi->event.events &= EP_PRIVATE_BITS;
/*注意这里 : 如果没有设置边缘触发 , 就把就绪file fd 写回 就绪链表 等待下一次用户空*间的处理*/
else if (!(epi->event.events & EPOLLET)) {
list_add_tail(&epi->rdllink, &ep->rdllist);

}
}

 

分享到:
评论

相关推荐

    itext 7.rar

    &lt;artifactId&gt;kernel&lt;/artifactId&gt; &lt;version&gt;7.0.4&lt;/version&gt; &lt;/dependency&gt; &lt;dependency&gt; &lt;groupId&gt;com.itextpdf&lt;/groupId&gt; &lt;artifactId&gt;io&lt;/artifactId&gt; &lt;version&gt;7.0.4&lt;/version&gt; &lt;/dependency&gt; ...

    Self.Service.Linux.Mastering.the.Art.of.Problem.Determination

    &lt;br/&gt;如果你想了解如何使用strace, gdb, how to compiling kernel, gdb, kdb等工具解决从应用层服务问题到核心级故障问题的话,成为处理linux问题的专家,这是一个很好的起点。&lt;br/&gt;&lt;br/&gt;If you use Linux ...

    Linux.Certification.in.a.Nutshell.2nd.Edition

    Major topics include:&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;GNU and Unix commands&lt;br&gt;&lt;br&gt;Linux installation and package management&lt;br&gt;&lt;br&gt;Devices, filesystems, and kernel configuration&lt;br&gt;&lt;br&gt;Text editing, processing, and ...

    linux howto中文合集

    html&lt;br&gt;ISP-Connectivity-html&lt;br&gt;ISP-Hookup-HOWTO-html&lt;br&gt;Java-CGI-HOWTO-html&lt;br&gt;Kerneld&lt;br&gt;Kernel-HOWTO-html&lt;br&gt;Large-Disk-html&lt;br&gt;LinuxDoc+Emacs+Ispell-HOWTO-html&lt;br&gt;Linux+DOS+Win95&lt;br&gt;Linux+FreeBSD...

    9500m gs mac os x 显卡 kext

    &lt;key&gt;Kernel&lt;/key&gt; &lt;string&gt;mach_kernel&lt;/string&gt; &lt;key&gt;VBIOS&lt;/key&gt; &lt;string&gt;y&lt;/string&gt; &lt;key&gt;VideoROM&lt;/key&gt; &lt;string&gt;/Extra/nvidia.rom&lt;/string&gt; &lt;/dict&gt; &lt;/plist&gt; 将显卡的bios文件名修改为nvidia.rom,...

    GNU Linux Application Programming

    &lt;br/&gt;&lt;br/&gt;Key Features:&lt;br/&gt;&lt;br/&gt;Focuses on GNU/ Linux, not only the Linux APIs, but the GNU tools and libraries that make Linux programming possible &lt;br/&gt;Covers a variety of useful APIs for process ...

    Linux 可卸载内核模块完全指南

    第一部分 基础知识 &lt;br&gt;1.1 什么是LKMs &lt;br&gt;1.2 什么是系统调用 &lt;br&gt;1.3 什么是内核符号表(Kernel-Symbol-Table) &lt;br&gt;1.4 如何实现从用户空间到内核空间的转换 &lt;br&gt;1.5 使用用户空间函数的方法 &lt;br&gt;1.6 常用内核空间...

    Sams.Ubuntu.Unleashed.Aug.2006.part2

    Part I: Installation and Configuration&lt;br&gt; Chapter 1 Introducing Ubuntu&lt;br&gt; Chapter 2 Preparing to Install Ubuntu&lt;br&gt; Chapter 3 Installing Ubuntu&lt;br&gt; Chapter 4 Post-Installation Configuration&lt;br&gt; ...

    linux双网卡驱动源码(enc28j60).zip

    #include &lt;linux/kernel.h&gt; #include &lt;linux/types.h&gt; #include &lt;linux/fcntl.h&gt; #include &lt;linux/interrupt.h&gt; #include &lt;linux/slab.h&gt; #include &lt;linux/string.h&gt; #include &lt;linux/errno.h&gt; #include &lt;linux/init...

    基于C的firewall

    这是一个简单的基于C语言的防火墙,#include &lt;linux/kernel.h&gt; #include &lt;linux/ip.h&gt; #include &lt;linux/module.h&gt; #include &lt;linux/netdevice.h&gt; #include &lt;linux/netfilter.h&gt; #include &lt;linux/netfilter_ipv4.h&gt; #...

    读写INI文件DEMO

    /// &lt;param name="INIPath"&gt;文件路径&lt;/param&gt; public INIClass(string INIPath) { inipath = INIPath; } /// &lt;summary&gt; /// 写入INI文件 /// &lt;/summary&gt; /// &lt;param name="Section"&gt;项目名称(如 [TypeName]...

    Source code for "µC/OS-II 2.52"

    µC/OS-II can manage up to 255 tasks and provides the following services:&lt;br&gt;&lt;br&gt;Semaphores &lt;br&gt;Mutual Exclusion Semaphores (to reduce priority inversions) &lt;br&gt;Event Flags &lt;br&gt;Message Mailboxes &lt;br&gt;...

    CSharp操作ini文件完美版_附带ini介绍

    /// &lt;param name="retVal"&gt;key所对应的值,如果该key不存在则返回空值&lt;/param&gt; /// &lt;param name="size"&gt;返回值允许的大小&lt;/param&gt; /// &lt;param name="filePath"&gt;INI文件的完整路径和文件名&lt;/param&gt; /// &lt;returns&gt;...

    Understanding the Linux Kernel pdf

    4. 《UnderStanding The Linux Kernel 3rd Edition V413HAV.pdf》:这正是标题中提到的《理解Linux内核》第三版,书中详尽介绍了Linux 2.6.x版本内核的设计和实现,包括调度程序、内存管理、文件系统、网络协议栈等...

    麒麟Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1

    Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1Linux ...

    WinDBG(微软开发的免费源码级调试工具) zkacb汉化版(8.16 update!)

    WinDbg是微软开发的免费源码级调试工具。Windbg可以用于Kernel模式调试和...各取所需吧&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;安装文件下载地址...

    Linux Kernel Development完美版

    ### Linux Kernel Development: 关键知识点概述 #### 一、引言 在《Linux Kernel Development: 完美版》这份文档中,作者Randy Dunlap详细介绍了Linux内核开发的相关知识和实践经验。作为一份全面深入的技术指南,...

    Embedded Linux Primer

    Book Description&lt;br&gt;&lt;br&gt;Comprehensive Real-World Guidance for Every Embedded Developer and Engineer&lt;br&gt;&lt;br&gt;This book brings together indispensable knowledge for building efficient, high-value, Linux-...

    Linux内核设计与实现 Linux Kernel Development 第三版

    ### Linux内核设计与实现——Linux Kernel Development 第三版 #### 书籍简介 《Linux Kernel Development》第三版是一本经典的Linux内核开发指南,由Robert Love编写,Pearson Education出版。该书作为Developer's...

Global site tag (gtag.js) - Google Analytics