【從頭到腳】併發編程(二)面試官問我Linux的網絡IO模式怎麼辦?

該衝啦

老哥們,又到了該衝的時候啦python

上一期咱們聊了 「進程,線程和協程分別是什麼,爲何使用它們以及它們之間的區別」(想看的小夥伴在這裏)今天咱們把目光放高一點,來看看進程,線程和協程的應用 --- 併發編程linux

併發編程

併發,顧名思義就是多個程序一塊兒運行。運行在哪裏呢?固然是cpu面試

因此併發就是多個程序一塊兒運行在cpu上咯?算法

no no no!就單個cpu而言,一次只能處理一個程序,之因此可以看起來「一塊兒運行」,是由於操做系統採用了分時系統,什麼時間片,調度算法啦,讓程序交替式的在cpu上運行,看起來好像是一塊兒運行同樣(這裏不展開了,後面再更新給你們)編程

因此這就是併發的所有意義嗎?就是多個程序經過操做系統的調度交替運行,看起來像一塊兒運行,因此稱爲併發?安全

固然不是,之因此稱爲併發編程,是由於它們有真正同時運行的地方。markdown

回顧一下操做系統的基礎知識。咱們知道,操做系統是採用虛擬存儲的,它的核心是內核,爲了保護內核的安全,操做系統把虛擬空間劃分紅兩部分,一部分是內核空間,一部分是用戶空間,就像星空戰艦上的指揮室同樣。網絡

這時候聰明的的你確定就會發現一個問題併發

cpu運算的結果是放在內核空間的,而咱們讀取結果倒是在用戶空間,這就有一個數據從內核空間到用戶空間的過程異步

沒錯,從操做系統層看程序運行時的數據流的話,是有 「等待數據 --- 數據拷貝到內核空間 --- cpu運算 --- 數據從內核空間拷貝到用戶空間」 這幾個過程的

程序運行時的數據流動過程
程序運行時的數據流動過程

併發,就發生在這幾個過程當中。

來一張經典的示意圖

中間的部分就是併發的部分,只要保證cpu運算的過程是串行的,其餘過程均可以併發執行。

咱們把數據從「等待」到「拷貝到用戶空間」稱做一次 IO操做

其中,「等待數據 --- 數據拷貝到內核空間 --- cpu運算」叫作 數據準備階段,「數據從內核空間拷貝到用戶空間」叫作 數據拷貝階段

Linux的網絡I/O模式

沒有實際應用的知識是沒有靈魂的,一樣,沒有被面試官問起的知識點都是孤獨的..

當咱們興高采烈地聊到這裏時,面試官會笑眯眯的問你

好的,你來說一下linux中有哪些網絡io模式吧?

好吧算你狠。

Linux中有五種網絡io模式,分別是

  • 阻塞 I/O
  • 非阻塞 I/O
  • I/O 多路複用
  • 信號驅動 I/O
  • 異步 I/O

其中由於 信號驅動 I/O 不經常使用,因此通常只要瞭解其餘四種 I/O 模式

當咱們掰着指頭好不容易數出這五種模式時,剛纔還昏昏欲睡的面試官忽然眼睛一亮,皮笑肉不笑地對你說

不錯嘛,那你來講說 阻塞/非阻塞 IO 和同步/異步 IO的區別吧?

搞我是吧

但既然人家問,咱也只能準備。要分清這四個概念,就要緊緊記住以前數據流動的過程

(再放一遍)

阻塞 I/O

用戶空間由於不能直接訪問內核空間,當他想要結果時須要發起一個 read調用

但數據的準備和計算是須要一個過程的。好比咱們進行網絡請求的時,數據尚未返回,這個過程須要等待,當數據返回後被拷貝到內核空間,通過一系列運算,最終拷貝到用戶空間

進程發起調用後就一直等待,也就是阻塞狀態

等到數據拷貝完成後,進程還不能運行,須要內核給一個 ok 的信號,告訴進程數據已經拷貝到你那裏了,用戶進程這才解除阻塞狀態,愉快的運行起來。

因此很明顯,阻塞 I/O的特色是內核返回數據拷貝完成的信號以前,進程一直被阻塞。也就是說阻塞其實包括兩個階段,一個是等待數據準備,一個是數據從內核空間拷貝到用戶空間

ok,理解了阻塞 I/O 的調用過程後,接下來的I/O模式就很簡單了

