epoll也是實現i/o多路復用的一種方法,為了深入了解epoll的原理,我們先來看下epoll水平觸發(level trigger,lt,lt為epoll的預設工作模式)與邊緣觸發(edge trigger,et)兩種工作模式。
使用脈衝訊號來解釋lt和et可能更加貼切。level是指訊號只需要處於水平,就一直會觸發;而edge則是指訊號為上公升沿或者下降沿時觸發。說得還有點玄乎,我們以生活中的乙個例子來模擬lt和et是如何確定讀操作是否就緒的。
水平觸發
兒子:媽媽,我收到了500元的壓歲錢。
媽媽:嗯,省著點花。
兒子:媽媽,我今天花了200元買了個變形金剛。
媽媽:以後不要亂花錢。
兒子:媽媽,我今天買了好多好吃的,還剩下100元。
媽媽:用完了這些錢,我可不會再給你錢了。
兒子:媽媽,那100元我沒花,我攢起來了
媽媽:這才是明智的做法!
兒子:媽媽,那100元我還沒花,我還有錢的。
媽媽:嗯,繼續保持。
兒子:媽媽,我還有100元錢。
媽媽:…
接下來的情形就是沒完沒了了:只要兒子一直有錢,他就一直會向他的媽媽匯報。lt模式下,只要核心緩衝區中還有未讀資料,就會一直返回描述符的就緒狀態,即不斷地喚醒應用程序。在上面的例子中,兒子是緩衝區,錢是資料,媽媽則是應用程序了解兒子的壓歲錢狀況(讀操作)。
邊緣觸發
兒子:媽媽,我收到了500元的壓歲錢。
媽媽:嗯,省著點花。
(兒子使用壓歲錢購買了變形金剛和零食。)
兒子:
媽媽:兒子你倒是說話啊?壓歲錢呢?
這個就是et模式,兒子只在第一次收到壓歲錢時通知媽媽,接下來兒子怎麼把壓歲錢花掉並沒有通知媽媽。即兒子從沒錢變成有錢,需要通知媽媽,接下來錢變少了,則不會再通知媽媽了。在et模式下, 緩衝區從不可讀變成可讀,會喚醒應用程序,緩衝區資料變少的情況,則不會再喚醒應用程序。
我們再詳細說明lt和et兩種模式下對讀寫操作是否就緒的判斷。
條件觸發:
只要輸入緩衝有資料就會一直觸發事件,直到緩衝區沒有資料。
1. 對於讀操作
只要緩衝內容不為空,lt模式返回讀就緒。
2. 對於寫操作
只要緩衝區還不滿,lt模式會返回寫就緒。
邊緣觸發:
1. 對於讀操作
(1)當緩衝區由不可讀變為可讀的時候,即緩衝區由空變為不空的時候。
(2)當有新資料到達時,即緩衝區中的待讀資料變多的時候。
(3)當緩衝區有資料可讀,且應用程序對相應的描述符進行epoll_ctl_mod 修改epollin事件時。
2. 對於寫操作
(1)當緩衝區由不可寫變為可寫時。
(2)當有舊資料被傳送走,即緩衝區中的內容變少的時候。
(3)當緩衝區有空間可寫,且應用程序對相應的描述符進行epoll_ctl_mod 修改epollout事件時。
#include #include #include int main()}}
}
我們輸入了一些資料到 stdin_fileno(標準輸入)中,但是沒有讀取資料。所以會一直觸發事件,就會一直列印hello world。
執行結果:
$ ./a.out
aaa //自己輸入的資料
hello world
hello world
hello world
hello world
hello world
^c
#include #include #include int main()}}
}
只有緩衝區發生變化才會觸發。
執行結果:
$ ./a.out
123 //自己輸入的資料
hello world
123hello world
123hello world
邊緣觸發方式下,以阻塞方式工作的read & write函式有可能引起伺服器端的長時間停頓。因此,邊緣觸發方式中一定要採用非阻塞read&
write函式。
#include #include #include #include int main()}}
}
執行結果:和條件觸發一樣
$ ./a.out
aaa //自己輸入的資料
hello world
hello world
hello world
hello world
hello world
^c
非阻塞邊緣觸發伺服器**:執行結果:
$ ./ser 9190
return epoll_wait
connected client: 5
return epoll_wait
return epoll_wait
return epoll_wait
return epoll_wait
return epoll_wait
closed client: 5
$ ./cli 192.168.1.141 9190
input message(q to quit) : asdfasdf
message from server: asdfasdf
input message(q to quit) : asdfasf
message from server: asdfasf
input message(q to quit) : sdfasdf
message from server: sdfasdf
input message(q to quit) : asdfasd
message from server: asdfasd
input message(q to quit) : ^c
伺服器:
#include #include #include #include #include #include #include #include #include #define buf_size 4
#define epoll_size 50
void setnonblockingmode(int fd);
void error_handling(char *buf);
int main(int argc, char *ar**)
serv_sock=socket(pf_inet, sock_stream, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=af_inet;
serv_adr.sin_addr.s_addr=htonl(inaddr_any);
serv_adr.sin_port=htons(atoi(ar**[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
epfd=epoll_create(epoll_size);
ep_events=(struct epoll_event*)malloc(sizeof(struct epoll_event)*epoll_size);
setnonblockingmode(serv_sock);
event.events=epollin;
event.data.fd=serv_sock;
epoll_ctl(epfd, epoll_ctl_add, serv_sock, &event);
while(1)
puts("return epoll_wait");
for(i=0; i客服端:
#include #include #include #include #include #include #define buf_size 1024
void error_handling(char *message);
int main(int argc, char* ar**)
sock = socket(pf_inet, sock_stream, 0);
if(sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=af_inet;
serv_addr.sin_addr.s_addr=inet_addr(ar**[1]);
serv_addr.sin_port=htons(atoi(ar**[2]));
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("connect() error i");
while(1)
close(sock);
return 0;
}void error_handling(char *message)
epoll的水平觸發和邊緣觸發
即level triggered and edge triggered 簡單說來 level triggered 只要事件是處於就緒狀態,你每詢問一次,就告訴你一次它處於就緒狀態。edge triggered 只有在狀態改變的時候告訴你,這也是為什麼et模式下,建議用非阻塞的socket。下面我們來...
epoll的水平觸發和邊緣觸發
水平觸發 level triggered 當滿足條件時,觸發.select和poll採用該方式 邊緣觸發 edge triggered 當狀態變化時,觸發.訊號驅動 signal driven i o 採用該方式 過程 當資料到來的時候,觸發器滿足條件,傳送通知,程序接收到通知後,請求核心讀取資料....
邊緣觸發和水平(條件)觸發
條件觸發 lt 和邊緣觸發 et 的區別在於事件的時間點 邊緣觸發 每當狀態變化時發生乙個io事件 條件觸發 只要滿足條件就發生乙個io事件 在條件觸發方式中,只要輸入緩衝有資料就會一直通知該事件。例如 伺服器輸入緩衝收到50位元組的資料時,伺服器端作業系統將通知該事件 註冊到發生變化的檔案描述符 ...