内核poll和select详解

合集下载

linux中select、poll、epoll原理 -回复

linux中select、poll、epoll原理 -回复

linux中select、poll、epoll原理-回复Linux中的select、poll和epoll是用于实现I/O多路复用的机制。

在传统的同步I/O模型中,一个线程只能处理一个I/O操作,无法同时处理多个I/O操作。

而利用这些I/O多路复用的机制,可以同时监听多个文件描述符的I/O事件,从而实现一个线程同时处理多个I/O操作的能力。

首先,我们从select机制开始介绍。

select是最早出现的一种I/O多路复用的机制。

它的原理是通过调用系统调用select,将用户感兴趣的文件描述符和事件类型传递给内核,然后让内核来检测文件描述符上是否发生了用户感兴趣的事件。

select的函数原型如下:cint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set*exceptfds, struct timeval *timeout);参数nfds表示文件描述符的数量,readfds、writefds和exceptfds分别用于传递用户感兴趣的读、写和异常事件的文件描述符集合。

timeout 是超时时间,用于设定select函数的等待时间。

select的原理是将用户传递的文件描述符集合拷贝到内核中,然后在内核中遍历这些文件描述符,检测是否有感兴趣的事件发生。

具体的伪代码如下:1. 将用户传递的文件描述符集合拷贝到内核中;2. 在内核中遍历这些文件描述符,检测是否有读、写或异常事件发生;3. 将发生事件的文件描述符添加到返回的文件描述符集合中。

select机制的优点是简单易用,适用于处理并发连接数不大的情况。

但是它也有一些缺点。

首先,select将用户感兴趣的文件描述符集合传递给内核,内核需要遍历这些文件描述符,如果文件描述符数量很大,会导致内核遍历的时间开销较大。

其次,select对文件描述符集合的管理采用的是线性查找的方式,当文件描述符数量较大时,效率较低。

linux中select、poll、epoll原理 -回复

linux中select、poll、epoll原理 -回复

linux中select、poll、epoll原理-回复在Linux中,select、poll和epoll是一些用于实现异步I/O和事件驱动的机制。

它们允许开发人员监控多个文件描述符的状态,以便及时处理I/O 事件而不会导致线程阻塞。

本文将详细介绍select、poll和epoll的原理及其实现方式。

一、selectselect是最早引入的一种多路复用机制,它的原理是通过将文件描述符的状态从用户空间传递到内核空间,然后内核根据文件描述符的状态变化来通知用户进程。

select模型中,需要将所有要监听的文件描述符放入一个描述符集中,然后调用select函数并传入描述符集,它会阻塞进程直到有文件描述符状态变化。

当select返回后,可以通过遍历描述符集来找到状态发生变化的文件描述符。

select存在一些限制。

首先,它使用的是一个位图来表示描述符集,因此最大能监听的文件描述符数量有限制。

其次,每次调用select,都需要将整个描述符集从用户空间复制到内核空间,这会造成一定的开销。

最后,select对每个描述符都是轮询,无法告知哪些描述符状态发生了变化,需要进程自己遍历整个描述符集来找到变化的描述符。

二、pollpoll是select的一种改进,主要解决了select中描述符数量限制和轮询开销等问题。

poll的原理和select类似,不同之处在于poll使用了一个结构体数组来表示描述符集,而不是位图。

这样就没有了描述符数量的限制,可以更灵活地监听多个文件描述符。

此外,poll函数返回时可以遍历整个结构体数组,找到状态发生变化的描述符,避免了轮询的开销。

与select相比,poll仍然存在一些问题。

首先,每次调用poll函数,仍然需要将整个结构体数组从用户空间复制到内核空间,这依然会造成开销。

其次,poll无法告知有哪些描述符状态发生了变化,需要进程自己遍历整个结构体数组来找到变化的描述符。

三、epollepoll是Linux内核2.6引入的一种高效的多路复用机制。

select poll epoll原理

select poll epoll原理

select poll epoll原理一、概述select、poll、epoll 都是常见的 I/O 多路复用机制,实现高效的 I/O 操作,提高程序的性能。

