**這個奇葩/
1. 動機
asynchronous i/o幫助使用者程式提高cpu和io裝置的利用率和提高程式效能,特別是在高負載的io操作下。比如各種**伺服器,資料庫,流伺服器等等。
2. 什麼是aio
很多人會將aio理解成磁碟io的非同步方案,會將aio狹隘化為類epoll介面在磁碟io的特殊化,其實aio應該是橫架於整個核心的介面,它把所有的io包括(本地裝置,網路,管道等)以統一的非同步介面提供給使用者程式,每個子系統都針對介面實現自己的非同步方案,而同步io(synchronous io)只是在核心內部的」aio+blocking」.
3. aio的設計方案
4. 替代方案
5. aio可以做的事
如kernel關於aio的文件中所說,aio可以一次性發出大量的read/write呼叫並且通過通用塊層的io排程來獲得更好的效能,使用者程式也可以減少過多的同步負載,還可以在業務邏輯中更靈活的進行併發控制和負載均衡。
另外相對於其他實現如使用者多執行緒後台同步,glibc等實現也減少了執行緒的負載和上下文切換。
6. aio實現的幾個問題
7. aio實現方案
上圖顯示的sync io在讀寫時的阻塞點,aio試圖在阻塞點採取一些非同步來達到不阻塞的效果。
在03年,suparna bhattacharya提出了aio在linux kernel的設計方案,裡面談到了用full async state machine模型來避免阻塞,把一系列的阻塞點用狀態機來驅動,把使用者態的buffer對映到核心來驅動(舉個簡單的例子,就是memcached的網路io狀態機)。這個模型被應用到linux kernel 2.4.
linux kernel 2.6與2.4是兩個不同的實現模型,2.4的模型雖然達到了很好的非阻塞效果,但與sync io採用兩條不同的**路徑並且非常複雜難以debug(更多的原因?)。
圖來自[linux asynchronous i/o design: evolution & challenges](
linux kernel 2.6使用的retry based模型,也就是把同步io的阻塞點全部exit and retry並且在caller的位址空間上進行。並且早已經把sync io遷移到aio的**路徑上,也就是說,現在sync io使用的就是aio+blocking。
這裡的最大問題是如何訪問task struct,眾所周知,系統呼叫利用current
巨集訪問task struct,但是非同步就導致後續的retry無法再次訪問之前的task struct,因此現在的aio將介面分為提交和執行兩階段,提交階段能訪問current
但是會block,然後執行階段就可以無需訪問current
,最後的結果就造成大量重複的**和函式重複,由於current
的訪問問題。這無疑是核心是無法接受的結果,就如果linux kernel 2.4中的問題一樣。
在07年由於ingo molnar躲開了亂攤子的aio專案,zach brown試圖重新開啟局面,遷移了大量的aio.c的實現到sync io,並且提出了syslet方案,也就是使用核心執行緒讓大部分阻塞點非同步,將sync io和async io**路徑統一,見《the return of syslets》,syslet利用了系統排程的tricks,如果非同步io執行緒阻塞,核心排程就會轉移到其他執行緒,這樣使得如果不阻塞的系統呼叫仍然正常進行並且避免了不必要的額外執行緒,而阻塞的呼叫會轉移到其他執行緒上。syslet的問題就是會改變呼叫程序的id,因為它需要去讓另乙個核心執行緒訪問current
,這個改變會讓核心理解變得晦澀也是無法接受的。在07年後,syslet方案就已經寸步難行了。更多的(syslet/threadlet/fibril)方案陸續湧現。
好吧,先不管aio的介面統一和能不能完全非同步,為什麼直到現在仍然只能使用direct io模式呢(目前aio支援ext2,ext3,ext4等檔案系統)?
aio+dio繞過了vfs快取記憶體固然讓大的資料庫系統如innodb,db2非常高興,然是普通應用仍然無法嘗試它,dio的快取繞過讓每個想要aio的普通應用都需要建立自己的快取系統而不是依賴核心(不建立快取的lighttpd已經證明dio面對sync buffered io仍有效能落差)。
在過去的幾年了,大量的filesystem/buffered aio補丁出現但一直沒進入主線,主要原因還是實現的buffered aio仍然會增加block points,比如write操作,很可能因為記憶體問題導致阻塞。而這對於aio的設計目標是背離的,無法做到非阻塞就無法更好的利用aio。如make io_submit non-blocking很明顯沒有解決阻塞問題,只是單純引入了buffered io,這顯然不是核心想要的。並且io_submit仍舊存在阻塞(disk block的分配),更多的補丁試圖在io_submit的上減少路徑,更快的return。
還有就是buffered io會與dio如何結合並且減少semaphore競爭和meta-data完整性問題《linux aio performance and robustness for
enterprise workloads》。
linux kernel 2.6時期的aio的補丁如fsaio,aio-epoll,posix aio enablement和syslets & threadlets都是沒能進入主線。
總的來說,fs/buffered io的實現非常多,但是在解決aio阻塞點存在問題之前,任何的這類實現都是徒勞的。不要小看可能極少出現的阻塞情況,如果乙個應用依賴於無阻塞的aio,在aio的核心態任何block的行為都會給應用帶來災難,如果應用在實現時就要防備核心的block那麼就會給應用實現上帶來額外的成本還不如不用aio。
8. aio現在
aio這個專案似乎還在泥潭上,從commit log上看lib-aio還是一直在commit,並且表示效能每年都有50%+的提公升,但是似乎還缺少乙個大的動作,讓aio專案擺脫長達十年的陰霾。另外國內的taobao核心似乎也在11年提交了buffered io的bio補丁,不過好像沒有針對使用者執行緒阻塞點的部分?
社群對於aio的io_submit阻塞問題仍舊停頓,從maillist可以得知,總是有使用者對於io_submit有明顯的block的抱怨,社群更希望使用者能找出block位置被希望在user code上解決。
9. 吐槽
其實剛開始我是打算寫一篇aio的概覽和linux kernel aio的全域性描述和區域性特寫,但是寫著的時候,資料查著查著就發現aio這個專案真的有點奇葩,從03年提供了aio專案並且制訂了設計方案,04年ibm的人覺得非同步狀態機的實現跟已存在的**不協調並且太複雜,搞出了retry模型,非常高興的在db2上演示了效能。後來發現retry模型看起來很美,但是永遠存在的block point無法解決弄得ibm的自己都不想整了,oracle oss又出來乙個接管,然後又覺得retry模型有很多問題,表示retry & exit在他們的乙個產品上會有很大效能問題,然後開始弄syslet方案,弄著弄著也崩潰了,留下社群一堆人大眼瞪小眼。
以上對具體問題的解釋可能有問題,因為我也是最近才去了解linux kernel aio的路徑(雖然以前對vfs比較熟悉),對一些遺留歷史問題和目前的困境還在探索。對一些fs io和buffered io等問題沒有特別深入,這水有點深,文件也太少。
參考
Linux的認識存在的一些誤區
1.linux執行很慢 一些dos程式看起來在執行速度上比linux程式要快,這主要是因為dos不是多工作業系統。事實上nt在執行一些程式時,速度也比dos慢,原因是相同的。但是兩個多工作業系統相比,nt遠沒有linux執行得快。2.linux會頻繁崩潰 有這種想法的使用者,一定對linux不是很了...
關於 linux 的一些發音
作業系統相關 unix ju niks 發音 yew nicks 尤里克斯 gnu g nju 發音 guh noo 葛扭linux li n ks 裡那克斯 gnome g no m 發音 guh nome 葛擩呣debian debi n 發音 deb e un ubuntu b nt 發音 o...
一些關於Linux 的認識
1.linux的歷史 linux是乙個開源的作業系統,它最初是芬蘭的helsinki大學的一位年輕的學生linux torvalds作為愛好開發的。linus對minix 乙個小型的unix系統 很感興趣,並且決定開發乙個比minix更好的系統。他在1991年開始開發並且當年發布0.02版本,他堅持...