epoll 系统调用


1. 内核事件表

epoll使用一系列函数来完成任务,把用户关心的文件描述符中的事件放到内核里的一个事件表中,因此不用像select、poll那样每次调用都要重复传入文件描述符集或事件表。epoll需要一个文件描述符来唯一标识该事件表,该文件描述符使用epoll_create函数创建:

    #include <sys/epoll.h>int epoll_create( int size );

size 标记事件表大小。该函数返回的文件描述符将用作其他所有epoll系统调用的第一个参数,以指定要访问的内核事件表。

epoll_ctl函数用于操作epoll的内核事件表:

    #include <sys/epoll.h>int epoll_ctl( int epfd, int op, int fd, struct epoll_event * event )

fd参数是要操作的文件描述符,op参数指定操作类型(如下):

  • EPOLL_CTL_ADD,往事件表中注册fd上的事件
  • EPOLL_CTL_DEL,删除fd上的注册事件
  • EPOLL_CTL_MOD,修改fd上的注册事件

event参数指定被操作的事件,它是epoll_event结构类型指针。epoll_event的定义如下:

    struct epoll_event{__uint32_t events;  //epoll事件epoll_data_t data;  //用户数据}

其中events成员描述事件类型(如 EPOLLIN表示数据可读事件)。epoll_data_t 定义如下:

    typedef union epoll_data{void * ptr;int fd;uint32_t u32;uint64_t u64;} epoll_data_t;

epoll_data_t 是一个共用体,其4个成员使用最多的是fd,它指定事件所从属的目标文件描述符。各成员不能同时使用。


2. epoll_wait 函数

epoll_wait 函数在一段超时时间内等待一组文件描述符上的事件,其原型如下:

#include <sys/epoll.h>
int epoll_wait( int epfd, struct epoll_event * events, int maxevents, int timeout ) 

该函数成功时返回就绪的文件描述符的个数,失败时返回-1并设置errno。timeout参数为延时时间。maxevents参数指定最多监听多少个事件,它必须大于0。
epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd指定)中复制到它的第二个参数events指向的数组中,该数组只用于输出epoll_wait检测到的就绪事件,而不像select和poll那样既传入用户注册事件,又输出内核检测到的就绪事件。这极大提高了应用程序索引就绪文件描述符的效率。

    /* 如何索引 epoll 返回的就绪文件描述符 */int ret = epol_wait( epoll_fd, events, MAX_EVENT_NUMBER, -1 );// 仅遍历就绪的ret个文件描述符for ( int i = 0; i < ret; i++ ){int sock_fd = events[i].data.fd;  //events中的所有文件描述符都已就绪handle( sock_fd );  //处理就绪的sock_fd}

3. LT和ET模式

LT : 电平触发(默认工作方式)
ET :边沿触发(高效工作方式)

对于采用LT工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当下一次调用epoll_wait()时,epoll_wait()还会再次向应用程序通知此事件,直至该事件被处理。
对于采用ET工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait()将不再向应用程序通知该事件。
可见,ET模式在很大程度上降低了同一个epoll事件被重复触发的次数,因此效率会比LT模式高。


4. code

