同步是指乙個任務的完成需要依賴另外乙個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成。
非同步是指不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什麼工作,依賴的任務也立即執行,只要自己完成了整個任務就算完成了,非同步一般使用狀態、通知和**。
阻塞是指呼叫結果返回之前,當前執行緒會被掛起,一直處於等待訊息通知,不能夠執行其他業務。
非阻塞是指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回。
準備資料
將資料從核心緩衝區拷貝到程序位址空間
由於存在這兩個階段,linux產生了下面五種io模型。
當使用者程序呼叫了recvfrom呼叫時,核心進入io的第乙個階段:準備資料(核心需要等待足夠的資料再拷貝),這個過程需要等待,使用者程序會被阻塞,等核心將資料準備好,然後拷貝到使用者位址空間,核心返回結果,使用者程序才從阻塞態進入就緒態。
linux中,預設情況下所有的socket都是阻塞的。
當使用者程序發出read操作時,如果kernel中的資料還沒有準備好,那麼它並不會block使用者程序,而是立刻返回乙個error。使用者程序判斷結果是乙個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦kernel中的資料準備好了,並且又再次收到了使用者程序的system call,那麼它馬上就將資料拷貝到了使用者記憶體,然後返回。
非阻塞io模式下使用者程序需要不斷地詢問核心的資料準備好了沒有。
linux下可以通過設定socket使其變為non-blocking。
通過一種機制,乙個程序可以監視多個檔案描述符(套接字描述符),一旦某個檔案描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。這樣就不需要每個使用者程序不斷的詢問核心資料準備好了沒有。
常用的io多路復用方式有select、poll和epoll。
2.3.1select
kernel會「監視」所有select負責的socket,當任何乙個socket中的資料準備好了,select就會返回。這個時候使用者程序再呼叫read操作,將資料從kernel拷貝到使用者程序。
int select (
int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
;
select 函式監視的檔案描述符分3類,分別是writefds、readfds、和exceptfds。呼叫後select函式會阻塞,直到有描述副就緒(有資料可讀、可寫、或者有except),或者超時(timeout指定等待時間,如果立即返回設為null即可),函式返回。當select函式返回後,可以通過遍歷fdset,來找到就緒的描述符。
select的乙個缺點在於單個程序能夠監視的檔案描述符的數量存在最大限制,在linux上一般為1024。
2.3.2poll
poll使用乙個 pollfd的指標實現。
int poll (
struct pollfd *fds,
unsigned
int nfds,
int timeout)
;
pollfd結構包含了要監視的event和發生的event
struct pollfd
;
和select函式一樣,poll返回後,需要遍歷pollfd來獲取就緒的描述符,但 poll 沒有監聽最大數量限制,解決了select模型的檔案監聽的限制
2.3.3epoll
epoll使用乙個檔案描述符管理多個描述符,將使用者關心的檔案描述符的事件存放到核心的乙個事件表中,採用監聽**的機制,這樣在使用者空間和核心空間的copy只需一次,避免再次遍歷就緒的檔案描述符列表。
epoll的操作過程需要三個介面:
int
epoll_create
(int size); //建立乙個epoll的控制代碼,size用來告訴核心這個監聽的數目一共有多大。
intepoll_ctl
(int epfd,
int op,
int fd,
struct epoll_event *event);
intepoll_wait
(int epfd,
struct epoll_event * events,
int maxevents,
int timeout)
;
/**
* 對指定描述符fd執行op操作
* epfd:是epoll_create()的返回值。
* op:表示op操作,用三個巨集來表示:新增epoll_ctl_add,刪除epoll_ctl_del,修改epoll_ctl_mod。分別新增、刪除和修改對fd的監聽事件。
* fd:是需要監聽的fd(檔案描述符)
* epoll_event:是告訴核心需要監聽什麼事
*/int
epoll_ctl
(int epfd,
int op,
int fd,
struct epoll_event *event)
;struct epoll_event
;
events可以是以下幾個巨集的集合:
/**
* 引數epfd等待epfd上的io事件,最多返回maxevents個事件。
* 引數events用來從核心得到事件的集合
* maxevents告之核心這個events有多大,這個maxevents的值不能大於建立epoll_create()時的size
* 引數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。
* * 該函式返回需要處理的事件數目,如返回0表示已超時。
*/int
epoll_wait
(int epfd,
struct epoll_event * events,
int maxevents,
int timeout)
;
epoll的兩種工作模式lt(level trigger,水平觸發)模式:當epoll_wait檢測到描述符就緒,將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫epoll_wait時,會再次響應應用程式並通知此事件。lt模式是預設的工作模式。
lt模式同時支援阻塞和非阻塞socket。
et(edge trigger,邊緣觸發)模式:當epoll_wait檢測到描述符就緒,將此事件通知應用程式,應用程式必須立即處理該事件。如果不處理,下次呼叫epoll_wait時,不會再次響應應用程式並通知此事件。
et是高速工作方式,只支援非阻塞socket。et模式減少了epoll事件被重複觸發的次數,因此效率要比lt模式高。
使用者程序發起read操作之後,立刻就可以開始去做其它的事。核心收到乙個非同步io read之後,會立刻返回,不會阻塞使用者程序。核心會等待資料準備完成,然後將資料拷貝到使用者記憶體,當這一切都完成之後,核心會給使用者程序傳送乙個signal,告訴它read操作完成了。
核心檔案描述符就緒後,通過訊號通知使用者程序,使用者程序再通過系統呼叫讀取資料。此方式屬於同步io,因為實際讀取資料到使用者程序快取的工作仍然是由使用者程序自己負責的。
網路程式設計之IO模型
io多路復用概念 io發生時涉及的物件和步驟。對於乙個網路io,它會涉及到兩個系統物件,乙個是呼叫io的程序或者執行緒,另乙個就是系統核心。如當乙個read操作發生時,會先等待資料準備,然後將資料從核心拷貝到程序中去 阻塞io blocking io 特點 在執行io的兩個階段 等待資料和拷貝資料兩...
網路程式設計之 IO模型
我們這裡研究的io模型都是針對網路io的 blocking io 阻塞io nonblocking io 非阻塞io io multiplexing io多路復用 signal driven io 訊號驅動io asynchronous io 非同步io 由signal driven io 訊號驅動...
網路程式設計之IO模型 非同步IO
linux下的asynchronous io其實用得不多,從核心2.6版本才開始引入。先看一下它的流程 使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到乙個asynchronous read之後,首先它會立刻返回,所以不會對使用者程序產生任何bl...