生活随笔
收集整理的這篇文章主要介紹了
异步IO实现和应用场景
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言
學習自用,還有很多不足的地方
異步IO和同步IO
當網卡有了數據,DMA會把數據拷貝到內核緩沖區(內核緩沖區的哪里呢);而從內核緩沖區拷貝到用戶態需要用戶調用read,同步地進行
異步則是注冊個讀完成事件,等其他用戶態線程/內核進程拷貝到用戶態后再提醒
aio最核心的需求就是解偶submit和wait for completion, 簡單地說就是在submit的時候不要阻塞, 這樣用戶在submit之后可以做其它事情或者繼續submit新的io, 從而獲得更高的cpu利用率和磁盤帶寬.
異步IO的好處
同步read從內核區拷貝到用戶態需要等待短暫的時間,全異步的aio_read不存在這個短暫時間(同步read的系統調用和從內核拷貝到用戶的時間),效率更高;異步IO一個例子就是nginx的讀文件的操作(?)那在內核上處理的真正異步io的優勢在于:和同步非阻塞io相比,優勢主要在于不用維護數據讀不齊時的邏輯處理(可能是等到讀齊了再通知吧)
異步IO的壞處
要提前規定好緩沖區大小以在數據到來時自動拷貝,如果設置太大會浪費,太小又有問題,而同步IO可以在讀取時適時增加和釋放空間,比如muduo就在read的同時用writev增加棧上buffer大小另外異步IO的編程會麻煩一點,調用aio_read后還要有等待完成讀取的邏輯,看看nginx里面文件操作就知道用起來多痛苦了(不過應該可以優化,用promise/future或await來優化?..那實現得更麻煩了)
Linux的AIO
POSIX的AIO(應該就是glic的aio),是用pthread實現的用回調或signal通知的,沒有connect,accept等網絡IO的API?kernel 的 aio,即kernel native AIO,被用在了nginx上,有一個封裝libaio,一開始是為了數據庫涉及的,所以只能以 O_DIRECT 方式做直接 IO,即跳過page cache直接寫入磁盤(數據庫應用這種實現如INNODB,它在應用層實現自己的cache機制而不用OS的page cache),這就導致一些問題如data buffer的對齊,它要求讀寫的的大小和偏移要以區塊的方式對齊即offset與size的對齊(都是啥啊?),估計普通用戶不會愿意觸及這個。除了數據庫的開發者。而且網絡API一樣沒有。另外kernel native AIO存在額外的開銷,每個IO提交需要拷貝64+8字節,每個IO完成需要拷貝32字節,這在某些場景下影響很可觀。在使用完成event的時候需要非常小心,否則容易丟事件。IO總是需要至少2個系統調用(submit + wait-for-completion),在spectre/meltdown開啟下性能下降非常嚴重。還有一個libev作者搞的libeio,在beta階段,設計上汲取了前兩個的經驗用了一些tricky的辦法,也是用線程在用戶態模擬的,bug更少但是代碼更難看懂。封裝了大量的文件API ,但也沒有網絡API。而且有bug的,不能用在 socket 讀寫上,而且不會返回顯式錯誤,而是自動切成非異步 IO最新內核引入的io_uring可能可以統一網絡和磁盤的aio模式。
AIO實現
AIO復制數據從內核態到用戶態的時候,可能會發生一些問題阻塞住,如申請用戶態的空間不夠,請求隊列slot和inode的semaphore(是啥?)
用何種方式來提醒讀寫完成呢,
- 信號IO不僅在大量IO操作時可能會因為信號隊列溢出導致沒法通知(?)
- 定義一個回調函數,用sigfd也可以,則需要用eventloop來進行監聽,復雜的話可以用promise/future來簡化語法
- Filesystem/Buffered AIO: Buffered IO與Direct IO的資源沖突和數據完整問題(??)
內核線程來或者單獨用一個線程進行read操作,(用多線程會有多個讀一個fd的問題),要維護read的隊列,傳入讀出來存的地方
猜想
猜想:如果大量不斷的連接到來事件然后斷開...
listen socket的可讀事件,消息socket可讀事件,如果讓其中一個單獨用一個線程異步處理,則開銷是:邏輯復雜度,另外的線程開銷,活躍線程多的情況會有切換開銷,好處是IO線程不需要用到系統調用read,IO線程阻塞時間變少
猜想:如果大量不斷到來然后發送一小段東西然后斷開...
同理上面吧,這樣的場景更適合異步,因為read時間更長,不過如果一直是消息到來事件,瓶頸就在CPU上,那個異步線程畢竟也是單核處理,如果夾雜著其他不耗時事件到來,這些事件響應可能會變高
異步更高效的場景
IO線程做的事:epoll_wait->分發事件;
如果是大量socket的大量數據且到來,則read耗時長,總結下兩個缺點:
同步read會阻塞epoll_wait的調用,也就延遲了socket的處理,假如同時夾雜著不耗時的socket連接到來事件則客戶端響應會變慢;只能通過每個只讀少量大小的方式來保證socket的及時處理,但這樣不管是LT或ET+robin list都會增加額外系統調用開銷或應用復雜度;read操作從內核搬數據到用戶態無法利用多核,實現得好的話,可以讓多個異步來處理異步IO(多個異步線程這樣實現應該會難吧,還會有跨線程讀同一個fd的問題,如果用epoll_oneshot來解決還會有epoll_ctl系統調用的性能損耗);單核CPU當然是同步更高效;
綜上:多核下的大量socket的大量數據(夾雜著大量耗時少的事件如連接到來事件應該更明顯),應該是異步IO的QPS更高(連接到來的socket和大量數據的socket事件響應,應不應該分開來算啊?)
同步更高效的場景
單核情況下,用異步會多一次上下文切換和一些多出來的步驟
TODO
為什么linux難以實現異步IO,寫完了還不是很懂光明說的,AIO有多個線程操作,第一次事件用AIO_read,但是沒被及時處理,因為LT,所以下次再出發再調用aio_read,線程不同了會有問題吧,也就是多線程處理epoll事件的問題,epoll還提供了oneshot這個標志位?...又說好像問題不大,不過和其他場景可能有問題,就是跟oneshot的問題是一樣的
參考資料
Linux上沒有真正的異步,為什么服務器還是用Linux的多? - 知乎 https://www.zhihu.com/question/41706901linux下的異步IO(AIO)是否已成熟? - 知乎 https://www.zhihu.com/question/26943558如何深刻地理解 Unix/Linux 中同步 IO 和異步 IO? - 知乎 https://www.zhihu.com/question/352921402為什么Linux下沒有真正的異步IO模型? - 知乎 https://www.zhihu.com/question/421584363吐槽linux,AIO的https://www.aikaiyuan.com/4556.html有點復雜,看不太懂https://kernel.taobao.org/2019/06/io_uring-a-new-linux-asynchronous-io-API/最新的io_uringhttps://zhuanlan.zhihu.com/p/149836046一開始linux AIO的設計出發點和io_uring,有點復雜比如阻塞點understanding linux kernel的6.2章節
總結
以上是生活随笔為你收集整理的异步IO实现和应用场景的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。