/*  该程序以  “ ./a.out ip_number port_number ”   方式使用
**  通过telnet连接到该运行程序 */#include <iostream>
#include <cstdio>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sys/epoll.h>
using namespace std;const int MAX_EVENT_NUMBER = 1024;
const int BUFFER_SIZE = 10;void err( int );
void addfd( int , int , bool );
int setnonblocking( int );
void lt( epoll_event * , int , int , int );
void et( epoll_event * , int , int , int );int main(int argc, char * argv[]) {if ( argc < 3 ) {cout << "usage:\n";return 1;}const char *ip = argv[1];const int port = atoi( argv[2] );struct sockaddr_in address;memset( &address, 0, sizeof( address ) );address.sin_family = AF_INET;address.sin_port = htons( port );inet_pton( AF_INET, ip, &address.sin_addr );int sock_fd = socket( AF_INET, SOCK_STREAM, 0 );if ( sock_fd < 0) {err( __LINE__ );}int ret = bind( sock_fd, ( struct sockaddr * )&address, sizeof( address ) );if ( ret < 0 ) {err( __LINE__ );}ret = listen( sock_fd, 5 );if ( ret < 0 ) {err( __LINE__ );}epoll_event events[MAX_EVENT_NUMBER];  //内核事件表,通过epoll_wait()函数发生作用int epoll_fd = epoll_create( 5 );if ( epoll_fd < 0 ) {err( __LINE__ );}addfd( epoll_fd, sock_fd, true );while( true ) {ret = epoll_wait( epoll_fd, events, MAX_EVENT_NUMBER, -1 );if ( ret < 0 ) {cout << "epoll failure\n";break;}lt( events, ret, epoll_fd, sock_fd );  //使用LT模式// et( events, ret, epoll_fd, sock_fd );  //使用ET模式}close( sock_fd );return 0;
}void err(int line) {cout << "error: line " << line << endl;
}void addfd( int epoll_fd, int fd, bool enable_et ) {epoll_event event;event.data.fd = fd;event.events = EPOLLIN;if ( enable_et ) {event.events |= EPOLLET;}epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event );setnonblocking( fd );
}int setnonblocking( int fd ) {int old_option = fcntl( fd, F_GETFL );int new_option = old_option | O_NONBLOCK;fcntl( fd, F_SETFL, new_option );return old_option;
}void lt( epoll_event * events, int number, int epoll_fd, int listen_fd) {char buf[ BUFFER_SIZE ];for( int i = 0; i < number; i++ ) {int sock_fd = events[i].data.fd;if ( sock_fd == listen_fd ) {  //有新的连接请求struct sockaddr_in client;socklen_t client_length = sizeof( client );int conn_fd = accept( sock_fd, ( struct sockaddr * )&client,&client_length );addfd( epoll_fd, conn_fd, false );} else if ( events[i].events & EPOLLIN ) {  //还存在未读数据cout << "event trigger once\n";memset( buf, 0, sizeof( buf ) );int ret = recv( sock_fd, buf, BUFFER_SIZE - 1, 0 );if ( ret <= 0 ) {close( sock_fd );}printf( "get %d bytes of content: -%s-\n", ret, buf );} else {cout << "something else happened\n";}}
}void et( epoll_event * events, int number, int epoll_fd, int listen_fd) {char buf[ BUFFER_SIZE ];for ( int i = 0; i < number; i++ ) {int sock_fd = events[i].data.fd;if ( sock_fd == listen_fd ) {  //有新的连接请求struct sockaddr_in client;socklen_t client_length = sizeof( client );int conn_fd = accept( sock_fd, ( struct sockaddr * )&client,&client_length );addfd( epoll_fd, conn_fd, true );} else if ( events[i].events & EPOLLIN ) {  //还存在未读数据printf("event trigger once\n");while( true ) {memset( buf, 0, sizeof( buf ) );int ret = recv( sock_fd, buf, BUFFER_SIZE - 1, 0 );if ( ret < 0 ) {/* 数据已全部读取完毕 */if ( errno == EAGAIN || errno == EWOULDBLOCK ) {cout << "read later\n";break;}close( sock_fd );break;} else if ( !ret ) {close( sock_fd );} else {printf("got %d bytes of content: -%s-\n", ret, buf);}}}}
}

I/O复用之 epoll相关推荐

  1. 使用多线程还是用IO复用select/epoll? epoll 或者 kqueue 的原理是什么?

    原作者:蓝形参 原文:http://www.zhihu.com/question/20114168/answer/14024115 使用多线程还是用IO复用select/epoll? 多线程模型适用于 ...

  2. 深入理解Linux IO复用之epoll

    作者:后端技术指南针 来自:后端技术指南针 0.概述 通过本篇文章将了解到以下内容: I/O复用的定义和产生背景Linux系统的I/O复用工具epoll设计的基本构成epoll高性能的底层实现epol ...

  3. Linux下的I/O复用与epoll详解

    前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管 ...

  4. Linux下的I/O复用与epoll详解(ET与LT)

    前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管 ...

  5. 高性能编程之IO复用之epoll

    2019独角兽企业重金招聘Python工程师标准>>> epoll是linux特有的IO复用函数. 把用户关心的文件描述符上的事件放在内核里的一个事件表里,从而无须像select和p ...

  6. IO复用之epoll系列

    epoll是什么? epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的 ...

  7. linux socket recv函数如何判断收完一包_linux 下经典 IO 复用模型 epoll 的使用

    1. 概述 epoll 是 linux 内核为处理大批量文件描述符而对 poll 进行的改进版本,是 linux 下多路复用 IO 接口 select/poll 的增强版本,显著提高了程序在大量并发连 ...

  8. Linux内核剖析-----IO复用函数epoll内核源码剖析

    本文参考董浩博客 http://donghao.org/uii/   epoll内核实现 (1)内核为epoll做准备工作 这个模块在内核初始化时(操作系统启动)注册了一个新的文件系统,叫" ...

  9. IO复用模型epoll

    一.简介 epoll是Linux多路复用IO接口select/poll的加强版,e对应的英文单词就是enhancement[增强] 定义:epoll是一种当文件描述符的内核缓冲区非空的时候,发出可读信 ...

最新文章

  1. LA 3353 最优巴士线路设计
  2. python--thread多线程总结
  3. 深入理解 Java 垃圾回收机制
  4. 动态调整canvas时的问题
  5. Neo4j Java REST绑定–第2部分(批处理)
  6. php mysql 迁移_将phpstudy中的mysql迁移至Linux教程
  7. python更新后yum问题
  8. mysql数据字典生成器_「python技术」列表推导、生成器表达式和字典推导的差异及其示例
  9. HTML MySQL实现登录注册_servlet+html+mysql实现登录注册功能
  10. shiro访问html没有验证码,Shiro在web应用中实现验证码、回显登录失败信息
  11. 程序员面试金典——9.3魔术索引
  12. 如何快速自学生物信息学
  13. 第六届智能家居亚洲峰会暨精品展(Smart Home Asia 2022)将于10月在沪召开
  14. DeFi热潮下的安全隐患:流动性危机恐将造成连锁反应 | 非正式会谈
  15. 嵌入式软件开发工程师面试指南_总结
  16. RateLimiter配合ConcurrentHashMap对用户进行简单限流
  17. 阿里云启动全球最大智算中心,总算力达12 EFLOPS
  18. 基于adb和shell命令的Android Mac电脑日常开发效率小工具
  19. android hal 定时器,汽车监控定时器  |  Android 开源项目  |  Android Open Source Project...
  20. 54 WebGL实现阴影效果

热门文章

  1. 一个前端岗位电话面试所带来的问题的思考
  2. 在vue中安装使用vux
  3. Jira filter subscribe issues
  4. noip模拟赛 写代码
  5. 模拟聊天室显示语句保持最新显示
  6. 修改input的placeholder颜色
  7. 关于在页面中针对不同版本的IE浏览器实现不同的JS或者CSS样式
  8. jquery全选,jquery全不选,jquery反选
  9. PHP Cookbook读书笔记 – 第16章互联网服务
  10. ellen 纽奥良大学演讲