非阻塞 I/O

和「阻塞 I/O」不一樣的是。「非阻塞 I/O」的進程發起一個read調用時,若是內核的數據尚未準備好,它就會馬上返回一個error信號

從用戶進程的角度講,它發起一個read操做後,並不須要等待就立刻獲得了一個結果,當用戶進程判斷結果是一個error時,它知道數據尚未準備好,因而再次發送read調用,一遍又一遍地詢問。

當內核運算完畢準備好數據,而且再次收到用戶進程的調用時,它就立刻把數據拷貝到用戶空間裏,而後返回ok信號

因此「非阻塞 I/O」的特色是,用戶進程不斷詢問內核,數據好了沒有

I/O 多路複用

「I/O 多路複用」其實是「阻塞 I/O」的變種。

select,poll,epoll都是「I/O 多路複用」的機制,它的好處在於單個進程就能夠同時處理多個網絡鏈接的IO

單個進程處理多個網絡鏈接??!

沒錯,好比select方法。用戶進程先調用select方法,select幹了兩件事,一個是管理多個負責網絡鏈接的socket,另外一個是讓內核監視socket的狀態

當內核接收到socket返回的數據並運算完成後,select方法就會返回,這時用戶進程再發起read調用,把數據從內核空間拷貝到用戶空間裏

須要注意的是,socket自己是非阻塞的,但整個用戶進程是阻塞的。

這種阻塞包括兩個階段,一個是發起select方法等待socket返回數據時阻塞,另外一個是等待數據從內核控件拷貝到用戶空間時阻塞,從這就能夠看出和「阻塞 I/O」實際上是同樣的

因此,「I/O 多路複用」的特色是一個進程能夠同時等待多個信號,任意一個信號就緒時發起系統調用

異步 I/O

終於到最後一個啦,加油加油!

區別於上面幾種I/O模式,「異步 I/O」是沒有阻塞的

用戶進程發起read調用以後,馬上就能夠開始去作其它的事。內核會馬上返回,因此不會對用戶進程產生任何阻塞。

當內核數據計算完成後,它會自動把數據拷貝到用戶空間,而後再給用戶進程發送一個信號,告訴它read調用完成了。因此用戶進程並非在收到ok信號後拷貝數據,數據拷貝階段也不會阻塞

因此「異步 I/O」的特色是,用戶進程不須要等待內核返回的信號,一直運行

總結

終於介紹完啦

總結一下

阻塞io和非阻塞io的區別是

內核準備數據時有沒有當即返回

阻塞io只有在內核準備好數據後才返回,而非阻塞io在準備過程當中一直返回。

同步io和異步io的區別

同步io會阻塞進程,異步io不會阻塞進程

因此其實「阻塞 I/O」,「非阻塞 I/O」和「I/O 多路複用」都是同步I/O

前面不夠細心的小夥伴可能會問了

爲何「非阻塞 I/O」是同步I/O呢?

仔細看「非阻塞 I/O」的流程圖

當內核的數據準備好後,用戶進程再次詢問時,數據開始從內核空間拷貝到用戶空間。這個過程當中用戶進程是阻塞的,只有當拷貝完成,內核返回完成信號,進程纔會繼續運行。

因此,不論是「阻塞 I/O」仍是「非阻塞 I/O」都是會發生阻塞

不一樣的是,「阻塞 I/O」在數據準備階段和從內核空間拷貝到用戶空間階段都會阻塞,「非阻塞 I/O」只有在拷貝階段會阻塞

結尾

到這裏,咱們瞭解了Linux主要的四種網絡I/O模式,這是併發編程系列的第一篇文章,不知道這樣子解釋夠不夠簡單清晰

天天我都會分享一篇關於「計算機基礎」「操做系統」「python」「數據分析」相關的文章

技術不該該是複雜枯燥的,只是一個知識點的掌握每每須要聯繫許許多多零碎的知識。我但願可以把這些知識有條理的組織起來,以一個探索者的視角,清晰的整理給你們~

若是你喜歡的話,歡迎點個👍,若是你不當心 關注 了話,我保證後面會有更多有意思的東西更新給你~

感謝你們閱讀到這裏!!無覺得謝,只能「砰砰砰」祝各位家庭幸福,每天開心 ~

相關文章
相關標籤/搜索