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

<Linux Kernel>eventepoll2

阅读更多

 

下面来看看 epoll 的操作函数  epoll_ctl 

 

SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
struct epoll_event __user *, event)
{
int error;
struct file *file, *tfile;
struct eventpoll *ep;
struct epitem *epi;
struct epoll_event epds;

error = -EFAULT;
   /*很简单  如果有事件过来了, 首先把用户空间的  struct epoll_event结构保存到内核空间*/
if (ep_op_has_event(op) &&
    copy_from_user(&epds, event, sizeof(struct epoll_event)))
goto error_return;

/* Get the "struct file *" for the eventpoll file */
error = -EBADF;
/*把当前这个 epfd 对应的epoll 从当前进程描述符的fdtable里面给摘出来 :用rcu 扣出来并且增加引用*/
file = fget(epfd);
if (!file)
goto error_return;
/*好吧, 再把你需要操作的那个  file fd也给扣出来 */

tfile = fget(fd);
if (!tfile)
goto error_fput;

/* The target file descriptor must support poll */
error = -EPERM;
/*该检查检查驱动到底支持 poll 不, 要知道epoll 就是 EnhancePoll 。。日*/
if (!tfile->f_op || !tfile->f_op->poll)
goto error_tgt_fput;

/*
 * We have to check that the file structure underneath the file descriptor
 * the user passed to us _is_ an eventpoll file. And also we do not permit
 * adding an epoll file descriptor inside itself.
 */
/*这个说得很明白 ,意思也是想防止死锁。 但是很糟糕,在11 年3月份的kernel 邮件列表里 还是提到了一个bug , 这个后面会说*/
error = -EINVAL;
if (file == tfile || !is_file_epoll(file))
goto error_tgt_fput;

/*
 * 好了,终于说召回了 我们的epoll_event
 */
ep = file->private_data;

    /*召回了就用 ,加锁 干活!*/
mutex_lock(&ep->mtx);

/*这里就开始肆无忌惮的到 (以 ep->rbr.rb_node 指向为根节点的)那棵RBTree里面找到这个要操作的fd对应的那个位置结构  struct epitem. 
 */
epi = ep_find(ep, tfile, fd);

error = -EINVAL;
switch (op) {
case EPOLL_CTL_ADD:
if (!epi) {
/*这里可以看到, 如果之前没有的话 增加会导致一个 POLLERR | POLLHUP事件*/

epds.events |= POLLERR | POLLHUP;/
/*插入 很多位置: struct epitem. ,这个后面会讲。下面的操作就很容易明白了*/
error = ep_insert(ep, &epds, tfile, fd);
} else
error = -EEXIST;
break;
case EPOLL_CTL_DEL:
if (epi)
error = ep_remove(ep, epi);
else
error = -ENOENT;
break;
case EPOLL_CTL_MOD:
if (epi) {
epds.events |= POLLERR | POLLHUP;
error = ep_modify(ep, epi, &epds);
} else
error = -ENOENT;
break;
}
mutex_unlock(&ep->mtx);
/*驱动不支持poll 的话  就要归还内核被召回的struct file 结构 2个*/
error_tgt_fput:
fput(tfile);
/*这里仅仅归还 eventpoll , fput操作其实做了很多事情他不仅仅是减少了struct file 引用计数 ,这个和file_table 的清理机制相关, 后面专门用一文讲吧
error_fput:dany
fput(file);
error_return:

return error;
}
 

好了,下面来看有趣的的 ep_insert

 

 

在看ep_inert之前 ,我们先来回忆一下 poll 

 

我们都知道在linux VFS模型中 struct file_operations 有一个 伟大的 poll 函数指针

unsigned int (*poll) (struct file *, struct poll_table_struct *);

 

举个例子 就看比较常见的  scsi 

 

 

static unsigned int tgt_poll(struct file * file, struct poll_table_struct *wait)

{

//....

/*tgt_poll_wait 就是该设备的等待队列,  首先要知道 poll_wait 不会阻塞 

*然后其实就是去调用 struct poll_table  注册的 qproc 函数指针*/

 

poll_wait(file, &tgt_poll_wait, wait);

 

spin_lock_irqsave(&ring->tr_lock, flags);

 

idx = ring->tr_idx ? ring->tr_idx - 1 : TGT_MAX_EVENTS - 1;

ev = tgt_head_event(ring, idx);

if (ev->hdr.status)/*设置上层用的标志位*/

mask |= POLLIN | POLLRDNORM;

 

spin_unlock_irqrestore(&ring->tr_lock, flags);

 

return mask;

}

其实poll 就干了一件事 ,就是返回当前file fd的状态 .  select 比较傻,他把集合里面所有位都遍历里了一遍(虽然也就 __FD_SETSIZE) ACE 提供了几种解决方法,其中比较好的就是ACE_Select_Reactor  这个以后的分析好像用到的方法引用了一篇论文 (ACE 其实就是很多论文的实现...)

 

好 下面就开始将比较有趣的ep_insert 

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_pqueue epq;
    /*给当前的 epoll fd增加一个修改者计数 当然不能也不会很多*/
if (unlikely(atomic_read(&ep->user->epoll_watches) >=
     max_user_watches))
return -ENOSPC;
   /*分配一个新的 struct epitem  位置结构 ,给这个要插入的file fd*/
if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
return -ENOMEM;

/* 初始化 就绪链表节点; 要挂到struct file 结构 epoll链表的节点; 要连接到   */
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
INIT_LIST_HEAD(&epi->pwqlist);
epi->ep = ep;/*保留相互引用*/
    /*初始化 struct epoll_filefd  当他可以用RBTree的节点*/
