一、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 <sys/epoll.h>
int epoll_create(int size);

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

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

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

#include <sys/epoll.h>
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 <sys/epoll.h>
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 <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h> #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <errno.h>
using namespace std; const int SERVER_PORT = ;
const int RECV_SIZE = ;
const int SEND_SIZE = ;
const int MAX_EVENTS = ;
typedef struct ClientInfo
{
sockaddr_in addr;
string buf;
}ClientInfo; int main(int argc, char **argv)
{
int server = socket(AF_INET, SOCK_STREAM, );
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)) < )
{
perror("bind failed!");
return -;
} listen(server, );
cout << "server listen on port:" << SERVER_PORT << " ..." << endl; int epollFd = epoll_create(MAX_EVENTS);
if (epollFd <= )
{
perror("epoll_create failed,");
return -;
} // 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 -;
} struct epoll_event events[];
int ret = ;
char recvBuf[RECV_SIZE + ];
char sendBuf[SEND_SIZE + ];
int client;
map<int, struct ClientInfo> mapClientInfo;
while ()
{
// wait epoll
ret = epoll_wait(epollFd, events, , );
// process active event
for (int i = ; 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 < )
{
perror("accept failed!");
return -;
}
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<int, struct ClientInfo>(client, info));
}
else if (events[i].events & EPOLLIN)
{ // connectting user and sth can be read
if ((client = events[i].data.fd) < )
{
continue;
} memset(recvBuf, , RECV_SIZE + );
memset(sendBuf, , SEND_SIZE + );
if ((ret = recv(client, recvBuf, RECV_SIZE, )) < )
{ // client closed
if (errno == ECONNRESET)
{
close(client);
events[i].data.fd = -;
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 = )
{
close(client);
events[i].data.fd = -;
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(), ) < ))
{
perror("send failed!");
return -;
} ev.data.fd = client;
ev.events = EPOLLIN;
epoll_ctl(epollFd, EPOLL_CTL_MOD, client, &ev);
}
}
} close(server);
return ;
}

Makefile:

echo: epoll.cpp
g++ -g -o $@ $< clean:
rm -rf echo

运行结果:

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

linux epoll 学习的更多相关文章

  1. linux epoll学习

    #include <sys/time.h> /* For portability */ #include <sys/select.h> int select(int nfds, ...

  2. c/c++ linux epoll系列3 利用epoll_wait设置timeout时间长度

    linux epoll系列3 利用epoll_wait设置timeout时间长度 epoll_wait函数的第四个参数可以设置,epoll_wait函数的等待时间(timeout时间长度). 例子1, ...

  3. c/c++ linux epoll系列2 利用epoll_wait查看是否可以送信

    linux epoll系列2 利用epoll_wait查看是否可以送信 write函数本来是非阻塞函数,但是当缓存区被写满后,再往缓存区里写的时候,就必须等待缓存区再次变成可写,所以这是write就变 ...

  4. c/c++ linux epoll系列1 创建epoll

    linux epoll系列1 创建epoll 据说select和poll的弱点是,随着连接(socket)的增加,性能会直线下降. epoll不会随着连接(socket)的增加,性能直线下降. 知识点 ...

  5. Linux.NET学习手记(7)

    前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...

  6. Linux.NET学习手记(8)

    上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中.这 ...

  7. 关于《Linux.NET学习手记(8)》的补充说明

    早前的一两天<Linux.NET学习手记(8)>发布了,这一篇主要是讲述OWIN框架与OwinHost之间如何根据OWIN协议进行通信构成一套完整的系统.文中我们还直接学习如何直接操作OW ...

  8. Linux LVM学习总结——扩展卷组VG

    Linux服务器由于应用变更或需求的缘故,有可能出现分区空间不足的情况,此时往往需要进行扩容(要增加分区的空间),而采用LVM的好处就是可以在不需停机的情况下可以方便地调整各个分区大小.如下所示,分区 ...

  9. linux的学习记录随笔

    为什么学习linux 因为操作系统是一种介质,你要接触其中的东西,首先必须要有介质,而linux在服务器端是老大哥的地位,所以呢,学习linux吧. 学习的方式 可以看视频 imooc.百度传课.网易 ...

随机推荐

  1. Leetcode: Max Sum of Rectangle No Larger Than K

    Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix s ...

  2. UISearchController的使用

    - (void)addSearchController { _searchController = [[UISearchController alloc] initWithSearchResultsC ...

  3. Codeforces Round #312 (Div. 2) E. A Simple Task

    题目大意就是给一个字符串,然后多个操作,每次操作可以把每一段区间的字符进行升序或者降序排序,问最终的字符串是多少. 一开始只考虑字符串中字符'a'的情况,假设操作区间[L,R]中有x个'a',那么一次 ...

  4. (三)开关检测来控制LED灯的亮灭

    开关检测案例一: 具体电路图如下: K1--K4闭合,控制 D1—D4 亮灭 产生的问题: 1.关于 R8 R9 R7 R10 的阻值选择问题,倘若太大的话,  比如10K 不管开关断开还是闭合,好像 ...

  5. Android 5.0新特性了解(二)----RippleEffect

    1.本文介绍的是Android5.0中其中一个炫酷的效果,点击水波纹扩散效果( RippleEffect),以下介绍的实现方式都是调用Android5.0的新API,并非自定义实现,所以支持在Andr ...

  6. git 命令--上传代码

    创建密钥命令: ssh-keygen -C 'your@email.address' -t rsa 找到生成的密钥文件id_rsa.pub 地址:C:\Documents and Settings\A ...

  7. Html基础知识讲解

    Html基础知识讲解 <title>淄博汉企</title> </head> <body bgcolor="#66FFCC" topmar ...

  8. 10个免费的PHP编辑器/开发工具

    转自: http://www.iteye.com/news/22672 一个好的编辑器或开发工具,能够极大提高我们的开发效率.下面介绍10个免费.强大的PHP编辑器/开发工具.这些编辑器拥有调试器.增 ...

  9. 二招解决php乱码问题

    PHP的乱码问题已经说了N+1遍了,但还是经常看到新手不知道该如何解决php乱码问题,在此本人再重新给总结一下,希望对新手有点帮助 php网页出现乱码一般是在建立数据库时用的编码和php网页的编码不同 ...

  10. 将服务费用DIY到底----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十)[转]

    前一段时间,讲了一系列开发经理.实施经理.服务经理的工具箱:开发经理的工具箱---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三) ,实施经理的工具箱--走出软件作坊:三五个人十来条枪 如何成 ...