这三种机制都是通过在一个线程中处理多个文件描述符的 I/O 事件实现高并发。

本文将介绍 select、poll、epoll 的原理。

二、selectselect 是最早的 I/O 多路复用机制之一,提出于1983 年。

select 的基本原理是将一些文件描述符添加到一个 fd_set 集合中,通过 select 函数等待这些 fd_set 集合中的文件描述符中有一个或多个就绪,然后在这些文件描述符上执行 I/O 操作。

select 函数可以设置一个超时时间,当超时时间到达后若 fd_set 集合中没有就绪的文件描述符,则返回 0。

select 的缺点是效率低下。

在处理大量文件描述符时会采用轮询的方式,每次遍历所有文件描述符查看是否有就绪的 I/O 事件,需要大量的 CPU 资源,并且 select 的 fd_set 集合最大长度受到系统宏 FD_SETSIZE 的限制,通常不超过 1024。

三、pollpoll 函数是对 select 函数的一次改进,也是 I/O 多路复用机制中比较常见的一种。

poll 的基本原理是将一些文件描述符添加到一个 pollfd 数组中,通过 poll 函数等待这些数组中的文件描述符中有一个或多个就绪,然后在这些文件描述符上执行 I/O 操作。

与 select 不同的是,poll 数组不受数量限制,我们可以根据需要动态调整数组长度。

poll 函数也可以设置超时时间,当超时时间到达后若 pollfd 数组中没有就绪的文件描述符,则返回0。

poll 的缺点是仍然存在效率问题。

poll 在处理大量文件描述符时,每次都需要遍历整个 pollfd 数组,查看是否有就绪的 I/O 事件。

四、epollepoll 是 Linux 下的一种 I/O 多路复用机制,也是效率最高的一种。

聊聊IO多路复用之select、poll、epoll详解

聊聊IO多路复用之select、poll、epoll详解

聊聊IO多路复⽤之select、poll、epoll详解IO多路复⽤是指内核⼀旦发现进程指定的⼀个或者多个IO条件准备读取,它就通知该进程。

IO多路复⽤适⽤如下场合:1. 当客户处理多个描述符时(⼀般是交互式输⼊和⽹络套接⼝),必须使⽤I/O复⽤。

2. 当⼀个客户同时处理多个套接⼝时,⽽这种情况是可能的,但很少出现。

3. 如果⼀个TCP服务器既要处理监听套接⼝,⼜要处理已连接套接⼝,⼀般也要⽤到I/O复⽤。

4. 如果⼀个服务器即要处理TCP,⼜要处理UDP,⼀般要使⽤I/O复⽤。

5. 如果⼀个服务器要处理多个服务或多个协议,⼀般要使⽤I/O复⽤。

与多进程和多线程技术相⽐,I/O多路复⽤技术的最⼤优势是系统开销⼩,系统不必创建进程/线程,也不必维护这些进程/线程,从⽽⼤⼤减⼩了系统的开销。

⽬前⽀持I/O多路复⽤的系统调⽤有 select,pselect,poll,epoll,I/O多路复⽤就是通过⼀种机制,⼀个进程可以监视多个描述符,⼀旦某个描述符就绪(⼀般是读就绪或者写就绪),能够通知程序进⾏相应的读写操作。

但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后⾃⼰负责进⾏读写,也就是说这个读写过程是阻塞的,⽽异步I/O则⽆需⾃⼰负责进⾏读写,异步I/O的实现会负责把数据从内核拷贝到⽤户空间。

对于IO多路复⽤机制不理解的同学,可以先⾏参考《》,来了解Linux五种IO模型。

1 select、poll、epoll简介epoll跟select都能提供多路I/O复⽤的解决⽅案。

在现在的Linux内核⾥有都能够⽀持,其中epoll是Linux所特有,⽽select则应该是POSIX所规定,⼀般操作系统均有实现。

1.1 select基本原理:select 函数监视的⽂件描述符分3类,分别是writefds、readfds、和exceptfds。

调⽤后select函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有except),或者超时(timeout指定等待时间,如果⽴即返回设为null即可),函数返回。

详细解读Linux内核的poll机制

详细解读Linux内核的poll机制

