中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

微教育云平臺網(wǎng)站建設長沙網(wǎng)站關(guān)鍵詞推廣

微教育云平臺網(wǎng)站建設,長沙網(wǎng)站關(guān)鍵詞推廣,深圳有哪些網(wǎng)站是做餐飲沙龍的,wordpress中英文模板前言 epoll的觸發(fā)模式是個引發(fā)討論非常多的話題,網(wǎng)絡上這方面總結(jié)的文章也很多,首先從名字上就不是很統(tǒng)一,LT模式常被稱為水平觸發(fā)、電平觸發(fā)、條件觸發(fā),而ET模式常被稱為邊緣觸發(fā)、邊沿觸發(fā)等,這些都是從英文翻譯過來…

前言

epoll的觸發(fā)模式是個引發(fā)討論非常多的話題,網(wǎng)絡上這方面總結(jié)的文章也很多,首先從名字上就不是很統(tǒng)一,LT模式常被稱為水平觸發(fā)、電平觸發(fā)、條件觸發(fā),而ET模式常被稱為邊緣觸發(fā)、邊沿觸發(fā)等,這些都是從英文翻譯過來的,只不過翻譯的時候有些差異,LT全稱 level-triggered,ET全稱 edge-triggered。

雖然這個知識點熱度很高,但很多人對于它的理解總是差那么一點,特別是在面試的時候,很多面試者總是處于一種回憶和背誦的狀態(tài),其實這兩種模式真的不需要去死記硬背,下面說說我個人對這兩種模式的理解和記憶方法。

名稱的記憶

每次提到ET(邊沿觸發(fā))首先映入我腦海的是大學里《數(shù)字邏輯電路》這門課程,里面會提到低電平、高電平,當電平從低到高時會有一個上升沿,而電平從高到低時會有一個下降沿,這個“沿”就是邊沿觸發(fā)時提到的“邊沿”,跟馬路邊的馬路牙子是同一種概念,也就是指狀態(tài)變化的時候。提起上升沿和下降沿我還是印象很深的,當時我可是占用了好幾節(jié)課的時間用Verilog語言寫了一個顯示“HELLO WORLD”的仿真波形,依靠的就是電平變化中的“沿”。

狀態(tài)變化

LT模式和ET模式可以類比電平變化來學習,但是在實際應用中概念卻不是完全一樣的,在epoll的應用中涉及到關(guān)于IO的讀寫,而讀寫的狀態(tài)變化有哪些呢?可讀、不可讀、可寫、不可寫,其實就是這四種狀態(tài)而已,以socket為例。

可讀:socket上有數(shù)據(jù)

不可讀:socket上沒有數(shù)據(jù)了

可寫:socket上有空間可寫

不可寫:socket上無空間可寫

對于水平觸發(fā)模式,一個事件只要有,就會一直觸發(fā)。
對于邊緣觸發(fā)模式,只有一個事件從無到有才會觸發(fā)。

LT模式

對于讀事件?EPOLLIN,只要socket上有未讀完的數(shù)據(jù),EPOLLIN?就會一直觸發(fā);對于寫事件?EPOLLOUT,只要socket可寫(一說指的是 TCP 窗口一直不飽和,我覺得是TCP緩沖區(qū)未滿時,這一點還需驗證),EPOLLOUT?就會一直觸發(fā)。

在這種模式下,大家會認為讀數(shù)據(jù)會簡單一些,因為即使數(shù)據(jù)沒有讀完,那么下次調(diào)用epoll_wait()時,它還會通知你在上沒讀完的文件描述符上繼續(xù)讀,也就是人們常說的這種模式不用擔心會丟失數(shù)據(jù)。

而寫數(shù)據(jù)時,因為使用 LT 模式會一直觸發(fā) EPOLLOUT 事件,那么如果代碼實現(xiàn)依賴于可寫事件觸發(fā)去發(fā)送數(shù)據(jù),一定要在數(shù)據(jù)發(fā)送完之后移除檢測可寫事件,避免沒有數(shù)據(jù)發(fā)送時無意義的觸發(fā)。

ET模式

對于讀事件?EPOLLIN,只有socket上的數(shù)據(jù)從無到有,EPOLLIN?才會觸發(fā);對于寫事件?EPOLLOUT,只有在socket寫緩沖區(qū)從不可寫變?yōu)榭蓪?#xff0c;EPOLLOUT?才會觸發(fā)(剛剛添加事件完成調(diào)用epoll_wait時或者緩沖區(qū)從滿到不滿)

