日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

百度epoll

發布時間:2023/12/13 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 百度epoll 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
epoll 編輯 epoll是Linux內核為處理大批量句柄而作了改進的poll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率。

目 錄

1簡介

2優點

  • 2.1?支持一個進程打開大數目的socket描述符
  • 2.2?IO效率不隨FD數目增加而線性下降
  • 2.3?使用mmap加速內核與用戶空間的消息傳遞
  • 3內核微調

    4使用

    5系統調用

    1簡介

    使用epoll進行高性能網絡編程

    epoll是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率,因為它會復用文件描述符集合來傳遞結果而不用迫使開發者每次等待事件之前都必須重新準備要被偵聽的文件描述符集合,另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。epoll除了提供select/poll那種IO事件的電平觸發(Level Triggered)外,還提供了邊沿觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。

    2優點

    支持一個進程打開大數目的socket描述符

    select 最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設置,默認值是1024。對于那些需要支持的上萬連接數目的IM服務器來說顯然太少了。這時候你一是可以選擇修改這個宏然后重新編譯內核,不過資料也同時指出這樣會帶來網絡效率的下降,二是可以選擇多進程的解決方案(傳統的Apache方案),不過雖然linux上面創建進程的代價比較小,但仍舊是不可忽視的,加上進程間數據同步遠比不上線程間同步的高效,所以也不是一種完美的方案。不過 epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大于2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。

    IO效率不隨FD數目增加而線性下降

    傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由于網絡延時,任一時間只有部分的socket是“活躍”的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對“活躍”的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。那么,只有“活躍”的socket才會主動的去調用 callback函數,其他idle狀態socket則不會,在這點上,epoll實現了一個“偽”AIO,因為這時候推動力在os內核。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll并不比select/poll有什么效率,相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

    使用mmap加速內核與用戶空間的消息傳遞

    這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核于用戶空間mmap同一塊內存實現的。而如果你像我一樣從2.5內核就關注epoll的話,一定不會忘記手工 mmap這一步的。

    3內核微調

    這一點其實不算epoll的優點了,而是整個linux平臺的優點。也許你可以懷疑linux平臺,但是你無法回避linux平臺賦予你微調內核的能力。比如,內核TCP/IP協議棧使用內存池管理sk_buff結構,那么可以在運行時期動態調整這個內存pool(skb_head_pool)的大小--- 通過echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數的第2個參數(TCP完成3次握手的數據包隊列長度),也可以根據你平臺內存大小動態調整。更甚至在一個數據包面數目巨大但同時每個數據包本身大小卻很小的特殊系統上嘗試最新的NAPI網卡驅動架構。

    4使用

    令人高興的是,2.6內核的epoll比其2.5開發版本的/dev/epoll簡潔了許多,所以,大部分情況下,強大的東西往往是簡單的。唯一有點麻煩是epoll有2種工作方式:LT和ET。 LT(level triggered)是缺省的工作方式,并且同時支持block和no-block socket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然后你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表。 ET (edge-triggered)是高速工作方式,只支持no-block socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然后它會假設你知道文件描述符已經就緒,并且不會再為那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少于一定量時導致了一個EWOULDBLOCK 錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once),不過在TCP協議中,ET模式的加速效用仍需要更多的benchmark確認。 ET和LT的區別就在這里體現,LT事件不會丟棄,而是只要讀buffer里面有數據可以讓用戶讀,則不斷的通知你。而ET則只在事件發生之時通知。可以簡單理解為LT是水平觸發,而ET則為邊緣觸發。LT模式只要有事件未處理就會觸發,而ET則只在高低電平變換時(即狀態從1到0或者0到1)觸發。[1]

    5系統調用

    epoll相關的系統調用有:epoll_create, epoll_ctl和epoll_wait。Linux-2.6.19又引入了可以屏蔽指定信號的epoll_wait: epoll_pwait。至此epoll家族已全。其中epoll_create用來創建一個epoll文件描述符,epoll_ctl用來添加/修改/刪除需要偵聽的文件描述符及其事件,epoll_wait/epoll_pwait接收發生在被偵聽的描述符上的,用戶感興趣的IO事件。epoll文件描述符用完后,直接用close關閉即可,非常方便。事實上,任何被偵聽的文件符只要其被關閉,那么它也會自動從被偵聽的文件描述符集合中刪除,很是智能。 每次添加/修改/刪除被偵聽文件描述符都需要調用epoll_ctl,所以要盡量少地調用epoll_ctl,防止其所引來的開銷抵消其帶來的好處。有的時候,應用中可能存在大量的短連接(比如說Web服務器),epoll_ctl將被頻繁地調用,可能成為這個系統的瓶頸。 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將可以大大的提高你的網絡服務器的支持人數。 首先通過epoll_create(int maxfds)來創建一個epoll的句柄,其中maxfds為你epoll所支持的最大句柄數。這個函數會返回一個新的epoll句柄,之后的所有操作將通過這個句柄來進行操作。在用完之后,記得用close()來關閉這個創建出來的epoll句柄。 之后在你的網絡主循環里面,每一幀的調用epoll_wait(int epfd, epoll_event events, int max events, int timeout)來查詢所有的網絡接口,看哪一個可以讀,哪一個可以寫了。基本的語法為:
    1 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范圍之后應該是一個循環,遍歷所有的事件:
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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 if (event[n].events & EPOLLIN) {? // 如果是已經連接的用戶,并且收到數據, ?????????????????????????????????????????????// 那么進行讀入 ????????int sockfd_r; ????????if ((sockfd_r = event[n].data.fd) < 0) ????????????continue; ????????read(sockfd_r, buffer, MAXSIZE); ????????// 修改sockfd_r上要處理的事件為EPOLLOUT ????????ev.data.fd = sockfd_r; ????????ev.events = EPOLLOUT | EPOLLET; ????????epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd_r, &ev) ????} else if (event[n].events & EPOLLOUT) { // 如果有數據發送 ????????int sockfd_w = events[n].data.fd; ????????write(sockfd_w, buffer, sizeof(buffer)); ????????// 修改sockfd_w上要處理的事件為EPOLLIN ????????ev.data.fd = sockfd_w; ????????ev.events = EPOLLIN | EPOLLET; ????????epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd_r, &ev) ????} ????do_use_fd(events[n].data.fd); }
    對,epoll的操作就這么簡單,總共不過4個API:epoll_create, epoll_ctl, epoll_wait和close。 如果您對epoll的效率還不太了解,請參考之前關于網絡游戲的網絡編程等相關的文章。

    轉載于:https://www.cnblogs.com/ccccccccc/p/3407707.html

    總結

    以上是生活随笔為你收集整理的百度epoll的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。