轉載|網絡編程中阻塞式函數的底層邏輯

逛知乎看到的,覺得寫的挺透徹的,轉載一下,原文鏈接:Unix網絡編程里的阻塞是在操作系統的內核態創建一個線程來死循環嗎?
原文以阻塞式的recv函數作為講解,但是所有阻塞式的api底層邏輯基本相通。
下面是正文:

作者:張彥飛
鏈接:https://www.zhihu.com/question/492983429/answer/2236327954
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

大家天天都在說阻塞,實際上95%的程序員并沒有真正理解阻塞是啥。這里并沒有循環的事情,我們來從內核視角詳細剖析一下阻塞到底是啥,它是如何工作的。把問題再具體一下,recv 接收數據阻塞的原理是啥? 理解了這個就能真正理解所有的阻塞了。用一段大家都熟悉的代碼來舉例!

int main()
{int sk = socket(AF_INET, SOCK_STREAM, 0);connect(sk, ...)recv(sk, ...)
}

在上面的 demo 中雖然只是簡單的兩三行代碼,但實際上用戶進程和內核配合做了非常多的工作。大致的工作流程如下:
看到這里,你可能還沒看著阻塞的原理。別著急,往下看。我們來看 recv 函數依賴的底層實現。首先通過 strace 命令跟蹤,可以看到 clib 庫函數 recv 會執行到 recvfrom 系統調用。進入系統調用后,用戶進程就進入到了內核態,通過執行一系列的內核協議層函數,然后到 socket 對象的接收隊列中查看是否有數據,沒有的話就把自己添加到 socket 對應的等待隊列里。最后讓出CPU,操作系統會選擇下一個就緒狀態的進程來執行。
整個流程圖如下:

以上這個流程圖是我根據 Linux 內核源碼的執行過程總結后畫出來的。注意上面的第四步和第五步。第四步中是在訪問 sock 對象下面的接收隊列,如果接收隊列中還沒有數據到達,那么就會進入第五步,把當前進程阻塞掉。但是在把自己阻塞掉之前,進程干了一件事, 給 socket 上留了個標記。告訴內核,如果這個 socket 上數據好了,記得叫我起來哈!就是源碼 prepare_to_wait 函數中的 __add_wait_queue 這一句。

//file: kernel/wait.c
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{unsigned long flags;wait->flags &= ~WQ_FLAG_EXCLUSIVE;spin_lock_irqsave(&q->lock, flags);if (list_empty(&wait->task_list))__add_wait_queue(q, wait);set_current_state(state);spin_unlock_irqrestore(&q->lock, flags);
}

接下來 Linux 就會選擇下一個就緒狀態的進程來執行。這就是阻塞原理的上半段,就是進程修改自己的狀態,主動交出 CPU 的執行權。當有數據到達的時候,內核首先將數據包放到該 socket 的接收隊列中。然后掃描一下 socket 等待隊列,然后發現:“呦呵,有進程阻塞在這個 socket 上面哎,好喚醒它”。

具體到代碼里就是 __wake_up_common 這個函數會訪問 socket 的等待隊列。

