對于一次IO訪問(以read舉例),數(shù)據(jù)會先被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中,然后才會從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應(yīng)用程序的內(nèi)存地址空間。所以說,當(dāng)一個read操作發(fā)生時,它會經(jīng)歷兩個階段:
1. 等待內(nèi)核緩沖區(qū)中數(shù)據(jù)準備 (Waiting for the data to be ready)
2. 將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到進程中 (Copying the data from the kernel to the process)
正式因為這兩個階段,linux系統(tǒng)產(chǎn)生了下面五種IO模式的方案:
當(dāng)一個進程需要read數(shù)據(jù)時,它需要先從內(nèi)核緩存區(qū)中讀取數(shù)據(jù),當(dāng)在內(nèi)核中數(shù)據(jù)還未準備好時,則該進程會被阻塞(block),直到數(shù)據(jù)準備好為止。
一個進程需要read數(shù)據(jù)時,它需要先從內(nèi)核緩存區(qū)中讀取數(shù)據(jù),當(dāng)在內(nèi)核中數(shù)據(jù)還未準備好時,內(nèi)核立即返回error,用戶進程得知error后,會繼續(xù)read,直到數(shù)據(jù)準備好為止。
上述阻塞IO和非阻塞IO,當(dāng)時一個用戶進程對應(yīng)一個IO操作,IO多路復(fù)用則通過一定的機制實現(xiàn)了一個進程可以對應(yīng)多個IO操作。
IO多路復(fù)用主要有select、poll、epoll三種模式,select/poll相差不大,主要是通過輪詢來不斷的檢測是否有描述符已就緒,select默認情況下支持最多監(jiān)控1024個描述符,poll則沒有這個限制(底層通過鏈表實現(xiàn),可動態(tài)增加);epoll不是通過輪詢,而是通過回調(diào)(callback)方式主動通知已有描述符已就緒,相比較select/poll效率有明顯提升。
不管哪種模式的IO多路復(fù)用,還都是同步IO,比如當(dāng)監(jiān)控的所有的描述符都還沒有準備就緒,那么該進程還是阻塞在此,下面將介紹異步IO
相比較同步IO,異步IO就顯得更加高大上了,基本原理是用戶進程發(fā)起read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當(dāng)它受到一個asynchronous read之后,首先它會立刻返回,所以不會對用戶進程產(chǎn)生任何block。然后kernel會等待數(shù)據(jù)準備完成,然后將數(shù)據(jù)拷貝到用戶內(nèi)存,當(dāng)這一切都完成之后,kernel會給用戶進程發(fā)送一個signal,告訴它read操作完成了,用戶進程收到signal后獲取已準備好的數(shù)據(jù)。
信號驅(qū)動IO和異步IO類似,甚至有些人認為信號驅(qū)動IO就是異步IO,當(dāng)kernel準備好數(shù)據(jù)后,會發(fā)送signal給用戶進程,告訴進程數(shù)據(jù)已經(jīng)準備就緒,這時進程會主動去read數(shù)據(jù)將數(shù)據(jù)拷貝到用戶內(nèi)存,這一步是由應(yīng)用程序自己操作的,而異步IO則是由kernal實現(xiàn),區(qū)別在此。
nginx在IO多路復(fù)用模式有較成熟的應(yīng)用;
python在異步IO模式上也有相應(yīng)的應(yīng)用,例如gevent、asyncio等。
聯(lián)系客服