IO多路复用--epoll

epoll就是为了 处理大批量句柄而改进的poll,相比与select,poll最大的好处在于它不会随着坚挺fd的数目增长而效率降低。因为在内核中的select是采用轮询来处理的,轮询fd的数目越多,自然耗时越多,并且slelct的监听数目有限(虽然可以通过头文件来改变,但并不治本)

一.epoll的相关系统调用

epoll只有三个简单地接口 分别为epoll_creat,epoll_ctl,epoll_wait

(1)int epoll_creat(int size)

创建一个epoll句柄,当创建好一个epoll句柄后,它就会占一个 fd值,所以在使用完以后要调用close()函数关闭,否则fd可能被耗尽

(2)int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event)

epoll的事件注册函数,它不同于 select函数在监听事件的时候告诉内核要监听什么类型的事件,而是 在 这里先注册要监听的事件类型

第一个参数是epoll_creat的返回值

第二个参数表示动作,用三个宏来表示

EPOLL_CTL_ADD:注册新的fd到epfd中

EPOLL_CTL_MOD;修改已经注册到的fd的监听事件

EPOLL_CTL_DEL:从epfd中删除一个fd

第三个参数 是 要监听 的fd

第四个参数 告诉内核 需要监听什么 事件

event可以是以下几个 宏的集合

EPOLLIN:表示 对应的文件描述符可以读(包括对socket正常关闭)

EPOLLOUT:表示 对应的文件描述符可以写

EPOLLPRI:表示对应的文件描述符有紧急的数据可读

EPOLLERR:对应的文件描述符发生错误

EPOLLET:将EPOLL设置为 边沿除法模式 ,这是相对于水平触发而言

EPOLLONESET:只监听一次事件,如果之后还需要监听的话,需要再次把这个socket键入到EPOLL队列里

(3)int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout)

收集epoll监控的事件中已经发生的事件参数event是分配好的epoll_event结构体数组。epoll把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到数组中,但不会帮用户开辟内存),maxevents告诉这个内核有多大这个maxevents的值不能大于创建的epoll_ctreat()时的size,阐述timeout是超时事件(毫秒 ,0立即返回,-1将不确定,也就是永久阻塞)如果函数调用成功,返回对应已准备好的文件描述符数目,如果返回0表示超时

二. epoll工作原理

epoll只告诉那些就绪的文件描述符,而且当你调用epoll_wait()获得就绪文件描述符的时候,返回的不是实际的文件描述符,而是一个代表就绪描述符数量值,你只是需要去epoll指定的一个数组 中一次获得相应数量的文件描述符即可。

另一个本质的改进在于epoll采用基于 事件的就绪通知方式,在select/poll中进程只有在调用一定的方法之后,内核才对所有监视的文件描述符进行扫描,而epoll先通过采用epoll_ctl()来注册一个文件描述符,当进程调用epoll_wait()时便得到通知。

三. epoll的工作方式-水平触发(LT)-边沿触发(ET)

LT(level triggered)是epoll缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

ET (edge-triggered)是高速工作方式,只支持no-block socket,它效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。

因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应

四.epoll的优点

(1)支持一个进程打开大数目的socket描述符(FD)

select所能够打开的fd是有限的一般为1024,对于那些支持上万条链接数目的服务器来说 太少了,epoll就没有这个限制 ,它所支持的FD上限是最大可以打开文件数目这个树种子一般大于2048;

(2)IO效率不随FD的数目 增加而线性下降

传统的select/poll致命弱点是当拥有一个很大的集合的时候,每次都用都会轮询扫描 集合 ,导致效率下降,但是epoll只对活跃 的socket进行操作--因为在内核中epoll根据每个fd上面的callback函数实现的,只有活跃的函数 才会 调用 callback函数

五.linux下epoll如何处理百万句柄

(1)首先调用epoll_creat建立一个epoll对象,参数 size是内核保证能够正确处理的最大句柄数

(2)epoll_ctl操作上面建立好的epoll,例如让刚建立的socket加入到epoll中让其监控,或者把epoll正在监控的某个socke句柄移除epoll

(3)epoll_wait在给定的timeout时间内当监控的所有句柄发审核变化时就返回用户态进程(毫秒 ,0立即返回,-1将不确定,也就是永久阻塞)如果函数调用成功,返回对应已准备好的文件描述符数目,如果返回0表示超时

从上面调用方式可以看出epoll比select/poll的优越之处:因为 后者每次调用时都会给你返回所要监控所有socket给select/poll系统 调用 ,这意味着需要将用户态的socket列表 拷贝到内核态,如果数以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效,但epoll_wait不用传递socket句柄给内核,因为内核已经在 epoll_ctl中拿到了要监控的句柄列表

下面是代码部分

