色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

網絡IO的弊端以及多路復用IO的優勢

開關電源芯片 ? 來源:低并發編程 ? 作者:閃客sun ? 2021-08-25 18:01 ? 次閱讀

為了講多路復用,當然還是要跟風,采用鞭尸的思路,先講講傳統的網絡 IO 的弊端,用拉踩的方式捧起多路復用 IO 的優勢。

為了方便理解,以下所有代碼都是偽代碼,知道其表達的意思即可。

阻塞 IO

服務端為了處理客戶端的連接和請求的數據,寫了如下代碼。

listenfd = socket(); // 打開一個網絡通信端口

bind(listenfd); // 綁定

listen(listenfd); // 監聽while(1) {

connfd = accept(listenfd); // 阻塞建立連接

int n = read(connfd, buf); // 阻塞讀數據

doSomeThing(buf); // 利用讀到的數據做些什么

close(connfd); // 關閉連接,循環等待下一個連接

}

這段代碼會執行得磕磕絆絆,就像這樣。

可以看到,服務端的線程阻塞在了兩個地方,一個是 accept 函數,一個是 read 函數。

如果再把 read 函數的細節展開,我們會發現其阻塞在了兩個階段。

這就是傳統的阻塞 IO。

整體流程如下圖。

4f91990a-e3ff-11eb-a97a-12bb97331649.png

所以,如果這個連接的客戶端一直不發數據,那么服務端線程將會一直阻塞在 read 函數上不返回,也無法接受其他客戶端連接。

這肯定是不行的。

非阻塞 IO

為了解決上面的問題,其關鍵在于改造這個 read 函數。

有一種聰明的辦法是,每次都創建一個新的進程或線程,去調用 read 函數,并做業務處理。

while(1) {

connfd = accept(listenfd); // 阻塞建立連接

pthread_create(doWork); // 創建一個新的線程

}

void doWork() {

int n = read(connfd, buf); // 阻塞讀數據

doSomeThing(buf); // 利用讀到的數據做些什么

close(connfd); // 關閉連接,循環等待下一個連接

}

這樣,當給一個客戶端建立好連接后,就可以立刻等待新的客戶端連接,而不用阻塞在原客戶端的 read 請求上。

不過,這不叫非阻塞 IO,只不過用了多線程的手段使得主線程沒有卡在 read 函數上不往下走罷了。操作系統為我們提供的 read 函數仍然是阻塞的。

所以真正的非阻塞 IO,不能是通過我們用戶層的小把戲,而是要懇請操作系統為我們提供一個非阻塞的 read 函數。

這個 read 函數的效果是,如果沒有數據到達時(到達網卡并拷貝到了內核緩沖區),立刻返回一個錯誤值(-1),而不是阻塞地等待。

操作系統提供了這樣的功能,只需要在調用 read 前,將文件描述符設置為非阻塞即可。

fcntl(connfd, F_SETFL, O_NONBLOCK);

int n = read(connfd, buffer) != SUCCESS);

這樣,就需要用戶線程循環調用 read,直到返回值不為 -1,再開始處理業務。

這里我們注意到一個細節。

非阻塞的 read,指的是在數據到達前,即數據還未到達網卡,或者到達網卡但還沒有拷貝到內核緩沖區之前,這個階段是非阻塞的。

當數據已到達內核緩沖區,此時調用 read 函數仍然是阻塞的,需要等待數據從內核緩沖區拷貝到用戶緩沖區,才能返回。

整體流程如下圖

IO 多路復用

為每個客戶端創建一個線程,服務器端的線程資源很容易被耗光。

當然還有個聰明的辦法,我們可以每 accept 一個客戶端連接后,將這個文件描述符(connfd)放到一個數組里。

fdlist.add(connfd);

然后弄一個新的線程去不斷遍歷這個數組,調用每一個元素的非阻塞 read 方法。

while(1) {

for(fd 《-- fdlist) {

if(read(fd) != -1) {

doSomeThing();

}

}

}

這樣,我們就成功用一個線程處理了多個客戶端連接。

你是不是覺得這有些多路復用的意思?

但這和我們用多線程去將阻塞 IO 改造成看起來是非阻塞 IO 一樣,這種遍歷方式也只是我們用戶自己想出的小把戲,每次遍歷遇到 read 返回 -1 時仍然是一次浪費資源的系統調用。

在 while 循環里做系統調用,就好比你做分布式項目時在 while 里做 rpc 請求一樣,是不劃算的。

所以,還是得懇請操作系統老大,提供給我們一個有這樣效果的函數,我們將一批文件描述符通過一次系統調用傳給內核,由內核層去遍歷,才能真正解決這個問題。

