epoll()簡單介紹

epoll()簡單介紹(轉貼)

c版在本年初曾有過關於多路轉接I/O的討論,涉及到了epoll(),不過側重點不是它。我看過linux 2.6內核的代碼,感覺epoll()和select()僅僅是文件描述符的存儲方式不一樣(epoll()用紅黑樹,select()用位串),select()效率似乎還要高些,但是大家都說epoll()效率最高,所以需要測試。
很久沒有在unix下編程了,希望c版的學術氣氛越來越濃!
以下是文章,出處不明

Q:網絡服務器的瓶頸在哪?
A:IO效率。

  在大家苦苦的爲在線人數的增長而導致的系統資源吃緊上的問題正在發愁的時候,Linux 2.6內核中提供的System Epoll爲我們提供了一套完美的解決方案。傳統的select以及poll的效率會因爲在線人數的線形遞增而導致呈二次乃至三次方的下降,這些直接導致了網絡服務器可以支持的人數有了個比較明顯的限制。

  自從Linux提供了/dev/epoll的設備以及後來2.6內核中對/dev/epoll設備的訪問的封裝(System Epoll)之後,這種現象得到了大大的緩解,如果說幾個月前,大家還對epoll不熟悉,那麼現在來說的話,epoll的應用已經得到了大範圍的普及。

  那麼究竟如何來使用epoll呢?其實非常簡單。
  通過在包含一個頭文件#include <sys/epoll.h>;以及幾個簡單的API將可以大大的提高你的網絡服務器的支持人數。

  首先通過create_epoll(int maxfds)來創建一個epoll的句柄,其中maxfds爲你epoll所支持的最大句柄數。這個函數會返回一個新的epoll句柄,之後的所有操作將通過這個句柄來進行操作。在用完之後,記得用close()來關閉這個創建出來的epoll句柄。

  之後在你的網絡主循環裏面,每一幀的調用epoll_wait(int epfd, epoll_event events, int max events, int timeout)來查詢所有的網絡接口,看哪一個可以讀,哪一個可以寫了。基本的語法爲:

  nfds = epoll_wait(kdpfd, events, maxevents, -1);

  其中kdpfd爲用epoll_create創建之後的句柄,events是一個epoll_event*的指針,當epoll_wait這個函數操 作成功之後,epoll_events裏面將儲存所有的讀寫事件。max_events是當前需要監聽的所有socket句柄數。最後一個timeout 是epoll_wait的超時,爲0的時候表示馬上返回,爲-1的時候表示一直等下去,直到有事件範圍,爲任意正整數的時候表示等這麼長的時間,如果一直 沒有事件,則範圍。一般如果網絡主循環是單獨的線程的話,可以用-1來等,這樣可以保證一些效率,如果是和主邏輯在同一個線程的話,則可以用0來保證主循 環的效率。

epoll_wait範圍之後應該是一個循環,遍利所有的事件:

    for(n = 0; n < nfds; ++n) {
        if(events[n].data.fd == listener) { //如果是主socket的事件的話,則表示有新連接進入了,進行新連接的處理。
            client = accept(listener, (struct sockaddr *) &local, &addrlen);
        if(client < 0){
            perror("accept");
            continue;
    }
    setnonblocking(client); // 將新連接置於非阻塞模式
    ev.events = EPOLLIN | EPOLLET; // 並且將新連接也加入EPOLL的監聽隊列。

  注意,這裏的參數EPOLLIN | EPOLLET並沒有設置對寫socket的監聽,如果有寫操作的話,這個時候epoll是不會返回事件的,如果要對寫操作也監聽的話,應該是EPOLLIN | EPOLLOUT | EPOLLET

    ev.data.fd = client;
    if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
        // 設置好event之後,將這個新的event通過epoll_ctl加入到epoll的監聽隊列裏面,這裏用EPOLL_CTL_ADD來加一個新的 epoll事件,通過EPOLL_CTL_DEL來減少一個epoll事件,通過EPOLL_CTL_MOD來改變一個事件的監聽方式。
        fprintf(stderr, "epoll set insertion error: fd=%d0, client);
        return -1;
        }
    }
    else // 如果不是主socket的事件的話,則代表是一個用戶socket的事件,則來處理這個用戶socket的事情,比如說read(fd,xxx)之類的,或者一些其他的處理。
        do_use_fd(events[n].data.fd);
    }

  對,epoll的操作就這麼簡單,總共不過4個API:epoll_create, epoll_ctl, epoll_wait和close。
  如果您對epoll的效率還不太瞭解,請參考我之前關於網絡遊戲的網絡編程等相關的文章。

  世界變了,原來擔心的問題,現在已經不是問題了。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章