1.创建监听套接字,设置端口服用127.0.0.1 指定簇为 IPv4,指定端口号并 转化为 网络字节序列,指定ip地址并转化为 网络 字节序列

2.调用epoll_creat得到一个epoll模型,判断若创建成功,则调用epoll_ctl函数注册要监听事件的类型 ,本代码中设置的监听事件类型为EPOLLIN:表示 对应的文件描述符可以读

epoll的事件注册函数,它不同于 select函数在监听事件的时候告诉内核要监听什么类型的事件,而是 在 这里先注册要监听的事件类型

第一个参数是epoll_creat的返回值

第二个参数表示动作,用三个宏来表示

EPOLL_CTL_ADD:注册新的fd到epfd中

EPOLL_CTL_MOD;修改已经注册到的fd的监听事件

EPOLL_CTL_DEL:从epfd中删除一个fd

第三个参数 是 要监听 的fd

第四个参数 告诉内核 需要监听什么 事件

event可以是以下几个 宏的集合

EPOLLIN:表示 对应的文件描述符可以读(包括对socket正常关闭)

EPOLLOUT:表示 对应的文件描述符可以写

EPOLLPRI:表示对应的文件描述符有紧急的数据可读

EPOLLERR:对应的文件描述符发生错误

EPOLLET:将EPOLL设置为 边沿除法模式 ,这是相对于水平触发而言

EPOLLONESET:只监听一次事件,如果之后还需要监听的话,需要再次把这个socket键入到EPOLL队列里

3.调用epoll_wait函数收集epoll监控的事件中已经发生的事件,参数event是分配好的epoll_event结构体数组。epoll把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到数组中,但不会帮用户开辟内存),maxevents告诉这个内核有多大这个maxevents的值不能大于创建的epoll_ctreat()时的size,

阐述timeout是超时事件(毫秒 ,0立即返回,-1将不确定,也就是永久阻塞)如果函数调用成功,返回对应已准备好的文件描述符数目,如果返回0表示超时

判断它是否为 监听套接字,如果是并且就绪  则对它进行接收,并且以边沿出让的方式读入 ,若是其他套接字 ,则对他进行同样的 接收

时间: 2024-11-04 17:32:57

IO多路复用--epoll的相关文章

IO多路复用--epoll详解

epoll 或者 kqueue 的原理是什么? [转自知乎] Epoll 引入简介 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流.之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据:通过write,我们可以往流写入数据.现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),这时候该怎么办

IO多路复用--epoll(待学习)

华清直播:http://www.makeru.com.cn/live/5413_1937.html 深度理解select.poll和epoll Linux IO模式及 select.poll.epoll详解 原文地址:https://www.cnblogs.com/y4247464/p/12237880.html

内核源码IO多路复用EPOLL

1. 简介: 本文将介绍内核epoll实现的原理.基于kernel 2.6.32版本. 本文只描述epoll对其他fd的监听,由于epoll本身也是一种文件系统,也可以被监听,这一部分不在这里介绍. 2. 基础数据结构: epoll中主要数据结构有两个,一个是epoll_create创建的epoll_fd的结构体eventpoll,一个是事件源对应的epitem结构体. epollevent的数据结构及相应解释如下:这里注意的是eventpoll中其实有两个ready list,一个是常规的rd

IO多路复用之epoll

1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次. 2.epoll接口 epoll操作过程需要三个接口,分别如下: #include <sys/epoll.h> int epoll_create(int size); int epoll_ctl(

nginx 多进程 + io多路复用 实现高并发

一.nginx 高并发原理 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二.nginx 多进程 启动nginx 解析初始化配置文件后会 创建(fork)一个master进程 之后 这个进程会退出 master 进程会 变为孤儿进程 由init进程托管.(可以通过python 或php 启动后创建子进程,然后杀死父进程得见子进程会由init进程托管) 如下图可以看到nginx master 进程由init(ppid 为1 )进程管理. maste

python之IO多路复用(二)——select、poll、epoll详解

select,poll,epoll都是IO多路复用的机制.I/O多路复用就是通过一种机制使一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的 异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间.   sellect.poll.epoll三者的区别 : select通过一个sel

转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的.下面记录下分别基于Select/Poll/Epoll的echo server实现.Python Select Server,可监控事件数量有限制: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

多路复用io接口-epoll

多路复用io接口-epoll的使用. 服务端使用epoll接口处理多个客户的服务请求. 例: 服务端代码 myepoll.c #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #

IO多路复用:select、poll、epoll示例

一.IO多路复用 所谓IO多路复用,就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. Linux支持IO多路复用的系统调用有select.poll.epoll,这些调用都是内核级别的.但select.poll.epoll本质上都是同步I/O,先是block住等待就绪的socket,再是block住将数据从内核拷贝到用户内存. 当然select.poll.epoll之间也是有区别的,如下表: \ select poll e