Linux的poll与epoll实现(2)---epoll_create

简介:     昨晚分析了poll,通过代码的阅读可以发现,poll操作有很多可以优化的地方。epoll是eventpoll的简称,他的效率是非常高的,我们今天来看看他的实现。他的实现在FS/Eventpoll.c,代码有1500多行,呵呵,怕了吧。
    昨晚分析了poll,通过代码的阅读可以发现,poll操作有很多可以优化的地方。epoll是eventpoll的简称,他的效率是非常高的,我们今天来看看他的实现。他的实现在FS/Eventpoll.c,代码有1500多行,呵呵,怕了吧。
    大家都知道,epoll有三个系统调用,C库封装成以下三个:
  1. int epoll_create(int size);
  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  3. int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
epoll的源码这么多,我们就干脆跟着他们三个走着瞧。今天先搞定第一个---epoll_create
 
第一个是
  1. /*
  2.  * It opens an eventpoll file descriptor by suggesting a storage of "size"
  3.  * file descriptors. The size parameter is just an hint about how to size
  4.  * data structures. It won't prevent the user to store more than "size"
  5.  * file descriptors inside the epoll interface. It is the kernel part of
  6.  * the userspace epoll_create(2).
  7.  */
  8. asmlinkage long sys_epoll_create(int size)
  9. {
  10.     int error, fd;
  11.     struct inode *inode;
  12.     struct file *file;

  13.     DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n",
  14.          current, size));

  15.     /* Sanity check on the size parameter */
  16.     error = -EINVAL;
  17.     if (size = 0)
  18.         goto eexit_1;

  19.     /*
  20.      * Creates all the items needed to setup an eventpoll file. That is,
  21.      * a file structure, and inode and a free file descriptor.
  22.      */
  23.     error = ep_getfd(&fd, &inode, &file);                  //(1)
  24.     if (error)
  25.         goto eexit_1;

  26.     /* Setup the file internal data structure ( "struct eventpoll" ) */
  27.     error = ep_file_init(file);                            //(2)
  28.     if (error)
  29.         goto eexit_2;


  30.     DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n",
  31.          current, size, fd));

  32.     return fd;

  33. eexit_2:
  34.     sys_close(fd);
  35. eexit_1:
  36.     DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n",
  37.          current, size, error));
  38.     return error;
  39. }

(1)这里用到了一个ep_getfd函数,从注释我们知道,这个函数建立eventpoll相关的file,当然,一个file要包括文件描述符、inode、还有文件对象,这也是我们传的三个参数。废话不说,看源码:

  1. /*
  2.  * Creates the file descriptor to be used by the epoll interface.
  3.  */
  4. static int ep_getfd(int *efd, struct inode **einode, struct file **efile)
  5. {
  6.     struct qstr this;
  7.     char name[32];
  8.     struct dentry *dentry;
  9.     struct inode *inode;
  10.     struct file *file;
  11.     int error, fd;

  12.     /* Get an ready to use file */
  13.     error = -ENFILE;
  14.     file = get_empty_filp();
  15.     if (!file)
  16.         goto eexit_1;

  17.     /* Allocates an inode from the eventpoll file system */
  18.     inode = ep_eventpoll_inode();
  19.     error = PTR_ERR(inode);
  20.     if (IS_ERR(inode))
  21.         goto eexit_2;

  22.     /* Allocates a free descriptor to plug the file onto */
  23.     error = get_unused_fd();
  24.     if (error 0)
  25.         goto eexit_3;
  26.     fd = error;

  27.     /*
  28.      * Link the inode to a directory entry by creating a unique name
  29.      * using the inode number.
  30.      */
  31.     error = -ENOMEM;
  32.     sprintf(name, "[%lu]", inode->i_ino);
  33.     this.name = name;
  34.     this.len = strlen(name);
  35.     this.hash = inode->i_ino;
  36.     dentry = d_alloc(eventpoll_mnt->mnt_sb->s_root, &this);
  37.     if (!dentry)
  38.         goto eexit_4;
  39.     dentry->d_op = &eventpollfs_dentry_operations;
  40.     d_add(dentry, inode);
  41.     file->f_vfsmnt = mntget(eventpoll_mnt);
  42.     file->f_dentry = dentry;
  43.     file->f_mapping = inode->i_mapping;

  44.     file->f_pos = 0;
  45.     file->f_flags = O_RDONLY;
  46.     file->f_op = &eventpoll_fops;
  47.     file->f_mode = FMODE_READ;
  48.     file->f_version = 0;
  49.     file->private_data = NULL;

  50.     /* Install the new setup file into the allocated fd. */
  51.     fd_install(fd, file);

  52.     *efd = fd;
  53.     *einode = inode;
  54.     *efile = file;
  55.     return 0;

  56. eexit_4:
  57.     put_unused_fd(fd);
  58. eexit_3:
  59.     iput(inode);
  60. eexit_2:
  61.     put_filp(file);
  62. eexit_1:
  63.     return error;
  64. }

