核心態:實從本質上說就是我們所說的核心,它是一種特殊的軟體程式,特殊在哪兒呢?控制計算機的硬體資源,例如協調cpu資源,分配記憶體資源,並且提供穩定的環境**用程式執行。核心程式可以訪問記憶體的所有資料,包括外圍裝置,例如硬碟,網絡卡,cpu也可以將自己從乙個程式切換到另乙個程式。
使用者態:使用者態就是提**用程式執行的空間,為了使應用程式訪問到核心管理的資源例如cpu,記憶體,i/o。核心必須提供一組通用的訪問介面,這些介面就叫系統呼叫。
檔案描述符(file descriptor)是電腦科學中的乙個術語,是乙個用於表述指向檔案的引用的抽象化概念。
檔案描述符在形式上是乙個非負整數。實際上,它是乙個索引值,指向核心為每乙個程序所維護的該程序開啟檔案的記錄表。當程式開啟乙個現有檔案或者建立乙個新檔案時,核心向程序返回乙個檔案描述符。在程式設計中,一些涉及底層的程式編寫往往會圍繞著檔案描述符展開。但是檔案描述符這一概念往往只適用於unix、linux這樣的作業系統。
在linux中,預設情況下所有的socket都是blocking,乙個典型的讀操作流程大概是這樣:
當使用者程序呼叫了recvfrom這個系統呼叫,kernel就開始了io的第乙個階段:準備資料(對於網路io來說,很多時候資料在一開始還沒有到達。比如,還沒有收到乙個完整的udp包。這個時候kernel就要等待足夠的資料到來)。這個過程需要等待,也就是說資料被拷貝到作業系統核心的緩衝區中是需要乙個過程的。而在使用者程序這邊,整個程序會被阻塞。當kernel一直等到資料準備好了,它就會將資料從核心中拷貝到使用者記憶體,然後核心返回結果,使用者程序才解除block的狀態,重新執行起來。
也就是說即使資料沒準備好,程式依舊在這裡等待。linux下,可以通過設定socket使其變為non-blocking。當對乙個non-blocking socket執行讀操作時,流程是這個樣子:
當使用者程序發出read操作時,如果kernel中的資料還沒有準備好,那麼它並不會block使用者程序,而是立刻返回乙個error。從使用者程序角度講 ,它發起乙個read操作後,並不需要等待,而是馬上就得到了乙個結果。使用者程序判斷結果是乙個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦kernel中的資料準備好了,並且又再次收到了使用者程序的system call,那麼它馬上就將資料拷貝到了使用者記憶體,然後返回。
所以,nonblocking io的特點是使用者程序在執行read操作時並且資料沒有準備好時,可以不被阻塞。io多路復用簡單的說,就是通過select、poll、epoll等系統庫的實現,利用單個執行緒監聽多個連線,當其中乙個連線的資料變得可讀時,由作業系統通知執行緒進行讀取。
事實上如果併發量不高,維護乙個執行緒池來對連線進行處理反而會比io多路復用更加的高效,畢竟io多路復用有兩次系統呼叫。當然這個得具體情況具體分析。由於這個io模型還沒有比較出名的應用示例,這裡也只是提一下有這麼個模型,不做具體的說明,當然書裡面還是有說的,有興趣可以去看一看書。
一般地說,它的工作機制是:告知核心啟動某個操作,並讓核心在整個操作 (包括將資料從核心複製到程式自己的緩衝區)完成後通知我們。這種模型與訊號驅動模型的主要區別在於:訊號驅動式io是由核心通知我們何時可以啟動乙個io操作,而非同步io模型是由核心通知我們io操作何時完成。
同步io: 上述我們提到的模型,除了非同步 i/o外,其餘的模型都被認為是同步io。仔細分析它的特徵,可以得出它們主要的差別是在資料還未可讀的時候,有著不同的處理方式:阻塞、非阻塞、核心**等。
非同步io:而非同步io則是在哪個階段不會阻塞,當程序收到通知時,資料已經被複製到程序空間裡了。
前四種既然都是同步io,io多路復用比阻塞io模型和非阻塞式io模型好在**?
上面提到的兩種模式要想及時響應請求:
select的實現方式就是顧名思義,程序將自己要監聽的檔案描述符列表通過select系統呼叫傳給核心,然後由核心幫我們確定哪些可以讀寫,然後做好標記並返回給我們,我們再通過遍歷找到可以讀寫的檔案描述符。
偽**如下
// 接收連線請求,建立乙個檔案描述符
while(1)
while(1)
}}
每次呼叫select函式時,都需要傳乙份檔案描述符的資料到核心,比較耗費資源。
呼叫返回的不是可以讀寫的列表,而是加上了一些標記返回原列表,還需要使用者態程序自己遍歷。
核心中通過輪詢遍歷檔案描述符的可讀寫狀態,而不是通過讀寫事件**。
select在設計上,有檔案描述符列表的長度限制,現在長度限制一般為1024個。
poll和select機制類似,區別在於取消了檔案描述符列表的長度限制。但是select限制的目的也是為了效能。即使取消限制,當如果遇到要監聽的列表很長也是不合適的。
epoll針對上面select說到的問題,進行了設計上的優化。如下:
epoll會儲存乙個程序所開啟的所有檔案描述符列表,在呼叫epoll_wait時無需傳入列表。
核心不再通過輪詢的方式找到就緒的檔案描述符,而是通過非同步 io 事件喚醒。
核心僅會將有 io 事件的檔案描述符返回給使用者,使用者也無需遍歷整個檔案描述符集合。
偽**如下:
// 建立epoll物件
epfd = epoll_create(1024);
//向核心新增、修改或刪除要監控的檔案描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
while(1)
本次介紹了一系列io模型的概念。同時說明了io多路復用的一些實現的特點,以及分析它們存在的一些問題。根據上面的講解,可以得出:io多路復用在併發較高的場景下效率更好,原因是避免的建立多個執行緒消耗資源的同時,也避免了頻繁的進行系統呼叫。取得了資源和效能的較好平衡,這也是為什麼redis、nginx等一系列出名的開源軟體使用該模型的原因。 IO模型 IO多路復用
用socket 一定會用到accept recv recvfrom這些方法 正常情況下 accept recv recvfrom都是阻塞的 如果setblocking false 整個程式就變成乙個非阻塞的程式了非阻塞的特點 沒有併發程式設計的機制 是乙個同步的程式 程式不會在某乙個連線的recv或...
IO模型 多路復用
乙個輸入操作通常包括兩個階段 應用程序被阻塞,直到資料從核心緩衝區複製到應用程序緩衝區中才返回。應該注意到,在阻塞的過程中,其它應用程序還可以執行,因此阻塞不意味著整個作業系統都被阻塞。因為其它應用程序還可以執行,所以不消耗 cpu 時間,這種模型的 cpu 利用率會比較高。應用程序執行系統呼叫之後...
IO模型 io多路復用(三)
兩者相互比較 1 如果只有乙個使用者連線server端,多路復用io還不如阻塞io效率高 2 相比阻塞io,多路復用io中間多了個反饋機制 3 多路復用io的 可以同時監控socket 多個socket物件coon1 coon2.recv 4 多路復用io可以識別有人連線某個coon3,然後告知有c...