转载|网络编程中阻塞式函数的底层逻辑
逛知乎看到的,覺得寫的挺透徹的,轉載一下,原文鏈接:Unix網(wǎng)絡編程里的阻塞是在操作系統(tǒng)的內(nèi)核態(tài)創(chuàng)建一個線程來死循環(huán)嗎?
 原文以阻塞式的recv函數(shù)作為講解,但是所有阻塞式的api底層邏輯基本相通。
 下面是正文:
 
作者:張彥飛
 鏈接:https://www.zhihu.com/question/492983429/answer/2236327954
 來源:知乎
 著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
大家天天都在說阻塞,實際上95%的程序員并沒有真正理解阻塞是啥。這里并沒有循環(huán)的事情,我們來從內(nèi)核視角詳細剖析一下阻塞到底是啥,它是如何工作的。把問題再具體一下,recv 接收數(shù)據(jù)阻塞的原理是啥? 理解了這個就能真正理解所有的阻塞了。用一段大家都熟悉的代碼來舉例!
int main() {int sk = socket(AF_INET, SOCK_STREAM, 0);connect(sk, ...)recv(sk, ...) }在上面的 demo 中雖然只是簡單的兩三行代碼,但實際上用戶進程和內(nèi)核配合做了非常多的工作。大致的工作流程如下:
 看到這里,你可能還沒看著阻塞的原理。別著急,往下看。我們來看 recv 函數(shù)依賴的底層實現(xiàn)。首先通過 strace 命令跟蹤,可以看到 clib 庫函數(shù) recv 會執(zhí)行到 recvfrom 系統(tǒng)調(diào)用。進入系統(tǒng)調(diào)用后,用戶進程就進入到了內(nèi)核態(tài),通過執(zhí)行一系列的內(nèi)核協(xié)議層函數(shù),然后到 socket 對象的接收隊列中查看是否有數(shù)據(jù),沒有的話就把自己添加到 socket 對應的等待隊列里。最后讓出CPU,操作系統(tǒng)會選擇下一個就緒狀態(tài)的進程來執(zhí)行。
 整個流程圖如下:
 
 以上這個流程圖是我根據(jù) Linux 內(nèi)核源碼的執(zhí)行過程總結后畫出來的。注意上面的第四步和第五步。第四步中是在訪問 sock 對象下面的接收隊列,如果接收隊列中還沒有數(shù)據(jù)到達,那么就會進入第五步,把當前進程阻塞掉。但是在把自己阻塞掉之前,進程干了一件事, 給 socket 上留了個標記。告訴內(nèi)核,如果這個 socket 上數(shù)據(jù)好了,記得叫我起來哈!就是源碼 prepare_to_wait 函數(shù)中的 __add_wait_queue 這一句。
接下來 Linux 就會選擇下一個就緒狀態(tài)的進程來執(zhí)行。這就是阻塞原理的上半段,就是進程修改自己的狀態(tài),主動交出 CPU 的執(zhí)行權。當有數(shù)據(jù)到達的時候,內(nèi)核首先將數(shù)據(jù)包放到該 socket 的接收隊列中。然后掃描一下 socket 等待隊列,然后發(fā)現(xiàn):“呦呵,有進程阻塞在這個 socket 上面哎,好喚醒它”。
 
 具體到代碼里就是 __wake_up_common 這個函數(shù)會訪問 socket 的等待隊列。
在 __wake_up_common 中找出一個等待隊列項 curr,然后調(diào)用其回調(diào)函數(shù) curr->func,來完成進程的喚醒。不過,要注意的是,這個喚醒只是把相應的進程放到可運行隊列里而已。真正的執(zhí)行還得等其它進程主動釋放 CPU 或者是時間片到了之后,內(nèi)核把其它進程拿下以后才能真正獲得 CPU 并開始執(zhí)行。
 參考:圖解 | 深入理解高性能網(wǎng)絡開發(fā)路上的絆腳石 - 同步阻塞網(wǎng)絡 IO說到這里,你可能還會問了。內(nèi)核是如何接收包的,畢竟喚醒用戶進程是它干的。難道它不是一個死循環(huán)么?是的,并不是。 網(wǎng)卡上收到數(shù)據(jù)包的時候,是通過硬中斷喚醒內(nèi)核進程處理,硬中斷會觸發(fā)軟中斷。有了軟中斷請求以后,ksoftirqd 內(nèi)核線程才開始執(zhí)行。來從網(wǎng)卡上取包,處理,放到接收隊列,然后喚醒用戶進程。
 參見:圖解Linux網(wǎng)絡包接收過程
 究其根源,是由網(wǎng)卡的硬中斷來觸發(fā)的。如果一段時間內(nèi)沒有網(wǎng)絡包處理,那么沒有死循環(huán)來消耗 CPU 的。對網(wǎng)絡底層還有啥不理解的,來看看我的公眾號「開發(fā)內(nèi)功修煉」 或許可以幫你解開一些困惑。
 
 Github: GitHub - yanfeizhang/coder-kung-fu: 開發(fā)內(nèi)功修煉
 哦對了,想理解多路復用,來看看我的這一篇吧,也是從源碼角度深入分析的。圖解 | 深入揭秘 epoll 是如何實現(xiàn) IO 多路復用的!
總結
以上是生活随笔為你收集整理的转载|网络编程中阻塞式函数的底层逻辑的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 颐和园关门时间清客时间
 - 下一篇: DNF现在更新的那个活动武器怎么得的?