详细解读Linux内核的poll机制详细解读Linux内核的poll机制所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数。

比如系统调用open、read、write、poll,与之对应的内核函数为:sys_open、sys_read、sys_write、sys_poll。

一、内核框架:对于系统调用poll或select,它们对应的内核函数都是sys_poll。

分析sys_poll,即可理解poll机制。

1. sys_poll函数位于fs/select.c文件中,代码如下:asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, long timeout_msecs){ s64 TImeout_jiffies;if (timeout_msecs > 0) {#if HZ > 1000 /* We can only overflow if HZ > 1000 */ if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ) TImeout_jiffies = -1; else#endif TImeout_jiffies = msecs_to_jiffies(TImeout_msecs); } else { /* Infinite (ait(…… fdcount = do_poll(nfds, head, ……}poll_initwait函数非常简单,它初始化一个poll_wqueues变量table:poll_initwait > init_poll_funcptr( > pt->qproc = qproc;即table->pt->qproc = __pollwait,__pollwait将在驱动的poll函数里用到。

select,poll,epoll的内部机制调研

select,poll,epoll的内部机制调研

1 等待队列实现原理1.1功能介绍进程有多种状态,当进程做好准备后,它就处于就绪状态(TASK_RUNNING),放入运行队列,等待内核调度器来调度。

当然,同一时刻可能有多个进程进入就绪状态,但是却可能只有1个CPU是空闲的,所以最后能不能在CPU上运行,还要取决于优先级等多种因素。

当进程进行外部设备的IO等待操作时,由于外部设备的操作速度一般是非常慢的,所以进程会从就绪状态变为等待状态(休眠),进入等待队列,把CPU让给其它进程。

直到IO操作完成,内核“唤醒”等待的进程,于是进程再度从等待状态变为就绪状态。

在用户态,进程进行IO操作时,可以有多种处理方式,如阻塞式IO,非阻塞式IO,多路复用(select/poll/epoll),AIO(aio_read/aio_write)等等。

这些操作在内核态都要用到等待队列。

1.2 相关的结构体typedef struct __wait_queue wait_queue_t;struct __wait_queue {unsigned int flags;#define WQ_FLAG_EXCLUSIVE 0x01struct task_struct * task;wait_queue_func_t func;struct list_head task_list;};这个是等待队列的节点,其中task表示等待队列节点对应的进程。

func表示等待队列的回调函数,在进程被唤醒。

在很多等待队列里,这个func函数指针默认为空函数。

但是,在select/poll/epoll函数中,这个func函数指针不为空,并且扮演着重要的角色。

struct __wait_queue_head {spinlock_t lock;struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;这个是等待队列的头部。

select,poll,epoll

select,poll,epoll因为select、poll、epoll都是IO复用模型的解决方案,它们最终的目标都是为了解决单个应用进程(从应用的角度来看这里也可以理解为单个线程,从系统的角度来看这里面向的就是应用进程)能同时处理多个网络连接的问题,如果不了解IO复用模型,建议先看下这篇。

从宏观上如果系统要对外提供一个进程可以监控多个连接的方法的话,那么实现这个方法需要考虑的问题主要是下面几条,而select、poll、epoll 他们的不同之处也都是围绕着这几点展开的:1、系统如何知道进程需要监控哪些连接和事件(也就是fd)。

2、系统知道进程需要监控的连接和事件后,采用什么方式去对fd进行状态的监控。

3、系统监控到活跃事件后如何通知进程。

应用进程想要通过select 去监控多个连接(也就是fd)的话需要经向大概如下的流程:1、在调用select之前告诉select 应用进程需要监控哪些fd可读、可写、异常事件,这些分别都存在一个fd_set数组中。

2、然后应用进程调用select的时候把3个fd_set传给内核(这里也就产生了一次fd_set在用户空间到内核空间的复制),内核收到fd_set后对fd_set进行遍历,然后一个个去扫描对应fd是否满足可读写事件。

3、如果发现了有对应的fd有读写事件后,内核会把fd_set里没有事件状态的fd句柄清除,然后把有事件的fd返回给应用进程(这里又会把fd_set从内核空间复制用户空间)。

4、最后应用进程收到了select返回的活跃事件类型的fd句柄后,再向对应的fd发起数据读取或者写入数据操作。

通过上面的图我想你已经大概了解了select的工作模式,select 提供一种可以用一个进程监控多个网络连接的方式,但也还遗留了一些问题,这些问题也是后来select面对高并发环境的性能瓶颈。

1、每调用一次select 就需要3个事件类型的fd_set需从用户空间拷贝到内核空间去,返回时select也会把保留了活跃事件的fd_set返回(从内核拷贝到用户空间)。

linux中select、poll、epoll原理

linux中select、poll、epoll原理select、poll和epoll是Linux下常用的I/O多路复用技术,都用于实现高效的事件驱动型的网络编程。

1. select(选择)select是最古老的I/O多路复用机制,它通过在套接字上设置阻塞(阻塞方式)进行等待,一旦有文件描述符准备就绪(可读、可写等),则返回。

select使用fd_set集合来保存要监听的文件描述符,因此其监听的文件描述符数量受到系统给定的FD_SETSIZE限制。

select的实现原理是:在内核中创建一个称为“等待队列”的数据结构(fd_set),该队列保存了需要等待的文件描述符,当某个文件描述符就绪时,会通过和用户进程的映射表通知用户进程。

select通过轮询所有注册的文件描述符,检查哪些文件描述符已经准备好,并将准备好的文件描述符从用户态拷贝到内核态。

select的缺点是每次调用都需要轮询全部的注册文件描述符,效率较低。

2. poll(轮询)poll是在select的基础上进行改进的多路复用技术。

poll与select的最大区别在于,它没有限制文件描述符的数量,并且使用了一个pollfd结构体数组来保存每个文件描述符及其关注的事件。

poll在内核中创建一个称为“等待队列”的数据结构,该队列保存了需要等待的文件描述符,当某个文件描述符就绪时,会通过和用户进程的映射表通知用户进程。

poll的实现原理是:将用户进程注册要监听的文件描述符及其关注的事件存储在内核中的一个事件表中,当发生事件时,内核会将该事件存储在内核态的事件表中,并通知用户进程。

与select不同的是,poll只需在事件发生时拷贝某些信息到内核态,而不需要拷贝全部的文件描述符。

poll的缺点是,当注册的文件描述符数量较大时,每次调用poll都需要遍历整个事件表,效率较低。

3. epoll(事件通知)epoll是Linux特有的一种I/O多路复用机制,通过内核与用户空间的共享内存来实现高效的事件通知。

1、select、poll和epoll的优缺点

1、select、poll和epoll的优缺点1.select:select本质上是通过设置或者检查存放fd标志位的数据结构数据结构来进⾏下⼀步的处理,时间复杂度:O(n) 缺点: 1)、每次调⽤select,都需要把fd集合从⽤户态拷贝到内核态,这个开销在fd很多时会很⼤; 2)、同时每次调⽤select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很⼤; 3)、单个进程可监视的fd数量被限制; 4)、对socket进⾏扫描是线性扫描;优点: 1)、select的可移植性更好,在某些Unix系统上不⽀持poll()。

