Linux中epoll()函数的底层实现
来源:互联网 发布:网络用语1是什么意思 编辑:程序博客网 时间:2024/05/29 15:30
epoll()是由epoll_create()、epoll_ctl()、epoll_wait()三个系统调用实现。
epoll_create()
epoll_create()调用内核函数sys_epoll_create(),在参数检查后,sys_epoll_create()中,最为重要的两个内核函数为ep_getfd()、ep_file_init();
1.在 ep_getfd()中为epoll事件分配file、inode、以及文件描述符fd;
file = get_empty_filp(); //在该函数中初始化一系列文件结构以及参数检查 //f = kmem_cache_alloc(filp_cachep, GFP_KERNEL);//分配空间 //eventpoll_init_file(f); inode = ep_eventpoll_inode(); //分配节点空间以及初始化 //struct inode *inode = new_inode(eventpoll_mnt->mnt_sb); error = get_unused_fd(); //分配文件描述符fd fd = error;
2.分配dentry结构体的空间并利用中间结构体qstr将其初始化,将file结构体初始化,并将当前进程与file、dentry、inode联系在一起;
sprintf(name, "[%lu]", inode->i_ino); this.name = name; this.len = strlen(name); this.hash = inode->i_ino; //通过中间结构体将inode与dentry联系在一起 dentry = d_alloc(eventpoll_mnt->mnt_sb->s_root, &this); dentry->d_op = &eventpollfs_dentry_operations; d_add(dentry, inode); //初始化file结构体 file->f_vfsmnt = mntget(eventpoll_mnt); file->f_dentry = dentry; //file与dentry联系在一起 file->f_mapping = inode->i_mapping; file->f_pos = 0; file->f_flags = O_RDONLY; file->f_op = &eventpoll_fops; file->f_mode = FMODE_READ; file->f_version = 0; file->private_data = NULL; fd_install(fd, file); //files->fd[fd] = file; //将file插入到当前进程PCB监测文件结构体中
3.在ep_file_init(),分配eventpoll结构体的空间并初始化,将file与该结构体联系起来。
if (!(ep = kmalloc(sizeof(struct eventpoll), GFP_KERNEL))) return -ENOMEM; memset(ep, 0, sizeof(*ep)); rwlock_init(&ep->lock); init_rwsem(&ep->sem); init_waitqueue_head(&ep->wq); init_waitqueue_head(&ep->poll_wait); INIT_LIST_HEAD(&ep->rdllist); ep->rbr = RB_ROOT; file->private_data = ep;
epoll_ctl()
epoll_ctl()调用内核函数sys_epoll_ctl(),在参数检查后,通过file结构体找到eventpoll结构体,再从其中的红黑树根节点遍历寻找新的sockfd是否已存在,最后通过判断操作OP,确定是否添加、删除或者更改。
1.获取到eventpoll结构体,然后寻找sockfd是否已经存在;
ep = file->private_data; epi = ep_find(ep, tfile, fd);
2.判断OP操作是什么并作出相应处理;
switch (op) { case EPOLL_CTL_ADD: if (!epi) { epds.events |= POLLERR | POLLHUP; 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; }
3.在该函数中最为重要的是ep_insert()函数,在ep_insert()中在申请epitem节点并初始化后,为此监听的sockfd在设备驱动中注册了一个回调函数,并将此节点插至eventpoll的红黑树中以便查找监听。
EP_RB_INITNODE(&epi->rbn); INIT_LIST_HEAD(&epi->rdllink); INIT_LIST_HEAD(&epi->fllink); INIT_LIST_HEAD(&epi->txlink); INIT_LIST_HEAD(&epi->pwqlist); epi->ep = ep; EP_SET_FFD(&epi->ffd, tfile, fd); epi->event = *event; atomic_set(&epi->usecnt, 1); epi->nwait = 0; //将epitem初始化 epq.epi = epi; init_poll_funcptr(&epq.pt, ep_ptable_queue_proc); //ep_pqueue结构体中有一个指向epitem的指针,还有一个回调函数 //在此注册第一个回调函数 revents = tfile->f_op->poll(tfile, &epq.pt); //调用第一个回调函数,并在其中注册第二个回调函数ep_poll_callback //以下皆为将初始化好的epitem挂入监听的结构中 list_add_tail(&epi->fllink, &tfile->f_ep_links); ep_rbtree_insert(ep, epi); if ((revents & event->events) && !EP_IS_LINKED(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); if (waitqueue_active(&ep->wq)) wake_up(&ep->wq); if (waitqueue_active(&ep->poll_wait)) pwake++; }
epoll_wait()
- epoll_wait()调用内核函数sys_epoll_wait(),在参数检查后,该函数大部分功能由ep_poll()完成,在ep_poll()中,会不断的检查eventpoll中rdllist是否为空,如果为空就一直处于浅舒眠,只要检测到rdllist不为空,就通过txlist发送给用户。
1.循环判断rdllist是否为空,为空睡眠,不空发送给用户;
retry: if (list_empty(&ep->rdllist) { init_waitqueue_entry(&wait, current); add_wait_queue(&ep->wq, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); //rdllist不空,break跳出循环,而不空的条件是有事件时设备驱动调用回调函数将事件加入rdllist if (!list_empty(&ep->rdllist) || !jtimeout) break; if (signal_pending(current)) { res = -EINTR; break; } write_unlock_irqrestore(&ep->lock, flags); jtimeout = schedule_timeout(jtimeout); write_lock_irqsave(&ep->lock, flags); } remove_wait_queue(&ep->wq, &wait); set_current_state(TASK_RUNNING); } //此中ep_events_transfer()实现向用户传送数据,将txlist作为中间链实现ET和LT //如果ET,将rdllist断链并链入txlist中由txlist发送给用户 //如果LT,会将事件重复加入rdlliat中再次通知用户 if (!res && eavail &&!(res = ep_events_transfer(ep, events, maxevents)) && jtimeout) goto retry;
epoll()实现至此,在剖析源码过程中,其实还有若干未解,如:
if (EP_OP_HASH_EVENT(op) && copy_from_user(&epds, event, sizeof(struct epoll_event))) goto eexit_1;
该判断,前半句为判断OP事件是否为删除,如不是删除返回值为真,可执行从用户态拷贝数据到内核态,但是在if条件判断后,执行的操作为退出?
此篇博客还有诸多不完善之处,如有错误和完善意见,麻烦大家发送至xiongduoru@163.com,欢迎大家指正~
阅读全文
0 0
- Linux中epoll()函数的底层实现
- Linux中fork()函数的底层实现
- epoll底层实现过程
- epoll底层实现过程
- epoll 底层实现源码分析
- C语言中字符串函数的使用底层实现方法
- linux kernel中epoll的设计和实现
- linux kernel中epoll的设计和实现
- linux route的底层实现
- epoll底层实现原理(通俗易懂)
- 底层实现的字符串操作函数
- C++虚函数的底层实现
- C++虚函数的底层实现原理
- C++虚函数的底层实现原理
- 虚函数的底层实现机制
- linux c++ epoll的简单实现
- linux之----------epoll函数
- 在C++中类的对象作为函数形参在底层的实现机制
- Effective Modern C++ Item 1
- js e2e测试-nightwatch入门
- 设计模式六大原则
- 前端基础面试题,菜鸟必备
- EventBus3.0 使用
- Linux中epoll()函数的底层实现
- ViewPager的高度根据item的高度自适应
- Android--快速接入微信支付
- vs2015的程序在vs2013上运行出现:error MSB8020
- elasticsearch.net search使用指南
- mysql忘记root用户密码如何进行重置
- android4.4 编译SDK与使用
- iOS开发进阶
- 网络请求踩坑多