這種模式聽起來清爽了很多,只有狀態(tài)變化時才會通知,通知的次數(shù)少了自然也會引發(fā)一些問題,比如觸發(fā)讀事件后必須把數(shù)據(jù)收取干凈,因為你不一定有下一次機會再收取數(shù)據(jù)了,即使不采用一次讀取干凈的方式,也要把這個激活狀態(tài)記下來,后續(xù)接著處理,否則如果數(shù)據(jù)殘留到下一次消息來到時就會造成延遲現(xiàn)象。

這種模式下寫事件觸發(fā)后,后續(xù)就不會再觸發(fā)了,如果還需要下一次的寫事件觸發(fā)來驅(qū)動發(fā)送數(shù)據(jù),就需要再次注冊一次檢測可寫事件。

數(shù)據(jù)的讀取和發(fā)送

關(guān)于數(shù)據(jù)的讀比較好理解,無論是LT模式還是ET模式,監(jiān)聽到讀事件從socket開始讀數(shù)據(jù)就好了,只不過讀的邏輯有些差異,LT模式下,讀事件觸發(fā)后,可以按需收取想要的字節(jié)數(shù),不用把本次接收到的數(shù)據(jù)收取干凈,ET模式下,讀事件觸發(fā)后通常需要數(shù)據(jù)一次性收取干凈。

而數(shù)據(jù)的寫不太容易理解,因為數(shù)據(jù)的讀是對端發(fā)來數(shù)據(jù)導致的,而數(shù)據(jù)的寫其實是自己的邏輯層觸發(fā)的,所以在通過網(wǎng)絡發(fā)數(shù)據(jù)時通常都不會去注冊監(jiān)可寫事件,一般都是調(diào)用 send 或者 write 函數(shù)直接發(fā)送,如果發(fā)送過程中, 函數(shù)返回 -1,并且錯誤碼是 EWOULDBLOCK 表明發(fā)送失敗,此時才會注冊監(jiān)聽可寫事件,并將剩余的服務存入自定義的發(fā)送緩沖區(qū)中,等可寫事件觸發(fā)后再接著將發(fā)送緩沖區(qū)中剩余的數(shù)據(jù)發(fā)送出去。

相關(guān)視頻推薦

全網(wǎng)最詳細epoll講解,6種epoll的設計,讓你吊打面試官

epoll實戰(zhàn)揭秘-支撐億級IO的底層基石

epoll的具體實現(xiàn)與epoll線程安全,互斥鎖、自旋鎖、原子操作、CAS

免費學習地址:c/c++ linux服務器開發(fā)/后臺架構(gòu)師

