日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符串缓冲区太小怎么解决_epoll的两种模式 ET和LT printf的缓冲区问题 边缘非阻塞模式...

發布時間:2025/3/20 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符串缓冲区太小怎么解决_epoll的两种模式 ET和LT printf的缓冲区问题 边缘非阻塞模式... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

學習于:https://www.bilibili.com/video/av44660437/?p=9

前文:何柄融:多路復用I/O select poll epoll 何柄融:select poll epoll 再一次的了解

這里就不講epoll的基礎了,如果對epoll沒了解的請去前文學習。

當時有:

epoll除了提供select/ poll那種IO事件的電平觸發(Level Triggered) 外,還提供了邊沿觸發(Edge Triggered) ,這就使得用戶空間程序有可能緩存IO狀態,減少epoll_ wait/epoll_ pwait的調用,提高應用程序效率。(這個沒怎么看得懂。。)

所以,特地來學習一下epoll的ET和LT。

epoll的三種工作模式:

1.水平觸發模式(默認):Level Triggered LT

2.邊沿觸發模式 : Edge Triggered ET

3.邊緣非阻塞觸發

先來介紹這個默認使用的水平觸發模式(LT)

特點:1.只要fd對應的緩沖區有數據,epoll_wait就返回

2.返回的次數與發送數據的次數沒有關系。

3.epoll默認的使用模式

看代碼:

核心:關鍵就在于處理已連接描述符時的緩沖區大小。之前我們都是使用1024個字節,而寫的時候也不會太大。 而這里把1024改成了5,也就是說每次處理5個字節。而如果客戶端一次發送過來100個字節,那么就會因為客戶端的這一次發送,服務器這邊要調用20次的epoll_wait(),因為 100個字節發送過來,服務器這邊的已連接描述符這里會有個對應的緩沖區緩沖這100個字節,然后因為緩沖區有數據,所以就會出發epoll_wait()的返回,而返回之后服務器又只從緩沖區接收5個字節來處理。那么就會剩下95個字節。那么當服務器處理完這5個字節后,本來應該阻塞在epollwait這里,不斷的喚醒沉睡的,可是由于緩沖區還有數據,所以又馬上讓epollwait返回,然后再一次處理這個文件描述符的事件。所以,這里就會多次調用epollwait。下面是一個比較形象的圖:

運行代碼的的輸出:

注意到,每次都多了69.123.31,這屬于亂碼,具體解釋如下:

