epoll三種工作模式
水平觸發模式 LT
之前僞代碼的問題。
假設有100個數據,但是某次讀只能讀50個字節。下一次再讀50個字節。
- 只要fd對應的緩衝區有數據,即判斷緩衝區
- epoll_wait返回
- 返回的次數與發送的數據次數沒有關係
用戶雖然把數據收下來了,但是可能不一定知道epoll_wait返回了幾次。
假設設置的讀緩衝區比較小,就會出現上述分兩次讀的情況。
- 打個比方:老師告訴你要做作業,“每次”都會提醒你下,你語文要做了,數學要做了,英語要做了。這裏的老師就好比內核,會督促你把作業都做完。
- 和邊沿模式不同,老師就提醒你一次,語文要做哦。你只做了成語判斷,還有閱讀分析沒做,但是她不會管你。一會兒可能數學作業要佈置下來了,你只做了選擇題,大題可能沒做。
- LT支持block和非block方式。這種模式,內核會告訴你一個fd是否就緒,如果你不做任何操作,內核還是會繼續通知你,所以這種模式編程出錯可能性要小一點。
邊沿觸發模式-ET
- fd --默認具有阻塞屬性
- Client Send Data to Server
- 發一次數據,server的epoll_wait返回一次
- 不再乎數據是否讀完,客戶端發一次,就返回一次
- “絞肉機”裏面剩下的肉需要新的肉塞進去才能出來
- 貌似不合理卻合理:epoll_wait調用次數越多,系統開銷越大。一般不單獨使用
- 存在即合理:epoll_wait調用次數越多,系統的開銷越大。
- 不單獨用,配合非阻塞方式
- 如果讀不完
- while(recv()); 內核緩衝區沒有數據,數據讀完之後會阻塞
- 解決阻塞問題
- 設置非阻塞 -fd (邊沿非阻塞模式)
//修改監聽的方式
tmp.events = EPOLL_IN | EPOLL_ET; //只增加一個EPOLL_ET宏
tmp.events.cfd = connfd; //accept進來的描述符
邊沿非阻塞觸發 ET+NO-BLOCK
- 效率高,高速工作方式,只支持no-block socket。當fd從not ready–>ready時,內核通過epoll通知你。然後假設你已知曉該事件,並不會再爲那個fd提供更多的就緒通知。如果一直不對這個fd做I/O操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once)
- 如何解決非阻塞
- open()
- 設置flags
- 必須 O_WDRW | O_NOBLOCK //變成非阻塞
- 適應終端文件: /dev/tty
- fcntl
- fcnt方式設置cfd非阻塞模式
- open()
//關鍵:設置cfd爲非阻塞模式
int flag = fcntl(cfd, F_GETFL);
flag | = O_NOBLOCK;
fcntl(cfd, F_SETFL, flag);
struct epoll_event tmp_evt;
tmp_evt.event = EPOLLIN|EPOLLET;
tmp_evt.data.fd = cfd; //accept返回後的描述符
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tmp_evt);
/*... ... ...*/
//循環讀取數據
while((recv_len = recv(fd, buf, sizeof(buf), 0))>0)
{
write(STDOUT_FILENO, buf, recv_len ); //打印到終端
send(fd, buf, len ,0); //返回給客戶端
}
if(len == 0)
{
printf("客戶端斷開連接\n");
epoll_ctl(expd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
}else if(len = -1){
printf("error\n");
exit(1);
}
上述代碼在讀完數據後會退出:
- 原因
- 數據讀完之後,又去讀,但是現在是非阻塞情況,又去強行讀緩衝區
- 強行讀了一個沒有數據的緩衝區(fd),導致返回-1
- 判斷 error == EAGAIN
if(len == -1)
{
if(error == EAGAIN)
{
printf("緩衝區數據已讀完");
}
else
{
printf("error\n");
exit(1);
}
}
文件描述符突破1024限制
- select 需要編譯內核才能突破1024限制
- poll和epoll 可以突破1024
- poll 內部鏈表
- epoll紅黑樹
- 查看文件描述符上線
- cat /proc/sys/fs/file-max
- 和機器硬件相關
- 通過配置文件修改上限值
- /etc/security/limits.conf
- soft nofile 8000 //不能超過limits上限
- hard nofile 8000
- 重啓或註銷才生效
- ulimit -n 3000
epoll反應堆模型
在struct epoll_event結構體中的
struct epoll_event{
__uint32_t events; /*Epoll events*/
epoll_data_t data; /*User data variable*/
}
typedef union epoll_data{
void *ptr;
int fd; //epoll_wait返回後利用response_event[i].data.fd來判斷某個fd就緒
uint32_t u32;
uint64_t u64;
}epoll_data_t;
//fake code-----------------------------------------------------------------------
rets=epoll_wait(gefd, response_events, max_events, -1);
for(int i=0;i<rets;i++)
{
...
response_events.data.fd = connfd; //利用的epoll_event.data.fd來幫助我們判斷,
...
}