條件觸發(lt)和邊緣觸發(et)的區別在於事件的時間點
邊緣觸發:每當狀態變化時發生乙個io事件
條件觸發:只要滿足條件就發生乙個io事件
在條件觸發方式中,只要輸入緩衝有資料就會一直通知該事件。
例如:伺服器輸入緩衝收到50位元組的資料時,伺服器端作業系統將通知該事件(註冊到發生變化的檔案描述符)。但伺服器端讀取20位元組後還剩30位元組的情況下,仍會註冊事件。也就是說,條件觸發方式中,只要輸入緩衝中還剩有資料,就將以事件方式再次註冊。
而邊緣觸發中輸入緩衝收到資料時僅註冊1次該事件。即使輸入緩衝中還留有資料,也不會再進行註冊。
下面是條件觸發的示例**:
#include#include#include#include#include#include#include#define epoll_size 50
#define buf_size 4
void error_handling(char *buf);
int main(int argc,char **argv)
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(argv[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=malloc(sizeof(struct epoll_event)*epoll_size);
event.events=epollin;
event.data.fd=serv_sock;
epoll_ctl(epfd,epoll_ctl_add,serv_sock,&event);
while(1)
puts("epoll_wait() error");
for(i=0;i邊緣觸發的伺服器端實現中必知的兩點
1. 通過errno變數驗證錯誤原因
2. 為了完成非阻塞i/o,更改套接字特性
linux的套接字相關函式一般通過返回-1通知發生了錯誤。雖然知道發生了錯誤,但僅憑這些內容無法得知產生錯誤的原因。因此,為了在發生錯誤時提供額外的資訊,linux宣告了如下全域性變數:
int errno;
為了訪問該變數,需要引入error.h標頭檔案,因為此標頭檔案中有上述變數的extern宣告。另外,每種函式發生錯誤時,儲存到errno變數中的值都不同,沒必要記住所有可能的值。
read函式發現輸入緩衝中沒有資料可讀時返回-1,同時在errno中儲存eagain常量。
下面是將套接字改為非阻塞方式的方法。linux提供更改或讀取檔案屬性的如下方法:
#includeint fcntl(int filedes,int cmd,...);
//成功時返回cmd引數相關值,失敗時返回-1
//filedes---屬性更改目標的檔案描述符
//cmd---表示函式呼叫的目的
fcntl具有可變引數的形式。如果向第二個引數傳遞f_getfl,可以獲得第乙個引數所指的檔案描述符屬性。反之,如果傳遞f_setfl,可以更改檔案描述符屬性。若希望將檔案(套接字)改為非阻塞模式,需要以下兩種語句:
int flag=fcntl(fd,f_getfl,0);
fcntl(fd,f_setfl,flag | o_nonblock);
通過第一條語句獲取之前設定的屬性資訊,通過第二條語句在此基礎上新增非阻塞o_nonblock標誌。呼叫read&write函式時,無論是否存在資料,都會形成非阻塞檔案。
實現邊緣觸發的回聲伺服器端
首先說明為何需要通過errno確認錯誤的原因:
邊緣觸發方式中,接收資料時僅註冊1次該事件
就因為這種特點,一旦發生輸入相關事件,就應該讀取輸入緩衝中的全部資料。因此需要驗證輸入緩衝是否為空。
read函式返回-1,變數errno中的值為eagain時,說明沒有資料可讀。
既然如此,為何需要將套接字變成非阻塞模式?邊緣觸發方式下,以阻塞模式工作的read & write函式有可能引起伺服器端的長時間停頓。因此,邊緣觸發方式一定要採用非阻塞read & write函式。
下面是以邊緣觸發方式工作的回聲伺服器端示例:
#include#include#include#include#include#include#include#include#include#define epoll_size 50
#define buf_size 4
void error_handling(char *buf);
void setnonblockingmode(int fd);
int main(int argc,char **argv)
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(argv[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=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;ielse
else if(str_len<0)
else}}
} }
close(serv_sock);
close(epfd);
return 0;
}void setnonblockingmode(int fd)
void error_handling(char *buf)
epoll 水平觸發 邊緣觸發
水平觸發 只要緩衝區還有資料,核心就還會通知使用者。使用者如果第一次讀取資料沒讀完,即使沒有任何新的操作觸發,還是可以繼續通過epoll wait來獲取事件 邊緣觸發 只有當新事件觸發的時候,才能通過epoll wait來獲取資料,如果第一次讀取資料沒讀完,就只能等待下一次事件觸發來獲取餘下的資料。...
epoll 水平觸發 邊緣觸發
先簡單比較一下level trigger 和 edge trigger 模式的不同。讓我們換乙個角度來理解et模式,事實上,epoll的et模式其實就是socket io完全狀態機。當socket由不可讀變成可讀時,epoll的et模式返回read 事件。對於read 事件,開發者需要保證把讀取緩衝...
水平觸發與邊緣觸發
今天開始封裝c的socket的基礎,在封裝的時候意識到這樣乙個問題,如果我現在fd的接收緩衝區中有2048位元組的資料,但是我唯讀出來1024個位元組的資料,當我下次select的時候 這之間沒有網路資料過來 還會檢測到該select可讀嗎?這樣就引申出來水linux的io多路復用中的水平觸發模式和...