epoll詳解(一)-- epoll在異步網絡編程中的應用

在epoll模型的講解中,這篇文章寫的非常簡單易懂,因此翻譯過來分享給大家,做爲epoll的入門。
原文地址:http://kovyrin.net/2006/04/13/epoll-asynchronous-network-programming/

一般情況下,應用“每個連接應用一個線程”的方式來實現TCP Server。但是在高負載的情況時,這種處理並不是很高效,因此需要應用其他的連接處理模型。在這篇文章中,將討論怎麼應用epoll來構建TCP Server的異步連接。

epoll是在Linux 2.6提出的新的系統調用,來代替被棄用的select(也包括poll)。select和poll的時間複雜度爲O(n),而epoll的時間複雜度爲O(1) - 這說明當被監聽的描述符不斷增加時,epoll仍然表現很好。select使用線性搜索方式,這導致它的時間複雜讀爲O(n),因此爲了提高效率epoll則在內核文件架構中應用callback機制。

epoll中另一個根本的不同是epoll可以使用邊沿觸發,區別與水平觸發。這說明當內核察覺文件描述符爲I/O的就緒狀態時你就會接受到“提示”(文件描述符可進行I/O操作的一瞬間),而不是被告知“描述符可以進行I/O操作”(只要文件描述符可以可進行I/O操作)。這樣的方式由很多小優勢:內核不必保持文件描述符的狀態,而是給用戶空間拋出異常,同時令用戶空間的程序變得更靈活(如,可以忽略總是可讀的狀態)。

應用epoll方法你需要在程序中完成以下幾個步驟:

  • 創建epoll調用的描述符:
    epfd = epoll_create(EPOLL_QUEUE_LEN);
    EPOLL_QUEUE_LEN是你想要同時管理的連接描述符的最大值。返回值epfd將在接下來的epoll調用中使用。這個描述符可以被關閉,當在後續程序中不需要使用時。

  • 完成了第一步後,你可以通過如下調用將需要監聽的描述符添加到epoll中。

static struct epoll_event ev;
int client_sock;
...
ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
ev.data.fd = client_sock;
int res = epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &ev);

ev是epoll事件配置結構體,EPOLL_CTL_ADD - 預定義的常量以用來添加socket到epoll中。epoll_ctrl更詳細的信息參考epoll_ctrl(2)的man頁。當client_sock被關閉時,它將自動在epoll中被刪除。

  • 當需要監聽的描述符添加到epoll中後,你的程序將空閒或者等待以對epoll中socket進行操作。
while (1) {
    // wait for something to do...
    int nfds = epoll_wait(epfd, events, 
            MAX_EPOLL_EVENTS_PER_RUN, 
            EPOLL_RUN_TIMEOUT);
    if (nfds < 0) die("Error in epoll_wait!");

    // for each ready socket
    for(int i = 0; i < nfds; i++) {
        int fd = events[i].data.fd;
        handle_io_on_socket(fd);
    }
}

下面介紹了網絡應用使用中的典型架構,這個架構在單進程和多進程中擴展性都非常好:

  • Listener - 在一個線程中執行bind()listen()調用,同時等待連接接入。當有連接接入後,這個線程可以在監聽的socket上調用accept(),同時將已連入的連接發送給一個I/O-workers.
  • I/O-worker(s) - 一個或多個線程從linsener中接收連接並將他們添加到epoll中。
  • Data Processing worker(s) - 一個或多個線程從I/O-workers中寫入或讀取數據,並進行處理。

如你所見,epoll()API相當簡單,但是卻很強大。線性擴展將允許你管理大量的平行連接,這相比與一個連接一個進程來比,應用了更少的進程。

發佈了37 篇原創文章 · 獲贊 24 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章