select

select 是操作系統提供的系統調用函數,通過它,我們可以把一個文件描述符的數組發給操作系統, 讓操作系統去遍歷,確定哪個文件描述符可以讀寫, 然后告訴我們去處理:

select系統調用的函數定義如下。

int select(

int nfds,

fd_set *readfds,

fd_set *writefds,

fd_set *exceptfds,

struct timeval *timeout);

// nfds:監控的文件描述符集里最大文件描述符加1// readfds:監控有讀數據到達文件描述符集合,傳入傳出參數// writefds:監控寫數據到達文件描述符集合,傳入傳出參數// exceptfds:監控異常發生達文件描述符集合, 傳入傳出參數// timeout:定時阻塞監控時間,3種情況// 1.NULL,永遠等下去// 2.設置timeval,等待固定時間// 3.設置timeval里時間均為0,檢查描述字后立即返回,輪詢

服務端代碼,這樣來寫。

首先一個線程不斷接受客戶端連接,并把 socket 文件描述符放到一個 list 里。

while(1) {

connfd = accept(listenfd);

fcntl(connfd, F_SETFL, O_NONBLOCK);

fdlist.add(connfd);

}

然后,另一個線程不再自己遍歷,而是調用 select,將這批文件描述符 list 交給操作系統去遍歷。

while(1) {

// 把一堆文件描述符 list 傳給 select 函數

// 有已就緒的文件描述符就返回,nready 表示有多少個就緒的

nready = select(list);

。..

}

不過,當 select 函數返回后,用戶依然需要遍歷剛剛提交給操作系統的 list。

只不過,操作系統會將準備就緒的文件描述符做上標識,用戶層將不會再有無意義的系統調用開銷。

while(1) {

nready = select(list);

// 用戶層依然要遍歷,只不過少了很多無效的系統調用

for(fd 《-- fdlist) {

if(fd != -1) {

// 只讀已就緒的文件描述符

read(fd, buf);

// 總共只有 nready 個已就緒描述符,不用過多遍歷

if(--nready == 0) break;

}

}

}

正如剛剛的動圖中所描述的,其直觀效果如下。(同一個動圖消耗了你兩次流量,氣不氣?)

可以看出幾個細節:

1. select 調用需要傳入 fd 數組,需要拷貝一份到內核,高并發場景下這樣的拷貝消耗的資源是驚人的。(可優化為不復制)2. select 在內核層仍然是通過遍歷的方式檢查文件描述符的就緒狀態,是個同步過程,只不過無系統調用切換上下文的開銷。(內核層可優化為異步事件通知)3. select 僅僅返回可讀文件描述符的個數,具體哪個可讀還是要用戶自己遍歷。(可優化為只返回給用戶就緒的文件描述符,無需用戶做無效的遍歷)

可以看到,這種方式,既做到了一個線程處理多個客戶端連接(文件描述符),又減少了系統調用的開銷(多個文件描述符只有一次 select 的系統調用 + n 次就緒狀態的文件描述符的 read 系統調用)。

poll

poll 也是操作系統提供的系統調用函數。

int poll(struct pollfd *fds, nfds_tnfds, int timeout);

struct pollfd {

intfd; /*文件描述符*/

shortevents; /*監控的事件*/

shortrevents; /*監控事件中滿足條件返回的事件*/

};

它和 select 的主要區別就是,去掉了 select 只能監聽 1024 個文件描述符的限制。

epoll

epoll 是最終的大 boss,它解決了 select 和 poll 的一些問題。

還記得上面說的 select 的三個細節么?

1. select 調用需要傳入 fd 數組,需要拷貝一份到內核,高并發場景下這樣的拷貝消耗的資源是驚人的。(可優化為不復制)2. select 在內核層仍然是通過遍歷的方式檢查文件描述符的就緒狀態,是個同步過程,只不過無系統調用切換上下文的開銷。(內核層可優化為異步事件通知)3. select 僅僅返回可讀文件描述符的個數,具體哪個可讀還是要用戶自己遍歷。(可優化為只返回給用戶就緒的文件描述符,無需用戶做無效的遍歷)

所以 epoll 主要就是針對這三點進行了改進。

1. 內核中保存一份文件描述符集合,無需用戶每次都重新傳入,只需告訴內核修改的部分即可。2. 內核不再通過輪詢的方式找到就緒的文件描述符,而是通過異步 IO 事件喚醒。3. 內核僅會將有 IO 事件的文件描述符返回給用戶,用戶也無需遍歷整個文件描述符集合。

具體,操作系統提供了這三個函數。

第一步,創建一個 epoll 句柄

int epoll_create(int size);

