Linux的poll与epoll实现(1)---poll

简介:      这是自己第一次写Linux源码的分析,看的多了,写还是第一次,呵呵。      废话不多说,相信看这篇文章的都用过poll和epoll,先来看poll系统调用的源码。 asmlinkage long sys_poll(struct pollfd __user ...
     这是自己第一次写Linux源码的分析,看的多了,写还是第一次,呵呵。
     废话不多说,相信看这篇文章的都用过poll和epoll,先来看poll系统调用的源码。
  1. asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout)
  2. {
  3.     struct poll_wqueues table;
  4.      int fdcount, err;
  5.      unsigned int i;
  6.     struct poll_list *head;
  7.      struct poll_list *walk;

  8.     /* Do a sanity check on nfds ... */
  9.     if (nfds > current->files->max_fdset && nfds > OPEN_MAX)        //(1)
  10.         return -EINVAL;

  11.     if (timeout) {                                                  //(2)
  12.         /* Careful about overflow in the intermediate values */
  13.         if ((unsigned long) timeout MAX_SCHEDULE_TIMEOUT / HZ)
  14.             timeout = (unsigned long)(timeout*HZ+999)/1000+1;
  15.         else /* Negative or overflow */
  16.             timeout = MAX_SCHEDULE_TIMEOUT;
  17.     }

  18.     poll_initwait(&table);                                       //(3)

  19.     head = NULL;
  20.     walk = NULL;
  21.     i = nfds;
  22.     err = -ENOMEM;
  23.     while(i!=0) { //(4)
  24.         struct poll_list *pp;
  25.         pp = kmalloc(sizeof(struct poll_list)+
  26.                 sizeof(struct pollfd)*
  27.                 (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i),
  28.                     GFP_KERNEL);
  29.         if(pp==NULL)
  30.             goto out_fds;
  31.         pp->next=NULL;
  32.         pp->len = (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i);
  33.         if (head == NULL)
  34.             head = pp;
  35.         else
  36.             walk->next = pp;

  37.         walk = pp;
  38.         if (copy_from_user(pp->entries, ufds + nfds-i,
  39.                 sizeof(struct pollfd)*pp->len)) {
  40.             err = -EFAULT;
  41.             goto out_fds;
  42.         }
  43.         i -= pp->len;
  44.     }
  45.     fdcount = do_poll(nfds, head, &table, timeout); (5)

  46.     /* OK, now copy the revents fields back to user space. */
  47.     walk = head;
  48.     err = -EFAULT;
  49.     while(walk != NULL) { (6)
  50.         struct pollfd *fds = walk->entries;
  51.         int j;

  52.         for (j=0; j walk->len; j++, ufds++) {
  53.             if(__put_user(fds[j].revents, &ufds->revents))
  54.                 goto out_fds;
  55.         }
  56.         walk = walk->next;
  57.       }
  58.     err = fdcount;
  59.     if (!fdcount && signal_pending(current))
  60.         err = -EINTR;
  61. out_fds:
  62.     walk = head;
  63.     while(walk!=NULL) {
  64.         struct poll_list *pp = walk->next;
  65.         kfree(walk);
  66.         walk = pp;
  67.     }
  68.     poll_freewait(&table);
  69.     return err;
  70. }

(1)这里是检查文件描述符集的最大个数是否符合要求。
(2)这里检查timeout并对他进行一些处理。
(3)这里的函数初始化类型为poll_wqueues的table,
  1. void poll_initwait(struct poll_wqueues *pwq)
  2. {
  3.     init_poll_funcptr(&pwq->pt, __pollwait);
  4.     pwq->error = 0;
  5.     pwq->table = NULL;
  6. }
可以看到,函数里面初始化了poll_wqueues结构体,我们接着看他有什么内涵。
  1. struct poll_wqueues {
  2.     poll_table pt;
  3.     struct poll_table_page * table;
  4.     int error;
  5. };
每一个poll_wqueue对应每一个poll调用。pt是对外的接口。
接着看poll_table:
  1. typedef struct poll_table_struct {
  2.     poll_queue_proc qproc;
  3. } poll_table;
