init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);和revents = tfile->f_op->poll(tfile, &epq.pt);这两个函数将ep_ptable_queue_proc注册到epq.pt中的qproc 。
typedef struct poll_table_struct {poll_queue_proc qproc;unsigned long key;}poll_table;
执行f_op->poll(tfile, &epq.pt)时,XXX_poll(tfile, &epq.pt)函数会执行poll_wait(),poll_wait()会调用epq.pt.qproc函数,即ep_ptable_queue_proc 。
更多Linux内核视频教程文档资料免费领取后台私信【内核】自行获取 。
文章插图
内核学习网站:
Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
ep_ptable_queue_proc函数如下:
/*当poll醒来时就回调用该函数,在文件操作中的poll函数中调用,将epoll的回调函数加入到目标文件的唤醒队列中 。如果监视的文件是套接字,参数whead则是sock结构的sk_sleep成员的地址*/static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,poll_table *pt){/*pt获取struct ep_queue的epi字段 。*/struct epitem *epi = ep_item_from_epqueue(pt);struct eppoll_entry *pwq;if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);pwq->whead = whead;pwq->base = epi;add_wait_queue(whead, &pwq->wait);list_add_tail(&pwq->llink, &epi->pwqlist);epi->nwait++;} else {/* We have to signal that an error occurred *//*如果分配内存失败,则将nwait置为-1,表示发生错误,即内存分配失败,或者已发生错误*/epi->nwait = -1;}}
其中struct eppoll_entry定义如下:struct eppoll_entry {struct list_head llink;struct epitem *base;wait_queue_t wait;wait_queue_head_t *whead;};
ep_ptable_queue_proc 函数完成 epitem 加入到特定文件的wait队列任务 。ep_ptable_queue_proc有三个参数:
struct file *file;该fd对应的文件对象wait_queue_head_t *whead;该fd对应的设备等待队列(同select中的mydev->wait_address)poll_table *pt;f_op->poll(tfile, &epq.pt)中的epq.pt
在ep_ptable_queue_proc函数中,引入了另外一个非常重要的数据结构eppoll_entry 。eppoll_entry主要完成epitem和epitem事件发生时的callback(ep_poll_callback)函数之间的关联 。首先将eppoll_entry的whead指向fd的设备等待队列(同select中的wait_address),然后初始化eppoll_entry的base变量指向epitem,最后通过add_wait_queue将epoll_entry挂载到fd的设备等待队列上 。完成这个动作后,epoll_entry已经被挂载到fd的设备等待队列 。由于ep_ptable_queue_proc函数设置了等待队列的ep_poll_callback回调函数 。所以在设备硬件数据到来时,硬件中断处理函数中会唤醒该等待队列上等待的进程时,会调用唤醒函数ep_poll_callback
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);/** If the event mask does not contain any poll(2) event, we consider the* descriptor to be disabled. This condition is likely the effect of the* EPOLLONESHOT bit that disables the descriptor when an event is received,* until the next EPOLL_CTL_MOD will be issued.*/if (!(epi->event.events & ~EP_PRIVATE_BITS))goto out_unlock;.../* If this file is already in the ready list we exit soon */if (ep_is_linked(&epi->rdllink))goto is_linked;/*将该fd加入到epoll监听的就绪链表中*/list_add_tail(&epi->rdllink, &ep->rdllist);is_linked:/** Wake up ( if active ) both the eventpoll wait list and the ->poll()* wait list.*//*唤醒调用epoll_wait()函数时睡眠的进程 。用户层epoll_wait(...) 超时前返回 。*/if (waitqueue_active(&ep->wq))wake_up_locked(&ep->wq);if (waitqueue_active(&ep->poll_wait))pwake++;out_unlock:spin_unlock_irqrestore(&ep->lock, flags);/* We have to call this outside the lock */if (pwake)ep_poll_safewake(&psw, &ep->poll_wait);return 1;}
epoll_waitepoll_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;/* The maximum number of event must be greater than zero */if (maxevents <= 0 || maxevents > EP_MAX_EVENTS)return -EINVAL;/* Verify that the area passed by the user is writeable *//* 检查用户空间传入的events指向的内存是否可写 。参见__range_not_ok() 。*/if (!access_ok(VERIFY_WRITE, events, maxevents * sizeof(struct epoll_event))) {error = -EFAULT;goto error_return;}/* Get the "struct file *" for the eventpoll file *//* 获取epfd对应的eventpoll文件的file实例,file结构是在epoll_create中创建 。*/error = -EBADF;file = fget(epfd);if (!file)goto error_return;/** We have to check that the file structure underneath the fd* the user passed to us _is_ an eventpoll file.*//* 通过检查epfd对应的文件操作是不是eventpoll_fops 来判断epfd是否是一个eventpoll文件 。如果不是则返回EINVAL错误 。*/error = -EINVAL;if (!is_file_epoll(file))goto error_fput;/** At this point it is safe to assume that the "private_data" contains* our own data structure.*/ep = file->private_data;/* Time to fish for events ... */error = ep_poll(ep, events, maxevents, timeout);error_fput:fput(file);error_return:return error;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Linux eBPF解析
- linux 国产操作系统deepin系统从安装到惊喜
- linux服务器垃圾清理记
- 2021 专业人士 Linux 系统 TOP 5
- linux中的makefile编写规则
- Linux服务器打造一个简单的文件共享系统
- 手相判断你的桃花从哪里来
- 求职|简历撰写最新指南
- 让额头变饱满有什么窍门
- Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器