需要C/C++ Linux服務器架構(gòu)師學習資料加qun812855908獲取(資料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享

代碼實踐

基礎代碼

以下為一個epoll觸發(fā)模式測試的基礎代碼,也不算太長,直接拿來就可以測試:

#include <sys/socket.h> //for socket
#include <arpa/inet.h>  //for htonl htons
#include <sys/epoll.h>  //for epoll_ctl
#include <unistd.h>     //for close
#include <fcntl.h>      //for fcntl
#include <errno.h>      //for errno
#include <iostream>     //for coutclass fd_object
{
public:fd_object(int fd) { listen_fd = fd; }~fd_object() { close(listen_fd); }
private:int listen_fd;
};/*
./epoll for lt mode
and
./epoll 1 for et mode
*/
int main(int argc, char* argv[])
{//create a socket fdint listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1){std::cout << "create listen socket fd error." << std::endl;return -1;}fd_object obj(listen_fd);//set socket to non-blockint socket_flag = fcntl(listen_fd, F_GETFL, 0);socket_flag |= O_NONBLOCK;if (fcntl(listen_fd, F_SETFL, socket_flag) == -1){std::cout << "set listen fd to nonblock error." << std::endl;return -1;}//init server bind infoint port = 51741;struct sockaddr_in bind_addr;bind_addr.sin_family = AF_INET;bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind_addr.sin_port = htons(port);if (bind(listen_fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == -1){std::cout << "bind listen socket fd error." << std::endl;return -1;}//start listenif (listen(listen_fd, SOMAXCONN) == -1){std::cout << "listen error." << std::endl;return -1;}elsestd::cout << "start server at port [" << port << "] with [" << (argc <= 1 ? "LT" : "ET") << "] mode." << std::endl;//create a epoll fdint epoll_fd = epoll_create(88);if (epoll_fd == -1){std::cout << "create a epoll fd error." << std::endl;return -1;}epoll_event listen_fd_event;listen_fd_event.data.fd = listen_fd;listen_fd_event.events = EPOLLIN;if (argc > 1) listen_fd_event.events |= EPOLLET;//add epoll event for listen fdif (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &listen_fd_event) == -1){std::cout << "epoll ctl error." << std::endl;return -1;}while (true){epoll_event epoll_events[1024];int n = epoll_wait(epoll_fd, epoll_events, 1024, 1000);if (n < 0)break;else if (n == 0) //timeoutcontinue;for (int i = 0; i < n; ++i){if (epoll_events[i].events & EPOLLIN)//trigger read event{if (epoll_events[i].data.fd == listen_fd){//accept a new connectionstruct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len);if (client_fd == -1)continue;socket_flag = fcntl(client_fd, F_GETFL, 0);socket_flag |= O_NONBLOCK;if (fcntl(client_fd, F_SETFL, socket_flag) == -1){close(client_fd);std::cout << "set client fd to non-block error." << std::endl;continue;}epoll_event client_fd_event;client_fd_event.data.fd = client_fd;client_fd_event.events = EPOLLIN | EPOLLOUT;if (argc > 1) client_fd_event.events |= EPOLLET;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_fd_event) == -1){std::cout << "add client fd to epoll fd error." << std::endl;close(client_fd);continue;}std::cout << "accept a new client fd [" << client_fd << "]." << std::endl;}else{std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;char recvbuf[1024] = { 0 };int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggeredif (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR)){if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." <<  std::endl;close(epoll_events[i].data.fd);}std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;}}else if (epoll_events[i].events & EPOLLOUT){if (epoll_events[i].data.fd == listen_fd) //trigger write eventcontinue;std::cout << "EPOLLOUT event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;}}}return 0;
}

簡單說下這段代碼的測試方法,可以使用?g++ testepoll.cpp -o epoll?進行編譯,編譯后通過?./epoll?運行為LT模式,通過?./epoll et模式運行為ET模式,我們用編譯好的epoll程序作為服務器,使用nc命令來模擬一個客戶端。

測試分類

1.編譯后直接./epoll,然后在另一個命令行窗口用 nc -v 127.0.0.1 51741 命令模擬一次連接,此時 ./epoll 會產(chǎn)生大量的 EPOLLOUT event triggered for client fd ...,那是因為在LT模式下,EPOLLOUT會被一直觸發(fā)。

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll
start server at port [51741] with [LT] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
...

2.注釋包含 EPOLLOUT event triggered for client fd 輸出內(nèi)容的第152行代碼,編譯后 ./epoll運行,然后在另一個命令行窗口用 nc -v 127.0.0.1 51741 模擬一次連接后,輸入abcd回車,可以看到服務器./epoll輸出內(nèi)容,EPOLLIN被觸發(fā)多次,每次讀取一個字節(jié)。

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll
start server at port [51741] with [LT] mode.
accept a new client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [b].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [c].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [d].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].

3.還原剛才注釋的那行代碼,編譯后執(zhí)行?./epoll et?啟動服務器,然后在另一個命令行窗口用?nc -v 127.0.0.1 51741?模擬一次連接后,然后在另一個命令行窗口用?nc -v 127.0.0.1 51741?模擬一次連接,服務器窗口顯示觸發(fā)了EPOLLOUT事件

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].

在此基礎上,從剛剛運行nc命令的窗口中輸入回車、輸入回車、輸出回車,那么epoll服務器窗口看到的是觸發(fā)了三次EPOLLIN事件,每次收到一個回車:

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].

但是如果在nc模擬的客戶端里輸出abcd回車,那么在epoll服務器窗口觸發(fā)一次EPOLLIN事件接收到一個a之后便再也不會觸發(fā)EPOLLIN了,即使你在nc客戶端在此輸入也沒有用,那是因為在接受的緩沖區(qū)中一直還有數(shù)據(jù),新數(shù)據(jù)來時沒有出現(xiàn)緩沖區(qū)從空到有數(shù)據(jù)的情況,所以在ET模式下也注意這種情況。

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].

怎么解決ET觸發(fā)了一次就不再觸發(fā)了

改代碼唄,ET模式在連接后觸發(fā)一次EPOLLOUT,接收到數(shù)據(jù)時觸發(fā)一次EPOLLIN,如果數(shù)據(jù)沒收完,以后這兩個事件就再也不會被觸發(fā)了,要想改變這種情況可以再次注冊一下這兩個事件,時機可以選擇接收到數(shù)據(jù)的時候,所以可以修改這部分代碼:

else
{std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;char recvbuf[1024] = { 0 };int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggeredif (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR)){if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." <<  std::endl;close(epoll_events[i].data.fd);}std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
}

添加再次注冊的邏輯:

