epoll三種工作模式

epoll三種工作模式

水平觸發模式 LT

之前僞代碼的問題。
假設有100個數據,但是某次讀只能讀50個字節。下一次再讀50個字節。

  • 只要fd對應的緩衝區有數據,即判斷緩衝區
  • epoll_wait返回
  • 返回的次數與發送的數據次數沒有關係
clientserver100bytesepoll_wait第1次返50bytesepoll_wait第2次返50bytesclientserver

用戶雖然把數據收下來了,但是可能不一定知道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非阻塞模式
//關鍵:設置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來幫助我們判斷,
	...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章