1.關鍵概念理解
-
同步:發起一個呼叫,得到結果才回傳,
-
異步:呼叫發起后,呼叫直接回傳;呼叫方主動詢問被呼叫方獲取結果,或被呼叫方通過回呼函式,
-
阻塞:呼叫是指呼叫結果回傳之前,當前執行緒會被掛起,呼叫執行緒只有在得到結果之后才會回傳,
-
非阻塞:呼叫指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒,
同步才有阻塞和非阻塞之分;阻塞與非阻塞關乎如何對待事情產生的結果(阻塞:不等到想要的結果我就不走了)
2.明確行程狀態
理解行程的狀態轉換
-
就緒狀態 -> 運行狀態:處于就緒狀態的行程被調度后,獲得CPU資源(分派CPU時間片),于是行程由就緒狀態轉換為運行狀態,
-
運行狀態 -> 就緒狀態:處于運行狀態的行程在時間片用完后,不得不讓出CPU,從而行程由運行狀態轉換為就緒狀態,此外,在可剝奪的作業系統中,當有更高優先級的行程就
、 緒時,調度程度將正執行的行程轉換為就緒狀態,讓更高優先級的行程執行, -
運行狀態 -> 阻塞狀態:當行程請求某一資源(如外設)的使用和分配或等待某一事件的發生(如I/O操作的完成)時,它就從運行狀態轉換為阻塞狀態,行程以系統呼叫的形式請求作業系統提供服務,這是一種特殊的、由運行用戶態程式呼叫作業系統內核程序的形式,
-
阻塞狀態 -> 就緒狀態:當行程等待的事件到來時,如I/O操作結束或中斷結束時,中斷處理程式必須把相應行程的狀態由阻塞狀態轉換為就緒狀態,

小編推薦自己的Linux、C/C++技術交流群:【960994558】整理了一些個人覺得比較好的學習書籍、視頻資料共享在里面(包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等等.),有需要的可以自行添加哦!~

3.從作業系統層面執行應用程式理解 IO 模型
阻塞IO模型:
-
簡介:行程會一直阻塞,直到資料拷貝完成應用程式呼叫一個IO函式,導致應用程式阻塞,等待資料準備好,
如果資料沒有準備好,一直等待….資料準備好了,從內核拷貝到用戶空間,IO函式回傳成功指示, 我們 第一次接觸到的網路編程都是從
listen()、send()、recv()等介面開始的,使用這些介面可以很方便的構建服務器 /客戶機的模型, -
阻塞I/O模型圖:在呼叫recv()/recvfrom()函式時,發生在內核中等待資料和復制資料的程序,

當呼叫recv()函式時,系統首先查是否有準備好的資料,如果資料沒有準備好,那么系統就處于等待狀態,當資料準備好后,將資料從系統緩沖區復制到用戶空間,然后該函式回傳,在套接應用程式中,當呼叫recv()函式時,未必用戶空間就已經存在資料,那么此時recv()函式就會處于等待狀態,阻塞模式給網路編程帶來了一個很大的問題,如在呼叫 send()的同時,執行緒將被阻塞,在此期間,執行緒將無法執行任何運算或回應任何的網路請求,這給多客戶機、多業務邏輯的網路編程帶來了挑戰,這時,我們可能會選擇多執行緒的方式來解決這個問題,應對多客戶機的網路應用,最簡單的解決方式是在服務器端使用多執行緒(或多行程),多執行緒(或多行程)的目的是讓每個連接都擁有獨立的執行緒(或行程),這樣任何一個連接的阻塞都不會影響其他的連接,具體使用多行程還是多執行緒,并沒有一個特定的模式,傳統意義上,行程的開銷要遠遠大于執行緒,所以,如果需要同時為較多的客戶機提供服務,則不推薦使用多行程;如果單個服務執行體需要消耗較多的
CPU 資源,譬如需要進行大規模或長時間的資料運算或檔案訪問,則行程較為安全,
非阻塞IO模型
- 簡介:非阻塞IO通過行程反復呼叫IO函式(多次系統呼叫,并馬上回傳);在資料拷貝的程序中,行程是阻塞的;
我們把一個SOCKET介面設定為非阻塞就是告訴內核,當所請求的I/O操作無法完成時,不要將行程睡眠,而是回傳一個錯誤,這樣我們的I/O操作函式將不斷的測驗資料是否已經準備好,如果沒有準備好,繼續測驗,直到資料準備好為止,在這個不斷測驗的程序中,會大量的占用CPU的時間,

