I/O复用之 epoll
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相关推荐
- 使用多线程还是用IO复用select/epoll? epoll 或者 kqueue 的原理是什么?
原作者:蓝形参 原文:http://www.zhihu.com/question/20114168/answer/14024115 使用多线程还是用IO复用select/epoll? 多线程模型适用于 ...
- 深入理解Linux IO复用之epoll
作者:后端技术指南针 来自:后端技术指南针 0.概述 通过本篇文章将了解到以下内容: I/O复用的定义和产生背景Linux系统的I/O复用工具epoll设计的基本构成epoll高性能的底层实现epol ...
- Linux下的I/O复用与epoll详解
前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管 ...
- Linux下的I/O复用与epoll详解(ET与LT)
前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管 ...
- 高性能编程之IO复用之epoll
2019独角兽企业重金招聘Python工程师标准>>> epoll是linux特有的IO复用函数. 把用户关心的文件描述符上的事件放在内核里的一个事件表里,从而无须像select和p ...
- IO复用之epoll系列
epoll是什么? epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的 ...
- linux socket recv函数如何判断收完一包_linux 下经典 IO 复用模型 epoll 的使用
1. 概述 epoll 是 linux 内核为处理大批量文件描述符而对 poll 进行的改进版本,是 linux 下多路复用 IO 接口 select/poll 的增强版本,显著提高了程序在大量并发连 ...
- Linux内核剖析-----IO复用函数epoll内核源码剖析
本文参考董浩博客 http://donghao.org/uii/ epoll内核实现 (1)内核为epoll做准备工作 这个模块在内核初始化时(操作系统启动)注册了一个新的文件系统,叫" ...
- IO复用模型epoll
一.简介 epoll是Linux多路复用IO接口select/poll的加强版,e对应的英文单词就是enhancement[增强] 定义:epoll是一种当文件描述符的内核缓冲区非空的时候,发出可读信 ...
最新文章
- LA 3353 最优巴士线路设计
- python--thread多线程总结
- 深入理解 Java 垃圾回收机制
- 动态调整canvas时的问题
- Neo4j Java REST绑定–第2部分(批处理)
- php mysql 迁移_将phpstudy中的mysql迁移至Linux教程
- python更新后yum问题
- mysql数据字典生成器_「python技术」列表推导、生成器表达式和字典推导的差异及其示例
- HTML MySQL实现登录注册_servlet+html+mysql实现登录注册功能
- shiro访问html没有验证码,Shiro在web应用中实现验证码、回显登录失败信息
- 程序员面试金典——9.3魔术索引
- 如何快速自学生物信息学
- 第六届智能家居亚洲峰会暨精品展(Smart Home Asia 2022)将于10月在沪召开
- DeFi热潮下的安全隐患:流动性危机恐将造成连锁反应 | 非正式会谈
- 嵌入式软件开发工程师面试指南_总结
- RateLimiter配合ConcurrentHashMap对用户进行简单限流
- 阿里云启动全球最大智算中心,总算力达12 EFLOPS
- 基于adb和shell命令的Android Mac电脑日常开发效率小工具
- android hal 定时器,汽车监控定时器 | Android 开源项目 | Android Open Source Project...
- 54 WebGL实现阴影效果