printf函數:有時候會發現打印不出來。因為printf有一個緩沖區,好像是8k,當你打印字符串的時候,如果沒加后面的 "n" ,那么它就有可能因為緩沖區沒有滿而導致不輸出。如果加了后面的“n”,就會強制把字符串從緩沖區中輸出。而有時候,n 也不能強制從緩沖區輸出。(這個緩沖區其實很像我們在文件io這篇文章中提到的8k的緩沖區,機制都是一樣的,緩沖區滿/調用flush/進程結束 使數據刷新,關于這里 https://blog.csdn.net/u013790372/article/details/54645860 這篇文章寫得不錯,大家可以了解一下)

后面改為:

直接向標準輸出寫就ok了。

正確輸出如下:

即多次讀取數據,直到把緩沖區的數據讀完。

而由前面的知識,我們知道epollwait調用次數越多,系統的開銷越大。(總結一下就是:epollwait會進入內核空間檢測內核文件描述符的事件,這里就有cpu的在系統調用時產生的上下文切換,估計還有些我不知道的東西。。)

然后,epoll的水平觸發模式就到這里了。主要就是緩沖區有數據就epollwait返回。

然后來看邊沿觸發模式(ET)

特點:

客戶端給server發數據,發一次數據,server的epoll_wait返回一次,不在乎客戶端的緩沖區的數據是否讀完。(其它客戶端的連接也會導致返回和數據讀取,不要忘了前面的基礎)

也就是說,緩沖區里面可能存有大量的客戶端之前發來的數據。這樣就不太好。

修改后:如下圖:也就是設置了結構里的events。或上 epollet ,這樣就能變為邊沿觸發模式。這里是添加新連接的時候的修改,而代碼前面一開始把監聽集合添加進紅黑樹的時候,也要修改這個events。(此時代碼的buf大小依然是5個字節)

演示:

具體就是:客戶端一次性輸入比較多的字符,服務端連進一個客戶端,就返回一次epollwait,然后讀取一次緩沖區的5個字節。后面客戶端再次發送,還是讀取緩沖區的前5個字節,也就是上次服務器接收了但是沒有從緩沖區刷新出來的數據,而此次發送的數據被放在了緩沖區的最后面,所以沒顯示出來。(這個要理解清楚!)

現在來總結一下兩者的區別:LT是我服務端一次只讀這么多,我可以多次讀你很多數據。消耗系統資源多。 ET是客戶端發送一次或者連接一次我就返回一次,然后慢慢讀。理解了前面的,其實基本上全部說下來都沒關系。

然后我們來看看怎么解決EL讀取的總是緩沖區的舊數據的問題:

下面這張圖片改為while(recv())就可以了,也就是說,只要緩沖區有數據,我就把它全部讀出來。這樣就不會說每次都讀取很少的字節了。

可是這又產生了一個問題: 這樣的話,因為io默認設置是阻塞的啊,也就是說fd默認阻塞屬性啊。那么在while(recv())這里,讀完數據之后,如果沒有數據來的話,就會阻塞在這里。那么就不會繼續返回到外面的while(1)的大循環中了。也就不會再epollwait函數那里阻塞了(也就是沒有辦法委托再去內核檢測了),這就完全把整個程序的功能弄錯了。

所以,我們要解決阻塞問題,所以,必須設置fd為非阻塞。

所以,我們前面介紹其實是邊緣阻塞模式。

接下來我們要介紹邊緣非阻塞模式。

我們要解決的是如何設置fd的非阻塞的屬性。

讓我們先來看看如何設置非阻塞(先脫離前面的問題先)

1.open()函數

它可以設置flags

必須 O_WDRE | O_NONBLOCK

終端文件 : /dev/tty 當前所操作的終端。前面已經講得很多次了。(也就是說,終端文件就可以使用open來設置非阻塞)

比如 fd=open("/dev/tty" ,O_WDRE | O_NONBLOCK); 那么返回正常的話就是返回的fd就是非阻塞的。這個是針對終端而言的。

2. 由于上面的方法針對的是終端,而我們現在要操作的是內核緩沖區。

fcntl :1.復制文件描述符 2.設置文件描述符的屬性為非阻塞。(第二個是我們要用的)

使用流程

int flag=fcntl(fd.F_GETFL); 第一步:獲取文件描述符的flags屬性 get flag

然后我們要把非阻塞屬性給它設置進去

flag |=O_NONBLOCK 修改flag屬性

fcntl(fd,F_SETFL.flags) 此處是吧flag屬性又設置進去

下面是視頻中的文檔:

然后我們來到epoll使用代碼上看怎么改:下面這里我們只修改已連接的文件描述符,不修改前面的監聽描述符了。

然后是接收數據那里:

注意此時的buf還是5個字節!

下面先是len>0的情況,len>0說明此時緩沖區有數據可讀,所以不斷地循環從緩沖區中接收5個字節,然后打印到終端,并且返回給客戶端。

下面是len返回為0和-1的情況。len等于0說明客戶端斷開了連接,此時把此客戶端對應的文件描述符從內核中的紅黑樹移除,然后關閉掉該文件描述符。

len等于-1。此時會有一個errno的量(好像在線程那里看見過),它如果等于EAGAIN,就說明此時是緩沖區數據讀完,那么就跳出這里,回到epollwait中阻塞;如果不等于,那么就是出現異常了,那就退出程序。

成功的返回:

這樣的話,我們就很完美地結束了epoll的邊緣非阻塞的編程。

這樣的話,邊緣非阻塞效率最高。

既沒有LT的多次重復調用epoll_wait的系統開銷大,也沒有ET的讀取到的都是舊數據或者是ET的阻塞。 客戶端一次發送數據,我就一次性把發到緩沖區的數據全部接收并且處理。

簡直perfect。

感覺那個視頻很不錯,最起碼epoll的幾種模式講得很不錯,推薦一下。

如果大家對java的nio有了解的話,我強烈建議來看看我這篇文章: 何柄融:nio基礎和手寫一個基于nio的demo , 然后你就會發現epoll和nio的編程居然是一模一樣的,沒任何差別。

歡迎交流討論。

總結

以上是生活随笔為你收集整理的字符串缓冲区太小怎么解决_epoll的两种模式 ET和LT printf的缓冲区问题 边缘非阻塞模式...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。