else
{std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;char recvbuf[1024] = { 0 };int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggeredif (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR)){if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." <<  std::endl;close(epoll_events[i].data.fd);}epoll_event client_fd_event;client_fd_event.data.fd = epoll_events[i].data.fd;client_fd_event.events = EPOLLIN | EPOLLOUT;if (argc > 1) client_fd_event.events |= EPOLLET;epoll_ctl(epoll_fd, EPOLL_CTL_MOD, epoll_events[i].data.fd, &client_fd_event);std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
}

這次以./epoll et方式啟動服務器,使用nc -v 127.0.0.1 51741模擬客戶端,輸入abc回車發(fā)現(xiàn),epoll服務器輸出顯示觸發(fā)的事件變了:

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [b].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [c].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLOUT event triggered for client fd [5].

總結(jié)

  • LT模式會一直觸發(fā)EPOLLOUT,當緩沖區(qū)有數(shù)據(jù)時會一直觸發(fā)EPOLLIN
  • ET模式會在連接建立后觸發(fā)一次EPOLLOUT,當收到數(shù)據(jù)時會觸發(fā)一次EPOLLIN
  • LT模式觸發(fā)EPOLLIN時可以按需讀取數(shù)據(jù),殘留了數(shù)據(jù)還會再次通知讀取
  • ET模式觸發(fā)EPOLLIN時必須把數(shù)據(jù)讀取完,否則即使來了新的數(shù)據(jù)也不會再次通知了
  • LT模式的EPOLLOUT會一直觸發(fā),所以發(fā)送完數(shù)據(jù)記得刪除,否則會產(chǎn)生大量不必要的通知
  • ET模式的EPOLLOUT事件若數(shù)據(jù)未發(fā)送完需再次注冊,否則不會再有發(fā)送的機會
  • 通常發(fā)送網(wǎng)絡數(shù)據(jù)時不會依賴EPOLLOUT事件,只有在緩沖區(qū)滿發(fā)送失敗時會注冊這個事件,期待被通知后再次發(fā)送

?

http://www.risenshineclean.com/news/54146.html

相關(guān)文章:

  • wordpress置頂文章不生效百度關(guān)鍵詞自然排名優(yōu)化公司
  • 重慶微信網(wǎng)站制作湖南企業(yè)網(wǎng)站建設
  • 叫人開發(fā)網(wǎng)站注意事項競價推廣托管公司價格
  • 如何創(chuàng)辦視頻網(wǎng)站媒體平臺
  • 寧波網(wǎng)站建設營銷推廣搜索引擎營銷案例分析
  • 陽江網(wǎng)站關(guān)鍵字優(yōu)化網(wǎng)游推廣
  • 信陽交友網(wǎng)站開發(fā)公司軟件開發(fā)培訓機構(gòu)
  • 南陽專業(yè)做網(wǎng)站福州seo管理
  • 怎么做網(wǎng)站教程視頻商城全網(wǎng)推廣運營公司
  • app源碼開發(fā)公司英文網(wǎng)站seo
  • 常寧網(wǎng)站建設去了外包簡歷就毀了嗎
  • 中企動力做網(wǎng)站多少錢北京百度seo排名點擊軟件
  • 任何人任意做網(wǎng)站銷售產(chǎn)品違法嗎什么軟件可以推廣
  • 做公益網(wǎng)站需要什么資質(zhì)免費網(wǎng)頁模板網(wǎng)站
  • 制作網(wǎng)站誰家做的好合肥百度seo代理
  • 湖南衡陽網(wǎng)站建設怎么樣優(yōu)化網(wǎng)站seo
  • 天津地鐵建設網(wǎng)站網(wǎng)站鏈接提交
  • 網(wǎng)架加工入門基礎知識網(wǎng)站關(guān)鍵詞排名seo
  • 江門網(wǎng)站建設開發(fā)建立一個企業(yè)網(wǎng)站需要多少錢
  • 專門做諾麗果的網(wǎng)站百度基木魚建站
  • 榆中建設局網(wǎng)站站長工具查詢域名
  • 怎么從網(wǎng)站上看出做網(wǎng)站的日期網(wǎng)頁快速收錄
  • 企業(yè)網(wǎng)站建設方案書范文關(guān)鍵詞挖掘工具有哪些
  • 技術(shù)專業(yè)網(wǎng)站建設免費的關(guān)鍵詞挖掘工具
  • python前端開發(fā)北京網(wǎng)站seo哪家公司好
  • 保存的密碼變成亂碼搜索引擎優(yōu)化叫什么
  • 網(wǎng)站互動功能網(wǎng)站推廣應該怎么做?
  • 上海新聞網(wǎng)頭條新聞seo常用分析的專業(yè)工具
  • 微信如何建商城網(wǎng)站百度投放廣告怎么收費
  • 杭州做網(wǎng)站外包公司哪家好優(yōu)化seo網(wǎng)站