目錄
0x01 阻塞與非阻塞、同步與異步
阻塞與非阻塞
同步與異步
總結(jié)
0x02 Unix、Linux上的五種IO模型
阻塞(blocking)
非阻塞(non-blocking——NIO)
IO復(fù)用(IO multiplexing)
信號驅(qū)動(signal-driven)
異步(asynchronous)
0x01 阻塞與非阻塞、同步與異步
為了理清楚這幾個概念,我們可以從數(shù)據(jù)就緒以及數(shù)據(jù)讀寫層面來看待這個問題。
阻塞與非阻塞
在數(shù)據(jù)就緒層面,需要考慮阻塞與非阻塞的問題。比如我們需要在操作系統(tǒng)中調(diào)用一個網(wǎng)絡(luò)層的recv
函數(shù),這個函數(shù)可以設(shè)置為阻塞以及非阻塞,阻塞顧名思義是我們需要等待這個消息的傳入到TCP數(shù)據(jù)緩沖區(qū),否則我們將一直把這個線程掛起。如果設(shè)置為非阻塞,那么我們不管數(shù)據(jù)是否已經(jīng)讀取到,我們都會不斷的去讀取這個TCP緩沖區(qū)是否有數(shù)據(jù)到達(dá),這個時候程序一直處于不斷地去判斷這個函數(shù)的返回值的狀態(tài)中,這個時候一直在消耗著CPU的資源:
ssize_t recv(int sockfd,void *buf,size_t len,int flags);
int size = recv(sockfd,buf,1024,0);
對于recv函數(shù),這兩種狀態(tài)不一樣在于其返回值的判斷:如果這個時候出現(xiàn)了出錯的狀態(tài),也就是變量size出現(xiàn)-1值得時候,這個時候我們需要去判斷出現(xiàn)這個-1的情況所對應(yīng)的類型,通過數(shù)據(jù)手冊說明可以得到這個狀態(tài)有如下:EINTR、EAGAIN、EWOULDBLOCK
,對于非阻塞的情況,它會是處于一個EAGAIN
的狀況,對于阻塞的情況,則是EINTTR
,一個會讓線程繼續(xù)接收消息,一個則會讓線程直接退出狀態(tài)。(這個處理會在后面的講解中細(xì)說)
那么我們現(xiàn)在就可以很清楚的總結(jié)出阻塞與非阻塞的區(qū)別:
-
阻塞和非阻塞這兩個概念與程序(線程)等待消息通知(無所謂同步或者異步)時的狀態(tài)有關(guān)。也就是說阻塞與非阻塞主要是程序(線程)等待消息通知時的狀態(tài)角度來說的。
-
阻塞和非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時的狀態(tài)。
-
阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會被掛起。調(diào)用線程只有在得到結(jié)果之后才會返回。
-
非阻塞調(diào)用指在不能立刻得到結(jié)果之前,不會改變線程的狀態(tài),可以通過返回值判斷當(dāng)前是否有數(shù)據(jù)可以進(jìn)行讀寫。
同步與異步
-
所謂同步,就是發(fā)出一個功能調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用就不返回或繼續(xù)執(zhí)行后續(xù)操作。
-
異步與同步相對,當(dāng)一個異步過程調(diào)用發(fā)出后,調(diào)用者在沒有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作。
舉個例子,在我們應(yīng)用程序中,我們現(xiàn)在需要讀取TCP緩沖區(qū)中的數(shù)據(jù),這個時候我們需要自己調(diào)用一些接口去自行讀取數(shù)據(jù)(具體實(shí)現(xiàn)取決于底層),當(dāng)這些數(shù)據(jù)還未讀取完時,我們不可以去進(jìn)行下一步操作,這個過程我們稱之為同步,這個時候數(shù)據(jù)的處理一直是應(yīng)用程序在處理的。那么對于異步,這個時候我們設(shè)置一個異步接口,傳入了我們需要讀取的文件描述符、數(shù)據(jù)存儲地址、通知方式(如signal信號等),之后我們就可以繼續(xù)執(zhí)行我們的程序了,直到有通知出現(xiàn),我們才去取數(shù)據(jù),這種無需我們自行等待數(shù)據(jù)的方式我們可以稱之為異步,在數(shù)據(jù)處理的時候消耗的并非應(yīng)用程序的時間,在異步的處理中,應(yīng)用程序可以自己處理自己的事情。異步IO諸如:aio_read()/aio_write()(Linux異步IO接口)。
總結(jié)
在處理IO的時候,阻塞和非阻塞都是同步IO,只有使用了特殊的API才是異步IO。注意:IO多路復(fù)用技術(shù)都是同步。
一個典型的網(wǎng)絡(luò)IO接口調(diào)用,分為兩個階段,分別是“數(shù)據(jù)就緒”和“數(shù)據(jù)讀寫”,數(shù)據(jù)就緒分為阻塞和非阻塞,表現(xiàn)的到的結(jié)果就是阻塞當(dāng)前線程或直接返回。
同步表示A向B請求調(diào)用一個網(wǎng)絡(luò)的IO接口時(或者調(diào)用某個業(yè)務(wù)邏輯API接口時),數(shù)據(jù)的讀寫都是由請求方A自己來完成的(不管是阻塞還是非阻塞);異步表示A向B請求調(diào)用一個網(wǎng)絡(luò)接口時(或者調(diào)用某個業(yè)務(wù)邏輯API接口時),向B傳入請求事件以及事件發(fā)生時通知的方式,A就可以處理其他邏輯了,當(dāng)B監(jiān)聽到事件處理完成后,會用事先約定好的通知方式,通知A處理結(jié)果。
0x02 Unix、Linux上的五種IO模型
阻塞(blocking)
調(diào)用者調(diào)用了某個函數(shù),等待這個函數(shù)返回,期間什么也不做,不停的去檢查這個函數(shù)有沒有返回,必須等這個函數(shù)返回才能進(jìn)行下一步動作。
比如函數(shù)read、recv,這與文件描述符有關(guān),直接可以去文件描述符中設(shè)置其阻塞或者非阻塞。這也是個同步IO的接口。以上這種方式只能一次檢測一個事件,相當(dāng)于是單線程。
非阻塞(non-blocking——NIO)
非阻塞等待,每隔一段時間就去檢測IO事件是否就緒。沒有就緒就可以做其他事。非阻塞I/O執(zhí)行系統(tǒng)調(diào) 用總是立即返回,不管事件是否已經(jīng)發(fā)生,若事件沒有發(fā)生,則返回-1,此時可以根據(jù) errno 區(qū)分這兩種情況,對于accept,recv 和 send,事件未發(fā)生時,errno 通常被設(shè)置成 EAGAIN/EWOULDBLOCK。
?
這也是一個同步的IO,數(shù)據(jù)處理等待還是在用戶態(tài)。以上這種方式只能一次檢測一個事件,相當(dāng)于是單線程。
IO復(fù)用(IO multiplexing)
Linux 用 select/poll/epoll 函數(shù)實(shí)現(xiàn) IO 復(fù)用模型,這些函數(shù)也會使進(jìn)程阻塞,但是和阻塞IO所不同的是這些函數(shù)可以同時阻塞多個IO操作。而且可以同時對多個讀操作、寫操作的IO函數(shù)進(jìn)行檢測。直到有數(shù)據(jù)可讀或可寫時,才真正調(diào)用IO操作函數(shù)。
?這種類似于多進(jìn)程多線程的處理。但是也是同步的機(jī)制。
信號驅(qū)動(signal-driven)
Linux 用套接口進(jìn)行信號驅(qū)動 IO,安裝一個信號處理函數(shù),進(jìn)程繼續(xù)運(yùn)行并不阻塞,當(dāng)IO事件就緒,進(jìn)程收到SIGIO 信號,然后處理 IO 事件。
?內(nèi)核在第一個階段是異步,在第二個階段是同步,因?yàn)檫@個時候的數(shù)據(jù)拷貝還是在用戶空間進(jìn)行的;與非阻塞IO的區(qū)別在于它提供了消息通知機(jī)制,不需要用戶進(jìn)程不斷的輪詢檢查,減少了系統(tǒng)API的調(diào)用次數(shù),提高了效率。
異步(asynchronous)
Linux中,可以調(diào)用 aio_read 函數(shù)告訴內(nèi)核描述字緩沖區(qū)指針和緩沖區(qū)的大小、文件偏移及通知的方式,然后立即返回,當(dāng)內(nèi)核將數(shù)據(jù)拷貝到緩沖區(qū)后,再通知應(yīng)用程序。
文章來源:http://www.zghlxwxcb.cn/news/detail-409441.html
?內(nèi)核自動把數(shù)據(jù)傳到用戶空間所規(guī)定的存儲空間中,無需用戶自行拷貝數(shù)據(jù)。使用如下結(jié)構(gòu)體進(jìn)行設(shè)置:文章來源地址http://www.zghlxwxcb.cn/news/detail-409441.html
/* Asynchronous I/O control block. */
struct aiocb
{
int aio_fildes; /* File desriptor. */
int aio_lio_opcode; /* Operation to be performed. */
int aio_reqprio; /* Request priority offset. */
volatile void *aio_buf; /* Location of buffer. */
size_t aio_nbytes; /* Length of transfer. */
struct sigevent aio_sigevent; /* Signal number and value. */
/* Internal members. */
struct aiocb *__next_prio;
int __abs_prio;
int __policy;
int __error_code;
__ssize_t __return_value;
#ifndef __USE_FILE_OFFSET64
__off_t aio_offset; /* File offset. */
char __pad[sizeof (__off64_t) - sizeof (__off_t)];
#else
__off64_t aio_offset; /* File offset. */
#endif
char __glibc_reserved[32];
}
到了這里,關(guān)于Linux搭建Web服務(wù)器(一)——阻塞與非阻塞、同步與異步、Linux五種IO模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!