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の例のプログラムです.スズメは小さいですが、五臓がそろっています.簡単なタイムアウト検査の仕組みも含まれています.簡潔のためにエラー処理をしていません.
//   
// 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