ep_set_ffd(&epi->ffd, tfile, fd);
    /*指向用户传入的 epoll_event 内核拷贝*/
epi->event = *event;
epi->nwait = 0;
epi->next = EP_UNACTIVE_PTR;

/* Initialize the poll table using the queue callback */
epq.epi = epi;

/*从上面的poll 回顾应该看出 , 这里其实是对 poll_wait 里面的回调函数进行注册,
*ep_ptable_queue_proc 这个关键函数 , 下面专门分析*/
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);

/*
 * Attach the item to the poll hooks and get current event bits.
 * We can safely use the file* here because its usage count has
 * been increased by the caller of this function. Note that after
 * this operation completes, the poll callback can start hitting
 * the new item.
 */
/*这里干了一件很有趣的事情 :  把不仅仅把刚才注册的函数立刻调用, 让poll_wait 的操作立刻生效 ,还能返回当前的状态 , */
revents = tfile->f_op->poll(tfile, &epq.pt);

/*看看是不是在poll_wait 回调的安装方法中出现问题, 假设是资源问题*/
error = -ENOMEM;
if (epi->nwait < 0)
goto error_unregister;

/* 记得用file fd自己的锁, 把 struct epitm 结构加到file 自己的epoll 监控队列里面*/
spin_lock(&tfile->f_lock);
list_add_tail(&epi->fllink, &tfile->f_ep_links);
spin_unlock(&tfile->f_lock);

/*插入到RBTree中*/
ep_rbtree_insert(ep, epi);

/* We have to drop the new item inside our item list to keep track of it */
spin_lock_irqsave(&ep->lock, flags);
/*如果当前的stuct file 已经 有需要的事件发生了 , 可 epi 还没有被连接到ready链表 */
/* If the file is already "ready" we drop it inside the ready list */
if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
/*把它加到 "全局"的 eventpoll结构中-> 就绪file fd链表*/
list_add_tail(&epi->rdllink, &ep->rdllist);
/*唤醒 eventpoll (上层) 和 poll (底层) 的等待队列 后面还会看到类似的地方
*几乎在每次 解中断恢复自选锁之前都会去检查一下 , 比如ep_insert ep_modify scan_ready_list等*/

if (waitqueue_active(&ep->wq))
wake_up_locked(&ep->wq);
if (waitqueue_active(&ep->poll_wait))
pwake++;
}

spin_unlock_irqrestore(&ep->lock, flags);

atomic_inc(&ep->user->epoll_watches);

/* 安全的(使用了递归计数)唤醒底层的poll 队列 */
if (pwake)
ep_poll_safewake(&ep->poll_wait);

return 0;
/*做一些清理工作 */

error_unregister:
ep_unregister_pollwait(ep, epi);
spin_lock_irqsave(&ep->lock, flags);
if (ep_is_linked(&epi->rdllink))
list_del_init(&epi->rdllink);
spin_unlock_irqrestore(&ep->lock, flags);

kmem_cache_free(epi_cache, epi);

return error;
}



好了  ,现在来看看
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
里面的 ep_ptable_queue_proc  也就是 poll_wait 的实际操作
说之前 我们先来看一个 重要的数据结构
对于每一个加入到epoll 检测集合里面的file fd 都会有一个这样的 entry 项 ,
了解他的重要性了吧
struct eppoll_entry {
/* 指向 struct epitem 结构中的 poll 等待队列头 */
struct list_head llink;

/* 红星闪闪向太阳 不解释... */
struct epitem *base;

/*用来链贴到 底层等待队列的项*/
wait_queue_t wait;

/* 指向上面说的那个队列*/
wait_queue_head_t *whead;
};

static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,
 poll_table *pt)
{
/*找到包含这个 poll_table 的 ep_pqueue 结构 , 然后利用它 去指向 ep_pqueue中包含的另一个 结构 就是位置结构体: struct epitem *epi;*/
struct epitem *epi = ep_item_from_epqueue(pt);
/*下面主要就是初始化一下 eppoll_entry 这个结构, 然后把它贴到内核需要检测的队列和链表上*/
struct eppoll_entry *pwq;
/*从内核的 slab 系统中分配到一个 eppoll_entry 结构体*/
if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
/*初始化struct eppoll_entry 中的等待队列, 并给它注册一个 ep_poll_callback */
init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
pwq->whead = whead;

pwq->base = epi;
/*加入到当前 底层需要检测的whead 等待队列中*/
add_wait_queue(whead, &pwq->wait);

/*把这个  struct eppoll_entry  pwq  给 贴到 struct epitem epi 的pwqlist 链表末尾 , 
增加要检测的 entry 数目*/
list_add_tail(&pwq->llink, &epi->pwqlist);
epi->nwait++;
} else {
/* We have to signal that an error occurred */
epi->nwait = -1;
}
}
 

 

<!--EndFragment-->

分享到:
评论

相关推荐

    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...

    Understanding the Linux Kernel pdf

    2. 《Professional Linux Kernel Architecture》:作者Wolfgang M. Rehm详细阐述了Linux内核的架构和实现,包括进程管理、内存管理、I/O子系统、设备驱动等关键模块,是学习Linux内核的另一本权威参考书。 3. ...

    基于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;...

    麒麟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内核开发的相关知识和实践经验。作为一份全面深入的技术指南,...

    Linux Kernel Development 2nd ed.

    The Linux kernel is one of the most interesting yet least understood open-source projects. It is also a basis for developing new kernel code. That is why Sams is excited to bring you the latest 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-...

Global site tag (gtag.js) - Google Analytics