epoll紹介及びプログラム例
12055 ワード
自動回転http://blog.csdn.net/sparkliang/article/details/4770655
Linux Epollの紹介とプログラムの実例
1.Epollはどこの神聖ですか?
Epollは現在Linux下開発で大規模なネットワークプログラムを併発している人気者です. Linux 2.6カーネルに正式に導入されました.selectと似ていますが、実は全部I/O多重技術です.神秘的なものはありません.
実はLinuxでは、典型的なAppacheモデル(Process Per Connection)、TPCモデル、およびselectモデルとpollモデルなどのネットワークプログラムを同時に設計していますが、なぜEpollというものを導入しますか?それは言いたいことがあります.
2. 常用モデルの欠点
他のモデルの欠点を並べ立てないと、Epollの長所はどう比較できますか?
2.1 PPC/TPCモデル
この二つのモデルは思想が似ています.一つ一つの接続を自分でしながら、もう来ないでください.ただPPCはプロセスを開くために、TPCはスレッドを開いています.しかし、煩わさないでください.時間と空間が必要です.接続が多くなったら、多くのプロセス/スレッドが切り替わります.これはお金がかかります.このようなモデルが許容できる最大接続数は高くないです.普通何百個ぐらいです.
2.2 selectモデル
1. 最大マージ数制限は、一つのプロセスが開いているFD(ファイル記述子)に制限があるので、FD_SETSIZE設定では、デフォルト値は1024/2048であるため、Selectモデルの最大マージン数はこれに応じて制限される.このFDを自分で変えます.SETSIZE?考えはいいですが、まずは下を見てみましょう.
2. 効率の問題は、selectが呼び出すたびに、すべてのFD集合をリニアスキャンし、このように効率は線形的に低下し、FD_SETSIZEが大きくなった結果、みんながゆっくりと来ます.何ですか?タイムアウトですか??!
3. カーネル/ユーザ空間 メモリコピー問題は、FDメッセージをユーザ空間にどうやってカーネルに通知しますか?この問題でselectはメモリコピーの方法をとった.
2.3 pollモデル
基本的に効率はselectと同じで、selectの欠点の2と3は全部直していません.
3.Epollの向上
他のモデルを一つ一つ批判して、Epollの改善点を見てみましょう.実はselectの欠点を逆にすると、Epollの長所です.
3.1.Epollは最大の同時接続の制限がなく、上限は最大で開くことができるファイルの数です.この数字は一般的に2048よりはるかに大きいです. 一般的にこの数はシステムメモリと大きく関係していますが、具体的な数はcat/proc/sys/fs/file-maxで確認できます.
3.2. 効率が向上するEpollの最大の利点は、接続総数に関係なく、あなたの「アクティブ」な接続ができるだけで、実際のネットワーク環境では、Epollの効率はselectとpollよりはるかに高くなります.
3.3. メモリコピー、Epollはこの点で「共有メモリ」を使っていますが、このメモリコピーも省略されています.
4.Epollはなぜ効率がいいですか?
Epollの高効率とそのデータ構造の設計は切り離せないものであり、これは以下に述べる.
まず、selectモデルを思い出してください.I/Oイベントが来た時、selectはアプリケーションにイベントがあったら速やかに処理するように通知します.アプリケーションはすべてのFDセットをポーリングして、FDごとにイベントが発生するかどうかをテストし、イベントを処理しなければなりません.コードは次のようになります.
要点 res=select(maxfd+1、&readfds、NULL、NULL、120)
if(res>0)
{
for(int i=0;i {
if(FD_ISSET(allConnection[i]、&readfds)
{
handleEvent(allConnection[i])
}
}
)
//if(res==0)handle timeout、res<0 handle error
Epollは、アプリケーションにI/0イベントが来るというだけでなく、アプリケーションに関する情報を教えます.これらの情報はアプリケーションが充填されているので、これらの情報アプリケーションによって直接イベントを特定することができます.
intres=epoll wait(epfd、events、20、120)
for(int i=0;i{
handleEvent(events[n]);
)
5.Epollキーデータ構造
前に述べたように、Epollの速度が速いことと、そのデータ構造が密接で不可分です.
structpollengvent{
__uint 32_tevents; //Epoll events
epoll udaut data; //User datavariable
}
typedef ユニオン epoll udta{
void *ptr;
要点 fd;
__uint 32_tu32;
__uint 64_tu64;
}epoll udaut
見ることができるepoll udcaはunion構造体であり、そのアプリケーションによって多くの種類の情報を保存することができます.fd、ポインタなどがあります.これがあれば、アプリケーションは直接ターゲットを特定することができます.
6. Epollを使う
Epollはselectよりいいなら、どう使いますか?煩雑じゃないですか?とりあえず下の三つの関数を見てみてください.Epollの使いやすさが分かります.
int tepollucreat(int size;
Epoll専用のファイル記述子を生成します.実はカーネル空間を申請して、あなたが注目したいsocket fd上で発生したかどうかとどのような事件が発生しましたか?sizeはこのEpoll fd上で注目できる最大のsocket fd数です.メモリが十分ある限り、サイズは自由です.
int polluctl(int epfd intop、 要点 fd、 structpollengvent*イベント;
Epollファイル記述子上のイベントを制御します.登録、変更、削除します.パラメータepfdは、epoll ucreat()がEpoll専用のファイル記述子を作成します.selectモデルのFDuSETとFDuCLRマクロに対して.
int pollouwait(int epfd,struct pollenvents*events,int maxevents,int タイムアウト
I/Oイベントの発生を待つ;パラメータの説明:
epfd:epoll ucreat() 生成されたEpoll専用のファイル記述子.
epoll_uevent:事件を代々処理するための行列.
maxevents:毎回処理できるイベント数.
timeout:I/Oイベントの発生を待つタイムアウト値;
発生件数を返します.
selectモデルにおけるselect関数に対して.
7. サンプルプログラム
以下は簡単なEcho Serverの例のプログラムです.スズメは小さいですが、五臓がそろっています.簡単なタイムアウト検査の仕組みも含まれています.簡潔のためにエラー処理をしていません.
http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html
http://www.cppblog.com/khan/archive/2008/04/02/46013.html
http://www.cnblogs.com/apprentice89/p/3234677.html
Linux Epollの紹介とプログラムの実例
1.Epollはどこの神聖ですか?
Epollは現在Linux下開発で大規模なネットワークプログラムを併発している人気者です. Linux 2.6カーネルに正式に導入されました.selectと似ていますが、実は全部I/O多重技術です.神秘的なものはありません.
実はLinuxでは、典型的なAppacheモデル(Process Per Connection)、TPCモデル、およびselectモデルとpollモデルなどのネットワークプログラムを同時に設計していますが、なぜEpollというものを導入しますか?それは言いたいことがあります.
2. 常用モデルの欠点
他のモデルの欠点を並べ立てないと、Epollの長所はどう比較できますか?
2.1 PPC/TPCモデル
この二つのモデルは思想が似ています.一つ一つの接続を自分でしながら、もう来ないでください.ただPPCはプロセスを開くために、TPCはスレッドを開いています.しかし、煩わさないでください.時間と空間が必要です.接続が多くなったら、多くのプロセス/スレッドが切り替わります.これはお金がかかります.このようなモデルが許容できる最大接続数は高くないです.普通何百個ぐらいです.
2.2 selectモデル
1. 最大マージ数制限は、一つのプロセスが開いているFD(ファイル記述子)に制限があるので、FD_SETSIZE設定では、デフォルト値は1024/2048であるため、Selectモデルの最大マージン数はこれに応じて制限される.このFDを自分で変えます.SETSIZE?考えはいいですが、まずは下を見てみましょう.
2. 効率の問題は、selectが呼び出すたびに、すべてのFD集合をリニアスキャンし、このように効率は線形的に低下し、FD_SETSIZEが大きくなった結果、みんながゆっくりと来ます.何ですか?タイムアウトですか??!
3. カーネル/ユーザ空間 メモリコピー問題は、FDメッセージをユーザ空間にどうやってカーネルに通知しますか?この問題でselectはメモリコピーの方法をとった.
2.3 pollモデル
基本的に効率はselectと同じで、selectの欠点の2と3は全部直していません.
3.Epollの向上
他のモデルを一つ一つ批判して、Epollの改善点を見てみましょう.実はselectの欠点を逆にすると、Epollの長所です.
3.1.Epollは最大の同時接続の制限がなく、上限は最大で開くことができるファイルの数です.この数字は一般的に2048よりはるかに大きいです. 一般的にこの数はシステムメモリと大きく関係していますが、具体的な数はcat/proc/sys/fs/file-maxで確認できます.
3.2. 効率が向上するEpollの最大の利点は、接続総数に関係なく、あなたの「アクティブ」な接続ができるだけで、実際のネットワーク環境では、Epollの効率はselectとpollよりはるかに高くなります.
3.3. メモリコピー、Epollはこの点で「共有メモリ」を使っていますが、このメモリコピーも省略されています.
4.Epollはなぜ効率がいいですか?
Epollの高効率とそのデータ構造の設計は切り離せないものであり、これは以下に述べる.
まず、selectモデルを思い出してください.I/Oイベントが来た時、selectはアプリケーションにイベントがあったら速やかに処理するように通知します.アプリケーションはすべてのFDセットをポーリングして、FDごとにイベントが発生するかどうかをテストし、イベントを処理しなければなりません.コードは次のようになります.
要点 res=select(maxfd+1、&readfds、NULL、NULL、120)
if(res>0)
{
for(int i=0;i
if(FD_ISSET(allConnection[i]、&readfds)
{
handleEvent(allConnection[i])
}
}
)
//if(res==0)handle timeout、res<0 handle error
Epollは、アプリケーションにI/0イベントが来るというだけでなく、アプリケーションに関する情報を教えます.これらの情報はアプリケーションが充填されているので、これらの情報アプリケーションによって直接イベントを特定することができます.
intres=epoll wait(epfd、events、20、120)
for(int i=0;i
handleEvent(events[n]);
)
5.Epollキーデータ構造
前に述べたように、Epollの速度が速いことと、そのデータ構造が密接で不可分です.
structpollengvent{
__uint 32_tevents; //Epoll events
epoll udaut data; //User datavariable
}
typedef ユニオン epoll udta{
void *ptr;
要点 fd;
__uint 32_tu32;
__uint 64_tu64;
}epoll udaut
見ることができるepoll udcaはunion構造体であり、そのアプリケーションによって多くの種類の情報を保存することができます.fd、ポインタなどがあります.これがあれば、アプリケーションは直接ターゲットを特定することができます.
6. Epollを使う
Epollはselectよりいいなら、どう使いますか?煩雑じゃないですか?とりあえず下の三つの関数を見てみてください.Epollの使いやすさが分かります.
int tepollucreat(int size;
Epoll専用のファイル記述子を生成します.実はカーネル空間を申請して、あなたが注目したいsocket fd上で発生したかどうかとどのような事件が発生しましたか?sizeはこのEpoll fd上で注目できる最大のsocket fd数です.メモリが十分ある限り、サイズは自由です.
int polluctl(int epfd intop、 要点 fd、 structpollengvent*イベント;
Epollファイル記述子上のイベントを制御します.登録、変更、削除します.パラメータepfdは、epoll ucreat()がEpoll専用のファイル記述子を作成します.selectモデルのFDuSETとFDuCLRマクロに対して.
int pollouwait(int epfd,struct pollenvents*events,int maxevents,int タイムアウト
I/Oイベントの発生を待つ;パラメータの説明:
epfd:epoll ucreat() 生成されたEpoll専用のファイル記述子.
epoll_uevent:事件を代々処理するための行列.
maxevents:毎回処理できるイベント数.
timeout:I/Oイベントの発生を待つタイムアウト値;
発生件数を返します.
selectモデルにおけるselect関数に対して.
7. サンプルプログラム
以下は簡単なEcho Serverの例のプログラムです.スズメは小さいですが、五臓がそろっています.簡単なタイムアウト検査の仕組みも含まれています.簡潔のためにエラー処理をしていません.
//
// a simple echo server using epoll in linux
//
// 2009-11-05
// 2013-03-22: ,1 /n ,2 ET ;
// , recv/send buffer
// by sparkling
//
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
#define MAX_EVENTS 500
struct myevent_s
{
int fd;
void (*call_back)(int fd, int events, void *arg);
int events;
void *arg;
int status; // 1: in epoll wait list, 0 not in
char buff[128]; // recv data buffer
int len, s_offset;
long last_active; // last active time
};
// set event
void EventSet(myevent_s *ev, int fd, void (*call_back)(int, int, void*), void *arg)
{
ev->fd = fd;
ev->call_back = call_back;
ev->events = 0;
ev->arg = arg;
ev->status = 0;
bzero(ev->buff, sizeof(ev->buff));
ev->s_offset = 0;
ev->len = 0;
ev->last_active = time(NULL);
}
// add/mod an event to epoll
void EventAdd(int epollFd, int events, myevent_s *ev)
{
struct epoll_event epv = {0, {0}};
int op;
epv.data.ptr = ev;
epv.events = ev->events = events;
if(ev->status == 1){
op = EPOLL_CTL_MOD;
}
else{
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if(epoll_ctl(epollFd, op, ev->fd, &epv) < 0)
printf("Event Add failed[fd=%d], evnets[%d]
", ev->fd, events);
else
printf("Event Add OK[fd=%d], op=%d, evnets[%0X]
", ev->fd, op, events);
}
// delete an event from epoll
void EventDel(int epollFd, myevent_s *ev)
{
struct epoll_event epv = {0, {0}};
if(ev->status != 1) return;
epv.data.ptr = ev;
ev->status = 0;
epoll_ctl(epollFd, EPOLL_CTL_DEL, ev->fd, &epv);
}
int g_epollFd;
myevent_s g_Events[MAX_EVENTS+1]; // g_Events[MAX_EVENTS] is used by listen fd
void RecvData(int fd, int events, void *arg);
void SendData(int fd, int events, void *arg);
// accept new connections from clients
void AcceptConn(int fd, int events, void *arg)
{
struct sockaddr_in sin;
socklen_t len = sizeof(struct sockaddr_in);
int nfd, i;
// accept
if((nfd = accept(fd, (struct sockaddr*)&sin, &len)) == -1)
{
if(errno != EAGAIN && errno != EINTR)
{
}
printf("%s: accept, %d", __func__, errno);
return;
}
do
{
for(i = 0; i < MAX_EVENTS; i++)
{
if(g_Events[i].status == 0)
{
break;
}
}
if(i == MAX_EVENTS)
{
printf("%s:max connection limit[%d].", __func__, MAX_EVENTS);
break;
}
// set nonblocking
int iret = 0;
if((iret = fcntl(nfd, F_SETFL, O_NONBLOCK)) < 0)
{
printf("%s: fcntl nonblocking failed:%d", __func__, iret);
break;
}
// add a read event for receive data
EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]);
EventAdd(g_epollFd, EPOLLIN, &g_Events[i]);
}while(0);
printf("new conn[%s:%d][time:%d], pos[%d]
", inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port), g_Events[i].last_active, i);
}
// receive data
void RecvData(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s*)arg;
int len;
// receive data
len = recv(fd, ev->buff+ev->len, sizeof(ev->buff)-1-ev->len, 0);
EventDel(g_epollFd, ev);
if(len > 0)
{
ev->len += len;
ev->buff[len] = '\0';
printf("C[%d]:%s
", fd, ev->buff);
// change to send event
EventSet(ev, fd, SendData, ev);
EventAdd(g_epollFd, EPOLLOUT, ev);
}
else if(len == 0)
{
close(ev->fd);
printf("[fd=%d] pos[%d], closed gracefully.
", fd, ev-g_Events);
}
else
{
close(ev->fd);
printf("recv[fd=%d] error[%d]:%s
", fd, errno, strerror(errno));
}
}
// send data
void SendData(int fd, int events, void *arg)
{
struct myevent_s *ev = (struct myevent_s*)arg;
int len;
// send data
len = send(fd, ev->buff + ev->s_offset, ev->len - ev->s_offset, 0);
if(len > 0)
{
printf("send[fd=%d], [%d<->%d]%s
", fd, len, ev->len, ev->buff);
ev->s_offset += len;
if(ev->s_offset == ev->len)
{
// change to receive event
EventDel(g_epollFd, ev);
EventSet(ev, fd, RecvData, ev);
EventAdd(g_epollFd, EPOLLIN, ev);
}
}
else
{
close(ev->fd);
EventDel(g_epollFd, ev);
printf("send[fd=%d] error[%d]
", fd, errno);
}
}
void InitListenSocket(int epollFd, short port)
{
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking
printf("server listen fd=%d
", listenFd);
EventSet(&g_Events[MAX_EVENTS], listenFd, AcceptConn, &g_Events[MAX_EVENTS]);
// add listen socket
EventAdd(epollFd, EPOLLIN, &g_Events[MAX_EVENTS]);
// bind & listen
sockaddr_in sin;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
bind(listenFd, (const sockaddr*)&sin, sizeof(sin));
listen(listenFd, 5);
}
int main(int argc, char **argv)
{
unsigned short port = 12345; // default port
if(argc == 2){
port = atoi(argv[1]);
}
// create epoll
g_epollFd = epoll_create(MAX_EVENTS);
if(g_epollFd <= 0) printf("create epoll failed.%d
", g_epollFd);
// create & bind listen socket, and add to epoll, set non-blocking
InitListenSocket(g_epollFd, port);
// event loop
struct epoll_event events[MAX_EVENTS];
printf("server running:port[%d]
", port);
int checkPos = 0;
while(1){
// a simple timeout check here, every time 100, better to use a mini-heap, and add timer event
long now = time(NULL);
for(int i = 0; i < 100; i++, checkPos++) // doesn't check listen fd
{
if(checkPos == MAX_EVENTS) checkPos = 0; // recycle
if(g_Events[checkPos].status != 1) continue;
long duration = now - g_Events[checkPos].last_active;
if(duration >= 60) // 60s timeout
{
close(g_Events[checkPos].fd);
printf("[fd=%d] timeout[%d--%d].
", g_Events[checkPos].fd, g_Events[checkPos].last_active, now);
EventDel(g_epollFd, &g_Events[checkPos]);
}
}
// wait for events to happen
int fds = epoll_wait(g_epollFd, events, MAX_EVENTS, 1000);
if(fds < 0){
printf("epoll_wait error, exit
");
break;
}
for(int i = 0; i < fds; i++){
myevent_s *ev = (struct myevent_s*)events[i].data.ptr;
if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN)) // read event
{
ev->call_back(ev->fd, events[i].events, ev->arg);
}
if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) // write event
{
ev->call_back(ev->fd, events[i].events, ev->arg);
}
}
}
// free resource
return 0;
}
見てください:http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html
http://www.cppblog.com/khan/archive/2008/04/02/46013.html
http://www.cnblogs.com/apprentice89/p/3234677.html