2)、select对于超时值提供了更好的精度:微秒,⽽poll是毫秒。

2.poll:poll本质上和select没有区别,它将⽤户传⼊的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待对垒中加⼊⼀项继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,知道设备就绪或者主动超时,被唤醒后它⼜要再次遍历fd这个过程经理了多次⽆谓的遍历。

时间复杂度O(n)缺点: 1)、⼤量的fd的数组被整体复制于⽤户态和内核地址空间之间,⽽不管这样的复制是不是有意义; 2)、与select⼀样,poll返回后,需要轮询pollfd来获取就绪的描述符。

优点: 1)、poll() 不要求开发者计算最⼤⽂件描述符加⼀的⼤⼩。

2)、poll() 在应付⼤数⽬的⽂件描述符的时候速度更快,相⽐于select。

3)、它没有最⼤连接数的限制,原因是它是基于链表来存储的。

3.epoll:epoll可以理解为event poll,不同于忙轮询和⽆差别轮询,epoll会把哪个流发⽣了怎样的I/O事件通知我们。

所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。

(复杂度降低到了O(1))缺点: 1)、相对select来说, epoll的跨平台性不够⽤只能⼯作在linux下, ⽽select可以在windows linux apple上使⽤。

linux下select 和 poll的用法

linux下select 和poll的用法select()函数的作用系统调用select和poll的后端实现,用这两个系统调用来查询设备是否可读写,或是否处于某种状态。