第二步,向內核添加、修改或刪除要監控的文件描述符。

int epoll_ctl(

int epfd, int op, int fd, struct epoll_event *event);

第三步,類似發起了 select() 調用

int epoll_wait(

int epfd, struct epoll_event *events, int max events, int timeout);

使用起來,其內部原理就像如下一般絲滑。

如果你想繼續深入了解 epoll 的底層原理,推薦閱讀飛哥的《圖解 | 深入揭秘 epoll 是如何實現 IO 多路復用的!》,從 linux 源碼級別,一行一行非常硬核地解讀 epoll 的實現原理,且配有大量方便理解的圖片,非常適合源碼控的小伙伴閱讀。

后記

大白話總結一下。

一切的開始,都起源于這個 read 函數是操作系統提供的,而且是阻塞的,我們叫它 阻塞 IO。為了破這個局,程序員在用戶態通過多線程來防止主線程卡死。后來操作系統發現這個需求比較大,于是在操作系統層面提供了非阻塞的 read 函數,這樣程序員就可以在一個線程內完成多個文件描述符的讀取,這就是 非阻塞 IO。

但多個文件描述符的讀取就需要遍歷,當高并發場景越來越多時,用戶態遍歷的文件描述符也越來越多,相當于在 while 循環里進行了越來越多的系統調用。后來操作系統又發現這個場景需求量較大,于是又在操作系統層面提供了這樣的遍歷文件描述符的機制,這就是 IO 多路復用。多路復用有三個函數,最開始是 select,然后又發明了 poll 解決了 select 文件描述符的限制,然后又發明了 epoll 解決 select 的三個不足。

所以,IO 模型的演進,其實就是時代的變化,倒逼著操作系統將更多的功能加到自己的內核而已。如果你建立了這樣的思維,很容易發現網上的一些錯誤。比如好多文章說,多路復用之所以效率高,是因為用一個線程就可以監控多個文件描述符。這顯然是知其然而不知其所以然,多路復用產生的效果,完全可以由用戶態去遍歷文件描述符并調用其非阻塞的 read 函數實現。而多路復用快的原因在于,操作系統提供了這樣的系統調用,使得原來的 while 循環里多次系統調用,變成了一次系統調用 + 內核層遍歷這些文件描述符。

就好比我們平時寫業務代碼,把原來 while 循環里調 http 接口進行批量,改成了讓對方提供一個批量添加的 http 接口,然后我們一次 rpc 請求就完成了批量添加。一個道理。

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7085

    瀏覽量

    89217
  • 代碼
    +關注

    關注

    30

    文章

    4803

    瀏覽量

    68754

原文標題:你管這破玩意叫 IO 多路復用?