这个函数的注释都比较全,这里简单提一下,况且因为涉及到的函数太多,要深究起来涉及的知识太多,也不可能逐一去列代码。不过这个函数个人觉得比较经典,这函数就是创建一个文件的流程。

首先,我们得拿到一个file结构体,通过内核分配给我们;然后我们要拿到inode,调用这个ep_eventpoll_inode()就可以了;接着是get_unused_fd()拿到文件描述符;接着d_alloc()函数为我们拿到一个dentry;d_add(dentry, inode)函数把dentry建立hash里面并且绑定inode;后面是继续填充文件对象file;fd_install(fd, file)向进程注册文件,并通过这样的方式把文件描述符和文件对象关联起来。

(2)在跟踪ep_file_init函数之前,我们先来看一下eventpoll结构体:

  1. /*
  2.  * This structure is stored inside the "private_data" member of the file
  3.  * structure and rapresent the main data sructure for the eventpoll
  4.  * interface.
  5.  */
  6. struct eventpoll {
  7.     /* Protect the this structure access */
  8.     rwlock_t lock;

  9.     /*
  10.      * This semaphore is used to ensure that files are not removed
  11.      * while epoll is using them. This is read-held during the event
  12.      * collection loop and it is write-held during the file cleanup
  13.      * path, the epoll file exit code and the ctl operations.
  14.      */
  15.     struct rw_semaphore sem;

  16.     /* Wait queue used by sys_epoll_wait() */
  17.     wait_queue_head_t wq;

  18.     /* Wait queue used by file->poll() */
  19.     wait_queue_head_t poll_wait;

  20.     /* List of ready file descriptors */
  21.     struct list_head rdllist;

  22.     /* RB-Tree root used to store monitored fd structs */
  23.     struct rb_root rbr;
  24. };

注释也是相当清楚。这个eventpoll可以看得出来,是epoll的核心,它将会存储你想要监听的文件描述符,这也是为什么epoll高效之所在。

好,我们回到sys_epoll_create函数,开始跟踪ep_file_init函数:

  1. static int ep_file_init(struct file *file)
  2. {
  3.     struct eventpoll *ep;

  4.     if (!(ep = kmalloc(sizeof(struct eventpoll), GFP_KERNEL)))
  5.         return -ENOMEM;

  6.     memset(ep, 0, sizeof(*ep));
  7.     rwlock_init(&ep->lock);
  8.     init_rwsem(&ep->sem);
  9.     init_waitqueue_head(&ep->wq);
  10.     init_waitqueue_head(&ep->poll_wait);
  11.     INIT_LIST_HEAD(&ep->rdllist);
  12.     ep->rbr = RB_ROOT;

  13.     file->private_data = ep;

  14.     DNPRINTK(3, (KERN_INFO "[%p] eventpoll: ep_file_init() ep=%p\n",
  15.          current, ep));
  16.     return 0;
  17. }

其实也就是eventpoll结构体的初始化。

sys_epoll_create函数大概就这样了,明天接着看sys_epoll_ctl。

目录
相关文章
|
6天前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
64 0
|
6天前
|
网络协议 Linux Python
Python网络编程基础(Socket编程)epoll在Linux下的使用
【4月更文挑战第12天】在上一节中,我们介绍了使用`select`模块来实现非阻塞IO的方法。然而,`select`模块在处理大量并发连接时可能会存在性能问题。在Linux系统中,`epoll`机制提供了更高效的IO多路复用方式,能够更好地处理大量并发连接。
|
6天前
|
监控 Linux
Linux的epoll用法与数据结构data、event
Linux的epoll用法与数据结构data、event
6 0
|
6天前
|
监控 网络协议 Linux
Linux I/O多路复用深入解析:从select到epoll的演进之路
Linux I/O多路复用深入解析:从select到epoll的演进之路
78 0
|
6天前
|
NoSQL Java Linux
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
74 0
|
6天前
|
网络协议 Linux
【Linux C TCP服务器端-epoll案例】
本文主要介绍了linux下Select的TCP通信流程,实现了客户端和服务器的通信,主要实现了消息的回发,即服务器将消息原封不动的回发给客户端。
39 0
|
6天前
|
网络协议 Linux
【Linux C TCP服务器端-poll案例】
本文主要介绍了linux下Select的TCP通信流程,实现了客户端和服务器的通信,主要实现了消息的回发,即服务器将消息原封不动的回发给客户端。
26 0
|
6天前
|
网络协议 Linux API
Linux C TCP编程(socket,select/poll/epoll)
本文主要介绍了linux下标准的TCP通信流程,实现了客户端和服务器的通信,主要实现了消息的回发,即服务器将消息原封不动的回发给客户端。如果对接口不熟悉可以参考socket api介绍或者参考其他博客。
28 0
|
6天前
|
Linux
Linux网络编程(epoll的ET模式和LT模式)
Linux网络编程(epoll的ET模式和LT模式)
54 0
|
6天前
|
存储 Linux
Linux网络编程(epoll函数的使用)
Linux网络编程(epoll函数的使用)
52 0