可以看到,初始化的时候把__pollwait函数赋给poll_table里面的poll队列处理函数(字面翻译)。这个函数在poll_wait函数里面调用,在驱动里面的poll函数就会调用到poll_wait函数。那__pollwait是干什么的呢?这里引用一下别人的图: img_b7de96cec8603e5c774ed99ca4bc38ac.png
这里有人会问,为什么要一个数目为0的数组,那是方便找到邻接着的下一个结构体。
我们回到sys_poll接着往下看。
(4)这个循环的作用就是把要监听的文件描述符从用户态拷到内核态中。注意到因为这里建立了一个链表,如果要监听的描述符很多,超过一页的话,需要分配多个页,可能会影响性能的。
(5)这里有个do_poll函数。看源码:
  1. static int do_poll(unsigned int nfds, struct poll_list *list,
  2.             struct poll_wqueues *wait, long timeout)
  3. {
  4.     int count = 0;
  5.     poll_table* pt = &wait->pt;

  6.     if (!timeout)
  7.         pt = NULL;

  8.     for (;;) {
  9.         struct poll_list *walk;
  10.         set_current_state(TASK_INTERRUPTIBLE);
  11.         walk = list;
  12.         while(walk != NULL) {
  13.             do_pollfd( walk->len, walk->entries, &pt, &count);
  14.             walk = walk->next;
  15.         }
  16.         pt = NULL;
  17.         if (count || !timeout || signal_pending(current))
  18.             break;
  19.         count = wait->error;
  20.         if (count)
  21.             break;
  22.         timeout = schedule_timeout(timeout);
  23.     }
  24.     __set_current_state(TASK_RUNNING);
  25.     return count;
  26. }

do_pollfd就是针对每个传进来的fd,调用它们各自对应的poll函数,简化一下调用过程,如下:
struct file* file = fget(fd);
file->f_op->poll(file, &(table->pt));
“如果fd对应的是某个socket,do_pollfd调用的就是网络设备驱动实现的poll;如果fd对应的是某个ext3文
件系统上的一个打开文件,那do_pollfd调用的就是ext3文件系统驱动实现的poll。一句话,这个file-
>f_op->poll是设备驱动程序实现的,那设备驱动程序的poll实现通常又是什么样子呢?其实,设备驱动
程序的标准实现是:调用poll_wait,即以设备自己的等待队列为参数(通常设备都有自己的等待队列,不
然一个不支持异步操作的设备会让人很郁闷)调用struct poll_table的回调函数。”

(6)就是把结果拷贝回用户态。
相关文章
|
6月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
116 0
|
6月前
|
网络协议 Linux Python
Python网络编程基础(Socket编程)epoll在Linux下的使用
【4月更文挑战第12天】在上一节中,我们介绍了使用`select`模块来实现非阻塞IO的方法。然而,`select`模块在处理大量并发连接时可能会存在性能问题。在Linux系统中,`epoll`机制提供了更高效的IO多路复用方式,能够更好地处理大量并发连接。
|
监控 网络协议 Java
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)(上)
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
251 0
|
18天前
|
存储 JSON Java
细谈 Linux 中的多路复用epoll
大家好,我是 V 哥。`epoll` 是 Linux 中的一种高效多路复用机制,用于处理大量文件描述符(FD)事件。相比 `select` 和 `poll`,`epoll` 具有更高的性能和可扩展性,特别适用于高并发服务器。`epoll` 通过红黑树管理和就绪队列分离事件,实现高效的事件处理。本文介绍了 `epoll` 的核心数据结构、操作接口、触发模式以及优缺点,并通过 Java NIO 的 `Selector` 类展示了如何在高并发场景中使用多路复用。希望对大家有所帮助,欢迎关注威哥爱编程,一起学习进步。
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
24 0
Linux C/C++之IO多路复用(poll,epoll)
|
5月前
|
消息中间件 存储 监控
实战Linux I/O多路复用:借助epoll,单线程高效管理10,000+并发连接
本文介绍了如何使用Linux的I/O多路复用技术`epoll`来高效管理超过10,000个并发连接。`epoll`允许单线程监控大量文件描述符,显著提高了资源利用率。文章详细阐述了`epoll`的几个关键接口,包括`epoll_create`、`epoll_ctl`和`epoll_wait`,以及它们在处理并发连接中的作用。此外,还探讨了`epoll`在高并发TCP服务场景的应用,展示了如何通过`epoll`和线程/协程池来构建服务框架。
622 10
|
4月前
|
Linux 网络安全 虚拟化
Ngnix04系统环境准备-上面软件是免费版的,下面是收费版的,他更快的原因使用了epoll模型,查看当前Linux系统版本, uname -a,VMWARE建议使用NAT,PC端电脑必须使用网线连接
Ngnix04系统环境准备-上面软件是免费版的,下面是收费版的,他更快的原因使用了epoll模型,查看当前Linux系统版本, uname -a,VMWARE建议使用NAT,PC端电脑必须使用网线连接
|
6月前
|
监控 Linux
Linux的epoll用法与数据结构data、event
Linux的epoll用法与数据结构data、event
88 0
|
存储 监控 网络协议
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)(下)
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
221 0
|
6月前
|
监控 网络协议 Linux
Linux I/O多路复用深入解析:从select到epoll的演进之路
Linux I/O多路复用深入解析:从select到epoll的演进之路
451 0