如果poll为空,则驱动设备会被认为即可读又可写,返回值是一个状态掩码如何使用select()函数?select()函数的接口主要是建立在一种叫'fd_set'类型的基础上。

它('fd_set') 是一组文件描述符(fd)的集合。

由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量:fd_set set;FD_ZERO(&set); /* 将set清零*/FD_SET(fd, &set); /* 将fd加入set */FD_CLR(fd, &set); /* 将fd从set中清除*/FD_ISSET(fd, &set); /* 如果fd在set中则真*/在过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。

*这个值是系统相关的*,同时检查你的系统中的select() 的man手册。

有一些系统对多于1024个文件描述符的支持有问题。

[译者注:Linux就是这样的系统!你会发现sizeof(fd_set)的结果是128(*8 = FD_SETSIZE=1024)尽管很少你会遇到这种情况。

]select的基本接口十分简单:int select(int nfds, fd_set *readset, fd_set *writeset,fd_set *exceptset, struct timeval *timeout);其中:nfds需要检查的文件描述符个数,数值应该比是三组fd_set中最大数更大,而不是实际文件描述符的总数。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

poll(轮询)操作在应用程序中用于同时阻塞在多个文件上,当其中任何一个文件有应用程序所等待的事件(可读、可写、出错等)时,poll返回相应的掩码通知应用程序,使得应用程序知道应该对哪个文件做何种操作。

按照我的理解,poll的本质可以这样解释:休眠等待多个指定文件中的任何一个发生特定的事件,并将被该文件唤醒;醒来后轮询所有相关文件(通过再次调用所有文件对应驱动的poll方法),获取所有被监控文件的事件信息返回给应用程序。

从这里就可以看出:
(1)其中等待队列的使用是必不可少的。

实际上调用poll的进程将会休眠在多个等待队列(一般所有被监控文件的都有至少一个的等待队列)上,从其中任何一个队列上唤醒该进程,都可能使poll函数返回。

(2)驱动中的poll方法不实现休眠,而是:
1i、把当前进程添加到相应的等待队列中(仅在休眠时执行,唤醒时不会执行此功能)。

2ii、返回文件当前的状态掩码(告知是否有事件发生,休眠和唤醒都会执行)。

通过对内核源码、《深入Linux设备驱动程序内核机制》的学习,我对Poll系统调用和内核驱动的poll方法的关系和结构有了整体且深入的了解,基本搞清了poll系统调用的执行脉络。

对于poll系统调用的内核原理,请大家先看《深入Linux设备驱动程序内核机制》那本书写的比较详细了,我不废话了。

以后我会把我自己觉得需要注意的地方写出来。

这里我把这个关系和数据结构图绘制了出来,请大家指正:
对于等待队列的情况,我用下面一个例子和图来示意一下:
例如有3个进程:
task-1:使用poll检测文件1~3
task-2:使用poll检测文件2~3
task-3:使用poll检测文件3
则等待队列的情况如下:
之后,假设task-2由于文件2或3被唤醒,且task-1/3对此不感兴趣(未设置该掩码),那么等待队列的情况如下:
等待队列入口项的添加和删除主要是由poll_initwait(&table);和poll_freewait(&table);完成。

poll_initwait(&table);完成初始化struct poll_wqueues table的工作,而poll_freewait(&table);负责清理这个结构体。

这里需要注意的是等待队列中的wait_queue_t并不是在唤醒函数pollwake从队列中删除的,而是最后由poll_freewait(&table);集中处理的。

而唤醒函数和普通的wait_event的唤醒函数有很大不同,请大家对比上面的图和之前我写的《对Linux系统休眠的理解》中的图。

相关文档
最新文档