IO復用模型:
- 簡介:IO multiplexing就是我們說的select,poll,epoll,有些地方也稱這種IO方式為event driven
IO,select/epoll的好處就在于單個process就可以同時處理多個網路連接的
IO,它的基本原理就是select,poll,epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有資料到達了,就通知用戶行程,

當用戶行程呼叫了select,那么整個行程會被block,而同時,kernel會“監視”所有select負責的socket,當任何一個socket中的資料準備好了,select就會回傳,這個時候用戶行程再呼叫read操作,將資料從kernel拷貝到用戶行程,所以,I/O 多路復用的特點是通過一種機制一個行程能同時等待多個檔案描述符,而這些檔案描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select()函式就可以回傳,
異步IO模型
- 簡介:用戶行程發起read操作之后,立刻就可以開始去做其它的事,而另一方面,從kernel的角度,當它受到一個asynchronous
read之后,首先它會立刻回傳,所以不會對用戶行程產生任何block,然后,kernel會等待資料準備完成,然后將資料拷貝到用戶記憶體,當這一切都完成之后,kernel會給用戶行程發送一個signal,告訴它read操作完成了,

4.區別IO多路復用中的select poll epoll
select
- int select (int n, fd_set *readfds, fd_set writefds, fd_setexceptfds, struct timeval *timeout); select 函式監視的檔案描述符分3類,分別是writefds、readfds、和exceptfds,呼叫后select函式會阻塞,直到有描述符就緒(有資料 可讀、可寫、或者有except),或者超時(timeout指定等待時間,如果立即回傳設為null即可),函式回傳,當select函式回傳后,可以 通過遍歷fdset,來找到就緒的描述符
poll
- int poll (struct pollfd *fds, unsigned int nfds, int timeout); 不同與select使用三個位圖來表示三個fdset的方式,poll使用一個pollfd的指標實作,pollfd并沒有最大數量限制(但是數量過大后性能也是會下降), 和select函式一樣,poll回傳后,需要輪詢pollfd來獲取就緒的描述符,
epoll
-
epoll是通過事件的就緒通知方式,呼叫epoll_create創建實體,呼叫epoll_ctl添加或洗掉監控的檔案描述符,呼叫epoll_wait阻塞住,直到有就緒的檔案描述符,通過epoll_event引數回傳就緒狀態的檔案描述符和事件,
-
epoll操作程序需要三個介面,分別如下: int epoll_create(int
size);//創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大 生成一個
epoll專用的檔案描述符,其實是申請一個內核空間,用來存放想關注的 socket fd 上是否發生以及發生了什么事件, -
int epoll_ctl(int epfd, int op, int fd, struct epoll_event
*event);控制某個 epoll 檔案描述符上的事件:注冊、修改、洗掉,其中引數 epfd 是 epoll_create() 創建 epoll 專用的檔案描述符, -
int epoll_wait(int epfd, struct epoll_event * events, int maxevents,
int timeout);等待 I/O 事件的發生;回傳發生事件數,
引數說明:
epfd: 由 epoll_create() 生成的Epoll 專用的檔案描述符;
epoll_event:用于回傳代處理事件的陣列;
maxevents: 每次能處理的事件數;
timeout: 等待 I/O 事件發生的超時值;
區別總結
- select,poll實作需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替,而epoll其實也需要呼叫epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,呼叫回呼函式,把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進入睡眠的行程,雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間,這就是回呼機制帶來的性能提升,
- select,poll每次呼叫都要把fd集合從用戶態往內核態拷貝一次,epoll通過 mmap把內核空間和用戶空間映射到同一塊記憶體,省去了拷貝的操作,
應用舉例
- Tornado:
使用單執行緒的方式,避免執行緒切換的性能開銷,同時避免在使用一些函式介面時出現執行緒不安全的情況支持異步非阻塞網路IO模型,避免主行程阻塞等待,
tornado 的 IOLoop 模塊 是異步機制的核心,它包含了一系列已經打開的檔案描述符和每個描述符的處理器 (handlers),這些 handlers 就是對 select, poll , epoll等的封裝,(所以本質上說是 IO 復用)
- Django
沒有用異步,通過使用多行程的WSGI server(比如uWSGI)來實作并發,這也是WSGI普遍的做法,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/237692.html
標籤:其他