//file: kernel/sched/core.c
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, int wake_flags, void *key)
{wait_queue_t *curr, *next;list_for_each_entry_safe(curr, next, &q->task_list, task_list) {unsigned flags = curr->flags;if (curr->func(curr, mode, wake_flags, key) &&(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)break;}
}

__wake_up_common 中找出一個等待隊列項 curr,然后調用其回調函數 curr->func,來完成進程的喚醒。不過,要注意的是,這個喚醒只是把相應的進程放到可運行隊列里而已。真正的執行還得等其它進程主動釋放 CPU 或者是時間片到了之后,內核把其它進程拿下以后才能真正獲得 CPU 并開始執行。
參考:圖解 | 深入理解高性能網絡開發路上的絆腳石 - 同步阻塞網絡 IO說到這里,你可能還會問了。內核是如何接收包的,畢竟喚醒用戶進程是它干的。難道它不是一個死循環么?是的,并不是。 網卡上收到數據包的時候,是通過硬中斷喚醒內核進程處理,硬中斷會觸發軟中斷。有了軟中斷請求以后,ksoftirqd 內核線程才開始執行。來從網卡上取包,處理,放到接收隊列,然后喚醒用戶進程。
參見:圖解Linux網絡包接收過程
究其根源,是由網卡的硬中斷來觸發的。如果一段時間內沒有網絡包處理,那么沒有死循環來消耗 CPU 的。對網絡底層還有啥不理解的,來看看我的公眾號「開發內功修煉」 或許可以幫你解開一些困惑。

Github: GitHub - yanfeizhang/coder-kung-fu: 開發內功修煉
哦對了,想理解多路復用,來看看我的這一篇吧,也是從源碼角度深入分析的。圖解 | 深入揭秘 epoll 是如何實現 IO 多路復用的!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/376782.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376782.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376782.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

把txt文件中的json字符串寫到plist文件中

- (void)json2Plist {NSString *filePath [self applicationDocumentsDirectoryFileName:"json"];NSMutableArray *tempArray [[NSMutableArray alloc] initWithContentsOfFile:filePath];//第一次添加數據時,數組為空if (tempArray.count 0) {tempArray [NSMuta…

樹的存儲結構2 - 數據結構和算法42

樹的存儲結構 讓編程改變世界 Change the world by program 孩子表示法 我們這次換個角度來考慮,由于樹中每個結點可能有多棵子樹,可以考慮用多重鏈表來實現。 就像我們雖然有計劃生育,但我們還是無法確保每個家庭只養育一個孩子的沖動&a…

海量數據去重

海量數據去重 一個文件中有40億條數據,每條數據是一個32位的數字串,設計算法對其去重,相同的數字串僅保留一個,內存限制1G. 方法一:排序 對所有數字串進行排序,重復的數據傳必然相鄰,保留第一…

Sharepoint 2013 發布功能(Publishing features)

一、默認情況下,在創建網站集時,只有選擇的模板為‘ Publishing Portal(發布門戶)’與‘ Enterprise Wiki(企業 Wiki)’時才默認啟用發布功能,如下圖所示: 二、發布功能包含兩塊&…

【原】android啟動時白屏或者黑屏的問題

解決應用啟動時白屏或者黑屏的問題 由于Activity只能到onResume時,才能展示到前臺,所以,如果為MAIN activity設置背景的話,無論onCreate-onResume速度多快,都會出現短暫的白屏或者黑屏 其實解決的辦法很簡單&#xff0…

【草稿】windows + vscode 遠程開發

主要分為三個步驟: 1、開啟openssh服務 2、通過ssh命令連接到遠程服務器 3、通過vscode連接遠程服務器進行開發調試 ssh概念 SSH是較可靠,專為遠程登陸會話和其他網絡服務提供安全性得協議,利用ssh協議可以有效防止遠程管理過程中得信息…

POJ3185(簡單BFS,主要做測試使用)

沒事做水了一道POJ的簡單BFS的題目 這道題的數據范圍是20,所以狀態總數就是&#xff08;1<<20&#xff09; 第一次提交使用STL的queue&#xff0c;并且是在隊首判斷是否達到終點&#xff0c;達到終點就退出&#xff0c;超時&#xff1a;&#xff08;其實這里我是很不明白…

tomcat站點配置

tomcat版本&#xff1a;tomcat5.5.91、打開tomcat\conf\server.xml&#xff0c;在里面找到<Engine name"Catalina" defaultHost"localhost">.....</Engine>2、在<Engine name"Catalina" defaultHost"localhost"><…

新的視頻會議模式:StarlineProject

目錄效果展示部分用戶參與度部分技術細節機械裝置以及硬件配置。視頻系統照明人臉跟蹤壓縮和傳輸圖像渲染音頻系統step1&#xff1a;捕獲音頻step2&#xff1a;音頻去噪處理step3&#xff1a;壓縮、傳輸、解壓step4&#xff1a;渲染可以改進的點效果展示部分 〔映維網〕谷歌光場…

HDU 3934

/*這是用的有旋轉卡殼的思想。 首先確定i&#xff0c;j&#xff0c;對k進行循環&#xff0c;知道找到第一個k使得cross(i,j,k)>cross(i,j,k1),如果ki進入下一次循環。 對j&#xff0c;k進行旋轉&#xff0c;每次循環之前更新最大值&#xff0c;然后固定一個j&#xff0c;同樣…

[ios] UILocalNotification實現本地的鬧鐘提醒【轉】

http://www.cnblogs.com/jiangshiyong/archive/2012/06/06/2538204.html轉載于:https://www.cnblogs.com/jinjiantong/archive/2013/04/01/2992624.html

sql server根據表中數據生成insert語句

幾個收藏的根據數據庫生成Insert語句的存儲過程[修正版]----根據表中數據生成insert語句的存儲過程--建立存儲過程&#xff0c;執行spGenInsertSQL 表名--感謝playyuer----感謝szyicol--CREATEproc[dbo].[spGenInsertSQL](tablenamevarchar(256))asbegindeclaresqlvarchar(8000…

Javascript eval()函數 基礎回顧

如果您想詳細了解ev al和JSON請參考以下鏈接&#xff1a; eval &#xff1a;https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Functions/Eval JSON&#xff1a;http://www.json.org/ eval函數的工作原理 eval函數會評估一個給定的含有JavaScript代碼的…

雜感無題|

今天中午和組里面的人吃飯&#xff0c;聊起了科興跳樓的事情。這事其實前幾天我華為的mentor就轉給我了&#xff0c;當時也沒太在意&#xff0c;在脈脈上看了看&#xff0c;也不知曉是誰&#xff0c;想著可能又是抑郁癥吧。 飯后依舊繞著食堂散步&#xff0c;ly說那個人好像還是…

uva1366_Martian Mining_簡單DP

題目不難&#xff0c;卻想了好長時間&#xff0c;目測自己DP還是很水。。。囧 思路&#xff1a;舍f[i][j]為前i行j列的最大礦總量不難推出狀態轉移方程為f[i][j]max(f[i-1][j]line[i][j],f[i][j-1]row[j][i]) 其中line[i][j]為第i行前j個A礦的和&#xff08;a[i][1]a[i][2]...a…

數學圖形之Boy surface

這是一個姓Boy的人發現的,所以取名為Boy surface.該圖形與羅馬圖形有點相似,都是三分的圖形.它甚至可以說是由羅馬曲面變化而成的. 本文將展示幾種Boy曲面的生成算法和切圖,使用自己定義語法的腳本代碼生成數學圖形.相關軟件參見:數學圖形可視化工具,該軟件免費開源.QQ交流群: …

開個定時器給echarts組件配置定時更新

我在js文件中開了個定時器&#xff0c;每1s從后端獲取數據并解析&#xff0c;然后用異步方法就渲染不出來&#xff0c;改成同步就可以了。 這個解決方法來自于這篇文章&#xff0c;我出的問題和他一樣&#xff1a;關于ajax中readyState的值一直為1的問題 這里將ajax參數修改為f…

SDK 操作 list-view control 實例 -- 遍歷進程

遍歷窗口&#xff0c;獲得控件句柄 1 EnumChildWindows(hwndDlg, (WNDENUMPROC)EnumChildProc, NULL); 回調函數 1 BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam )2 {3 char strCLSName[MAXBYTE] {0};4 GetClassName(hwnd, strCLSName, MAXBYTE);5 if (…

推薦一份不錯的清除默認樣式的CSS樣式

時間過得真快&#xff0c;離 Reset CSS 研究&#xff08;八卦篇&#xff09; 已經 3 個多月了。廢話少說&#xff0c;趕緊將技術篇寫完吧。 回顧與反思 第一份 reset css 是 Tantek 的 undohtml.css, 很簡單的代碼&#xff0c;Tantek 根據自己的需要&#xff0c;對瀏覽器的默認…

python深淺拷貝

在python中&#xff0c;對象賦值實際上是對象的引用。當創建一個對象&#xff0c;然后把它賦給另一個變量的時候&#xff0c;python并沒有拷貝這個對象&#xff0c;而只是拷貝了這個對象的引用。 所以一個結構類型被賦給另外一個對象的時候&#xff0c;盡可能不使用 &#xff…