现在开始看看这个大家认为最应该阻塞的函数 , 从现在开始呢 我会通过源码分析一些
大家对 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); } }
相关推荐
<artifactId>kernel</artifactId> <version>7.0.4</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>io</artifactId> <version>7.0.4</version> </dependency> ...
<br/>如果你想了解如何使用strace, gdb, how to compiling kernel, gdb, kdb等工具解决从应用层服务问题到核心级故障问题的话,成为处理linux问题的专家,这是一个很好的起点。<br/><br/>If you use Linux ...
Major topics include:<br><br><br><br>GNU and Unix commands<br><br>Linux installation and package management<br><br>Devices, filesystems, and kernel configuration<br><br>Text editing, processing, and ...
html<br>ISP-Connectivity-html<br>ISP-Hookup-HOWTO-html<br>Java-CGI-HOWTO-html<br>Kerneld<br>Kernel-HOWTO-html<br>Large-Disk-html<br>LinuxDoc+Emacs+Ispell-HOWTO-html<br>Linux+DOS+Win95<br>Linux+FreeBSD...
<key>Kernel</key> <string>mach_kernel</string> <key>VBIOS</key> <string>y</string> <key>VideoROM</key> <string>/Extra/nvidia.rom</string> </dict> </plist> 将显卡的bios文件名修改为nvidia.rom,...
<br/><br/>Key Features:<br/><br/>Focuses on GNU/ Linux, not only the Linux APIs, but the GNU tools and libraries that make Linux programming possible <br/>Covers a variety of useful APIs for process ...
第一部分 基础知识 <br>1.1 什么是LKMs <br>1.2 什么是系统调用 <br>1.3 什么是内核符号表(Kernel-Symbol-Table) <br>1.4 如何实现从用户空间到内核空间的转换 <br>1.5 使用用户空间函数的方法 <br>1.6 常用内核空间...
Part I: Installation and Configuration<br> Chapter 1 Introducing Ubuntu<br> Chapter 2 Preparing to Install Ubuntu<br> Chapter 3 Installing Ubuntu<br> Chapter 4 Post-Installation Configuration<br> ...
#include <linux/kernel.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/init...
这是一个简单的基于C语言的防火墙,#include <linux/kernel.h> #include <linux/ip.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #...
/// <param name="INIPath">文件路径</param> public INIClass(string INIPath) { inipath = INIPath; } /// <summary> /// 写入INI文件 /// </summary> /// <param name="Section">项目名称(如 [TypeName]...
µC/OS-II can manage up to 255 tasks and provides the following services:<br><br>Semaphores <br>Mutual Exclusion Semaphores (to reduce priority inversions) <br>Event Flags <br>Message Mailboxes <br>...
/// <param name="retVal">key所对应的值,如果该key不存在则返回空值</param> /// <param name="size">返回值允许的大小</param> /// <param name="filePath">INI文件的完整路径和文件名</param> /// <returns>...
4. 《UnderStanding The Linux Kernel 3rd Edition V413HAV.pdf》:这正是标题中提到的《理解Linux内核》第三版,书中详尽介绍了Linux 2.6.x版本内核的设计和实现,包括调度程序、内存管理、文件系统、网络协议栈等...
Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1Linux kernel本地权限提升漏洞(CVE-2022-0847)漏洞补丁及相关依赖包1Linux ...
WinDbg是微软开发的免费源码级调试工具。Windbg可以用于Kernel模式调试和...各取所需吧<br><br><br><br><br><br><br>http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx<br><br><br><br>安装文件下载地址...
### Linux Kernel Development: 关键知识点概述 #### 一、引言 在《Linux Kernel Development: 完美版》这份文档中,作者Randy Dunlap详细介绍了Linux内核开发的相关知识和实践经验。作为一份全面深入的技术指南,...
Book Description<br><br>Comprehensive Real-World Guidance for Every Embedded Developer and Engineer<br><br>This book brings together indispensable knowledge for building efficient, high-value, Linux-...
### Linux内核设计与实现——Linux Kernel Development 第三版 #### 书籍简介 《Linux Kernel Development》第三版是一本经典的Linux内核开发指南,由Robert Love编写,Pearson Education出版。该书作为Developer's...