加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 服务器 > 系统 > 正文

从内核看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核心。

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读