文章出處:【微信號:gh_3980db2283cd,微信公眾號:開關電源芯片】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    一文解讀Linux 5種IO模型

    Linux里有五種IO模型:阻塞IO、非阻塞IO、多路復用IO、信號驅動式IO和異步
    的頭像 發表于 11-09 11:12 ?356次閱讀
    一文解讀Linux 5種<b class='flag-5'>IO</b>模型

    Linux--IO多路復用(select,poll,epoll)

    IO多路復用——select,poll,epollIO多路復用是一種操作系統技術,旨在提高系統處理多個輸入輸出操作的性能和資源利用率。與傳統的多線程或多進程模型相比,IO
    的頭像 發表于 11-06 16:13 ?341次閱讀

    多路復用模擬輸入應用中使用ADS8411

    電子發燒友網站提供《在多路復用模擬輸入應用中使用ADS8411.pdf》資料免費下載
    發表于 10-22 09:32 ?0次下載
    在<b class='flag-5'>多路復用</b>模擬輸入應用中使用ADS8411

    多路復用器應用中的防護

    電子發燒友網站提供《多路復用器應用中的防護.pdf》資料免費下載
    發表于 09-21 10:47 ?0次下載
    <b class='flag-5'>多路復用</b>器應用中的防護

    如何使用多路復用器處理高壓共模應用

    電子發燒友網站提供《如何使用多路復用器處理高壓共模應用.pdf》資料免費下載
    發表于 09-11 11:34 ?0次下載
    如何使用<b class='flag-5'>多路復用</b>器處理高壓共模應用

    多路復用器將取代繼電器應用說明

    電子發燒友網站提供《多路復用器將取代繼電器應用說明.pdf》資料免費下載
    發表于 09-11 10:05 ?0次下載
    <b class='flag-5'>多路復用</b>器將取代繼電器應用說明

    需要選擇一顆并行io擴展器件,擴展16個io連接到外設io\'上,CD74HC4067滿足要求嗎?

    需要選擇一顆并行io擴展器件,擴展16個io連接到外設io\'上,那么CD74HC4067是滿足這個基本功能要求把?看著名字怎么叫模擬多路復用器,應該可以用吧。
    發表于 08-26 06:59

    什么是多路復用器?它有哪些作用和應用?

    在現代通信與數據處理領域,多路復用器(Multiplexer,簡稱MUX)作為一種關鍵設備,發揮著不可替代的作用。它能夠將多個輸入信號選擇性地合并到一個輸出信號中,從而實現了對通信信道的高效利用
    的頭像 發表于 05-23 16:38 ?4010次閱讀

    頻分多路復用和時分多路復用的區別有哪些

    頻分多路復用(FDM)和時分多路復用(TDM)是兩種主要的多路復用技術,它們在通信系統中扮演著至關重要的角色。
    的頭像 發表于 05-07 15:24 ?2968次閱讀

    多路復用技術主要有幾種類型?它們各有什么特點?

    多路復用技術主要有幾種類型?它們各有什么特點? 多路復用技術主要有以下幾種類型:進程多路復用、I/O多路復用、信號驅動I/O和異步I/O。每種類型都有其特點和應用場景。 1. 進程
    的頭像 發表于 03-28 15:36 ?3080次閱讀

    一文詳解多路復用的類型

    多路復用最初是在電話中發展起來的。多個信號被組合在一起,通過一根電纜發送。
    的頭像 發表于 03-05 15:44 ?3562次閱讀
    一文詳解<b class='flag-5'>多路復用</b>的類型

    多路復用的原理 為什么要多路復用多路復用技術的應用

    在計算機網絡中,多路復用是一種重要的通信技術,它允許多個信號通過同一個通信信道進行傳輸。
    的頭像 發表于 03-05 15:09 ?3026次閱讀
    <b class='flag-5'>多路復用</b>的原理 為什么要<b class='flag-5'>多路復用</b>?<b class='flag-5'>多路復用</b>技術的應用

    頻分多路復用的原理 頻分多路復用方式的分類

    頻分多路復用(Frequency-division multiplexing,FDM),是指載波帶寬被劃分為多種不同頻帶的子信道,每個子信道可以并行傳送一路信號的一種多路復用技術。
    的頭像 發表于 03-05 14:10 ?1558次閱讀
    頻分<b class='flag-5'>多路復用</b>的原理 頻分<b class='flag-5'>多路復用</b>方式的分類

    什么是io多路復用?IO多路復用的優缺點

    IO多路復用是一種同步IO模型,它允許單個進程/線程同時處理多個IO請求。具體來說,一個進程/線程可以監視多個文件句柄,一旦某個文件句柄就緒,就能夠通知應用程序進行相應的讀寫操作。在沒
    的頭像 發表于 01-18 15:48 ?1677次閱讀

    一文了解多路復用器濾波器

    本章將更深入地介紹多路復用器濾波器,以及它們如何用于各種應用中。您將了解到多路復用器如何幫助設計人員創造出更復雜的無線產品。
    的頭像 發表于 01-16 10:53 ?1505次閱讀
    一文了解<b class='flag-5'>多路復用</b>器濾波器
    主站蜘蛛池模板: 色人格影院第四色| 欧美深深色噜噜狠狠yyy| 99久久免费只有精品| 一区二区三区毛AAAA片特级| 四虎国产精品高清在线观看| 免费A级毛片无码无遮挡| 久草在线新是免费视频| 国产精品99久久久精品无码| 各种肉黄浪荡故事集| 成人短片迅雷下载| 思思99热久久精品在线6| 久久精品国产在热亚洲| 久久婷婷电影网| 男人的天堂MV在线视频免费观看| 免费完整版观看| 免费在线a| 精品午夜视频| 久久精品视频91| 恋老视频 国产国佬| 男人脱女人衣服吃奶视频| 日本高清片免费观看| 我的家庭女教师| 永久免费看mv网站入口| 91popny蜜桃臀| JAPANRCEP老熟妇乱子伦视频| 超碰在线97久久视频观看| 成人性生交大片免费看中文| 国产成人免费网站在线观看| 国产欧美日韩综合精品一区二区| 九九黄色大片| 第四色播日韩AV第一页| 国产在线高清视频无码不卡| 欧美色妞AV重囗味视频| 亚洲欧美日韩在线码不卡 | 双腿被绑成M型调教PLAY照片| 亚洲色欲色欲无码AV| 高清国语自产拍免费| 免费在线视频a| 永久免费的污视频网站| 国产精品一区二区AV白丝在线 | 成人国产精品玖玖热色欲|