博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux epoll 学习
阅读量:5223 次
发布时间:2019-06-14

本文共 6189 字,大约阅读时间需要 20 分钟。

一、epoll介绍

epoll是linux内核为处理大批量句柄而作的改进的poll,是linux下IO多路复用select、poll的增强版,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

epoll有两种工作方式:LT(水平触发)、ET(边缘触发)

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

ET(edge-triggered,边缘触发)是高速工作方式,只支持non-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后会假设你知道文件描述符已就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。

epoll相关的系统调用有3个:epoll_create,epoll_ctl,epoll_wait。

#include 
int epoll_create(int size);

参数size:用来告诉内核要监听的数目一共有多少个。

返回值:成功时,返回一个非负整数的文件描述符,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。

说明:创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

#include 
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数epfd:epoll_create()函数返回的epoll句柄。

参数fd:要进行操作的目标文件描述符。

参数event:struct epoll_event结构指针,将fd和药进行的操作关联起来。

返回值:成功时,返回0,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。

说明:epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

参数op的可选值有以下3个:

EPOLL_CTL_ADD:注册新的fd到epfd中。

EPOLL_CTL_MOD:修改已经注册的fd的监听事件。

EPOLL_CTL_DEL:从epfd中删除一个fd。

struct epoll_event结构如下:

typedef union epoll_data{    void *ptr;    int fd;    __uint32_t u32;    __uint64_t u64;}epoll_data_t;struct epoll_event{    __uint32_t events; // epoll event    epoll_data_t data; // user data variables};

events可以以下几个宏的集合:

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

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

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里表示有带外数据到来)。

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

EPOLLHUP:表示对应的文件描述符被挂断。

EPOLLET:将EPOLL设为边沿出触发模式,这是相对于水平触发模式来说的。

EPOLLNESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入的EPOLL队列中。

#include 
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

参数epfd:epoll_create()函数返回的epoll句柄。

参数events:struct epoll_event结构体指针,用来从内核得到事件的集合。

参数maxevents:告诉内核这个events有多大

参数timeout:等待时的超时事件,以毫秒为单位。

返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。

说明:等待事件的产生。

二、epoll的使用

示例1:回显服务器

/******************************************************************************** File Name        : epoll.cpp* Author        : zjw* Email            : emp3XzA3MjJAMTYzLmNvbQo= (base64 encode)* Create Time    : 2015年07月15日 星期三 18时33分00秒*******************************************************************************/#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;const int SERVER_PORT = 8080;const int RECV_SIZE = 1024;const int SEND_SIZE = 1040;const int MAX_EVENTS = 1024;typedef struct ClientInfo{ sockaddr_in addr; string buf;}ClientInfo;int main(int argc, char **argv){ int server = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrServer; bzero(&addrServer, sizeof(addrServer)); addrServer.sin_family = AF_INET; addrServer.sin_port = htons(SERVER_PORT); addrServer.sin_addr.s_addr = INADDR_ANY; if (bind(server, (struct sockaddr*)&addrServer, sizeof(addrServer)) < 0) { perror("bind failed!"); return -1; } listen(server, 5); cout << "server listen on port:" << SERVER_PORT << " ..." << endl; int epollFd = epoll_create(MAX_EVENTS); if (epollFd <= 0) { perror("epoll_create failed,"); return -1; } // set non block fcntl(server, F_SETFL, O_NONBLOCK); struct epoll_event ev; ev.data.fd = server; ev.events = EPOLLIN; // register event if (epoll_ctl(epollFd, EPOLL_CTL_ADD, server, &ev)) { perror("epoll_ctl failed!"); return -1; } struct epoll_event events[100]; int ret = 0; char recvBuf[RECV_SIZE + 1]; char sendBuf[SEND_SIZE + 1]; int client; map
mapClientInfo; while (1) { // wait epoll ret = epoll_wait(epollFd, events, 100, 500); // process active event for (int i = 0; i < ret; i++) { if (events[i].data.fd == server) { // a new client connect sockaddr_in addrClient; socklen_t len = sizeof(addrClient); client = accept(server, (struct sockaddr*)&addrClient, &len); if (client < 0) { perror("accept failed!"); return -1; } cout << "accept a new client:" << inet_ntoa(addrClient.sin_addr) << endl; fcntl(client, F_SETFL, O_NONBLOCK); ev.data.fd = client; ev.events = EPOLLIN; epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &ev); struct ClientInfo info; info.addr = addrClient; info.buf = ""; mapClientInfo.insert(make_pair
(client, info)); } else if (events[i].events & EPOLLIN) { // connectting user and sth can be read if ((client = events[i].data.fd) < 0) { continue; } memset(recvBuf, 0, RECV_SIZE + 1); memset(sendBuf, 0, SEND_SIZE + 1); if ((ret = recv(client, recvBuf, RECV_SIZE, 0)) < 0) { // client closed if (errno == ECONNRESET) { close(client); events[i].data.fd = -1; cout << "client[" << inet_ntoa(mapClientInfo[client].addr.sin_addr) << "] exit!" << endl; mapClientInfo.erase(client); } else { cout << "read from client[" << "ip" << "] failed!" << endl; } } else if (ret = 0) { close(client); events[i].data.fd = -1; cout << "client[" << inet_ntoa(mapClientInfo[client].addr.sin_addr) << "] exit!" << endl; mapClientInfo.erase(client); } cout << "client[" << inet_ntoa(mapClientInfo[client].addr.sin_addr) << "] said:" << recvBuf << endl; sprintf(sendBuf, "Your said:%s", recvBuf); mapClientInfo[client].buf = sendBuf; // set write event ev.data.fd = client; ev.events = EPOLLOUT; epoll_ctl(epollFd, EPOLL_CTL_MOD, client, &ev); } else if (events[i].events & EPOLLOUT) { // write event client = events[i].data.fd; string strTemp = mapClientInfo[client].buf; if ((ret = send(client, strTemp.c_str(), strTemp.length(), 0) < 0)) { perror("send failed!"); return -1; } ev.data.fd = client; ev.events = EPOLLIN; epoll_ctl(epollFd, EPOLL_CTL_MOD, client, &ev); } } } close(server); return 0;}

Makefile:

echo: epoll.cpp    g++ -g -o $@ $

运行结果:

server端

client端192.168.0.161

client端本地192.168.0.160

 三、参考

http://blog.chinaunix.net/uid-23842323-id-2656592.html

http://blog.chinaunix.net/uid-23842323-id-2656593.html

http://blog.csdn.net/sparkliang/article/details/4770655#comments

转载于:https://www.cnblogs.com/lit10050528/p/4652213.html

你可能感兴趣的文章
导航栏上的item的位置设置
查看>>
中继器,集线器,网桥,交换机,路由器
查看>>
iOS block的用法
查看>>
多附件上传控件
查看>>
Floyd 求最短路(poj 1161)
查看>>
word中方框中打钩
查看>>
AtCoder Regular Contest 078
查看>>
spring-注解配置-junit整合测试-aop
查看>>
VMware安装CentOS 7-64(2)
查看>>
select函数详解及实例分析及内核实现原理
查看>>
ADO.NET学习笔记--字符串函数
查看>>
delphi XE5 程序“以管理员身份运行”
查看>>
ROS关于cv_brige的使用
查看>>
iOS - 全屏滑动
查看>>
A * B Problem Plus(fft)
查看>>
OpenCASCADE经典问答之可视化(1)
查看>>
Asp.Net使用百度编辑器(ueditor)
查看>>
ubuntu /mac 终端命令大全
查看>>
指针是个疯狂的东西,可以修改const变量
查看>>
Supervisor 的配置与使用
查看>>