从内核看Epoll的达成
发布时间:2022-06-21 14:35:55 所属栏目:系统 来源:互联网
导读:epoll是现代服务器的基石,也是高效处理大量请求的利器,从设计上来看,epoll的设计思想也是非常优秀的,本文介绍epoll的实现,从中我们不仅看到epoll的实现原理和机制,同时也能领略到其中优秀的设计思想。 复制 SYSCALL_DEFINE1(epoll_create1, int, flags
epoll是现代服务器的基石,也是高效处理大量请求的利器,从设计上来看,epoll的设计思想也是非常优秀的,本文介绍epoll的实现,从中我们不仅看到epoll的实现原理和机制,同时也能领略到其中优秀的设计思想。 复制 SYSCALL_DEFINE1(epoll_create1, int, flags){ return do_epoll_create(flags); } SYSCALL_DEFINE1(epoll_create, int, size){ if (size <= 0) return -EINVAL; return do_epoll_create(0); } 我们看到epoll_create有两个版本,其中epoll_create1多支持了flags参数,比如设置非阻塞模式,两个API具体的区别不大。接下来我们看do_epoll_create。 复制 static int do_epoll_create(int flags){ int error, fd; struct eventpoll *ep = NULL; struct file *file; // 只支持CLOEXEC if (flags & ~EPOLL_CLOEXEC) return -EINVAL; // 分配一个eventpoll error = ep_alloc(&ep); // 获取一个空闲文件描述符 fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC)); // 获取一个file,并且关联eventpoll_fops和上下文ep file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, O_RDWR | (flags & O_CLOEXEC)); // ep和file关联起来,上面是file和ep关联 ep->file = file; // 关联fd和file fd_install(fd, file); return fd; } 我们看到do_epoll_create的实现非常简单,主要是创建了一个eventpoll结构体,eventpoll结构体比较复杂。下面列出核心的字段。 复制 struct eventpoll { struct mutex mtx; // 阻塞在该epoll的进程队列 wait_queue_head_t wq; // 当epoll被另一个epoll监听时需要使用poll_wait记录阻塞在该epoll的队列 wait_queue_head_t poll_wait; // 就绪队列 struct list_head rdllist; rwlock_t lock; // 红黑树根节点 struct rb_root_cached rbr; // 记录epitem的单链表 struct epitem *ovflist; struct wakeup_source *ws; // 创建该epoll的用户信息 struct user_struct *user; // epoll对应的file struct file *file; }; 创建了一个eventpoll结构体后,接着申请了一个file和fd,并且把file和eventpoll关联起来,主要的作用是调用方后续可以通过fd操作eventpoll,架构如下。 2 epoll_ctl epoll_ctl是操作epoll的总入口,也是非常复杂的开始,但是简单来说就是增删改的接口。 复制 SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event __user *, event){ struct epoll_event epds; // 判断是否需要复制数据,如果是删除则不需要,根据fd删除就行 if (ep_op_has_event(op) && copy_from_user(&epds, event, sizeof(struct epoll_event))) return -EFAULT; return do_epoll_ctl(epfd, op, fd, &epds, false); epoll_ctl是对do_epoll_ctl的封装。 复制 // 操作epoll int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds, bool nonblock){ int error; int full_check = 0; struct fd f, tf; struct eventpoll *ep; struct epitem *epi; struct eventpoll *tep = NULL; error = -EBADF; // 根据fd找到对应的数据结构 f = fdget(epfd); // 获取被操作的文件描述符的数据结构 tf = fdget(fd); error = -EPERM; // 资源有没有实现poll接口,使用epoll监听的资源需要实现poll钩子 if (!file_can_poll(tf.file)) goto error_tgt_fput; error = -EINVAL; // 保证被操作的fd不是自己,并且自己是epoll if (f.file == tf.file || !is_file_epoll(f.file)) goto error_tgt_fput; // 根据fd找到epoll数据结构 ep = f.file->private_data; case EPOLL_CTL_DEL: // 存在则删除,否则报错 if (epi) error = ep_remove(ep, epi); else error = -ENOENT; break; // 修改 case EPOLL_CTL_MOD: // 存在则修改,否则报错 if (epi) { if (!(epi->event.events & EPOLLEXCLUSIVE)) { epds->events |= EPOLLERR | EPOLLHUP; error = ep_modify(ep, epi, epds); } } else error = -ENOENT; break; } return error; } 我们看到do_epoll_ctl主要首先通过两个fd拿到对应的epoll和资源,然后做了一些校验,接着根据操作类型做进一步处理,操作类型有增删改,我们只需要分析插入就行,这是epoll核心。 (编辑:云计算网_泰州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |