使用C++ boost從零構建一個異步文件IO系統

前言

  • 由於本科畢業設計中要作一個分佈式文件系統,其中一個模塊須要實現文件IO。爲了驗證我對異步IO的理解,決定造一個異步文件IO的輪子。操做系統已經給出各類異步操做的API,如重疊IO, IOCP,kqueue,select,poll,epoll等機制,並且C++也有不少跨平臺的異步IO庫,如libevent,boost::asio等。我參考已有的實現來完善這個小系統的功能。
  • 渣技術,渣代碼,該日誌也只是做爲這個工程的記錄,出現問題請各位指出。

概述

  • 同步與異步:

  同步:假如我想對一個文件(socket也同理)進行處理,那麼通常的流程就是:git

  

1 fstream file;
2 file.open();
3 file.read();
4 //do something
5 file.close();

  一般狀況下,當這個線程運行到read()時會被阻塞,直到文件讀取完成。github

  異步:數據結構

  仍是上面的代碼,我在read()時經過操做系統或庫提供的異步機制,告訴操做系統我想讀一個文件,數據讀完後執行某個功能;而當前線程在交代完操做系統該作什麼工做以後,還能夠作些別的事情(線程沒必要等待文件IO完成)。負載均衡

  • 線程池

  爲了不IO阻塞線程致使程序無響應,徹底能夠爲每個文件操做建立一個線程,這樣就能夠同時處理多個文件了。可是建立線程,切換線程,銷燬線程也是一筆資源開銷,若是想重複使用已有的線程,就可使用線程池。做爲線程池,至少要提供建立線程和提交任務的功能,複雜一點能夠智能控制線程池裏的線程數量,還應該具備基本的負載均衡功能。這個文件系統中就會使用線程池。Windows API中的ThreadPool就很好用,但既然是造輪子,那麼爲了造輪子而造輪子也沒什麼關係,乾脆就寫個簡單的線程池出來。異步

  • 使用的庫

  這個模塊只使用stl 和boost 兩個庫。socket

  stl主要涉及容器和fstream。boost涉及到智能指針shared_ptr,線程同步shared_mutex,lock_guard, boost::filesystem中的path和一些文件操做,線程操做建立退出等。async

  •  智能指針

  自古以來內存管理都是C/C++中的重頭戲,智能指針的功能就是分配出來的內存由庫管理,若是某個智能指針指向的內存,經過其餘的智能指針也能訪問到(即有多個引用),那麼該智能指針即時被銷燬,指向的內存也不會銷燬;只有這塊內存沒有引用,纔會被庫釋放。分佈式

  • boost::filesystem

  這個庫提供了一些跨平臺文件操做的API,如文件夾遍歷,查看屬性,刪除文件等。path類能夠記錄跨平臺的路徑。函數

  • 由於水平有限,這個模塊基本不會出現跟模板有關的實現。(之後再說)

實現

  AsyncStatus是異步IO中須要實現的功能。像讀,寫,放棄異步操做,錯誤處理等。ui

  ErrorCode會出如今回調函數中,表示以前異步讀寫的結果,如正在處理,出錯,EOF等。

  FS_Handle_ST:這個結構體對應一個文件路徑。在系統中每一個handle都是惟一的,系統有一個map,經過handle能夠找到它對應的路徑。

  FS_AsyncHandle_ST:標識某個handle須要執行的任務,每一個AsyncHandle都須要指定status即任務。一個handle能夠有多個異步任務,但多個任務在系統中按照隊列順序執行。

 1     enum QueueOperation { PUSH_BACK, PUSH_FRONT };
 2     enum AsyncStatus { NONE, APPEND_WRITE, WRITE, READ, READ_ALL, ABORT, ERROR, EXIT };
 3     enum ErrorCode { DONE, PENDING, END_OF_FILE, OPEN_FAIL, BAD_STREAM, IO_FAIL, UNKNOWN_ERROR };        
 4 
 5     typedef uintmax_t FS_Handle;
 6     typedef uintmax_t FS_AsyncHandle;
 7 
 8     struct FS_AsyncHandle_ST {
 9         FS_Handle fileHandle = 0;
10         FS_AsyncHandle asyncHandle = 0;
11         AsyncStatus status = AsyncStatus::NONE;
12     };
13 
14     struct FS_Handle_ST {
15         FS_Handle handle = 0;
16         boost::filesystem::path fullPath;
17     };        

  回調函數:異步操做完成以後要作什麼。使用的時候把功能在派生類裏實現,重載虛函數run就能夠了。

 1     class FileSystemIOCallback {
 2     public:
 3         void operator=(const FileSystemIOCallback & cb) {
 4             //..
 5         }
 6 
 7         FileSystemIOCallback(const FileSystemIOCallback & cb) {
 8             //..
 9         }
10 
11         FileSystemIOCallback() {
12             //...
13         }
14 
15         virtual ~FileSystemIOCallback() {
16 
17         }
18 
19         virtual void run(const FS_AsyncHandle_ST & ast, ErrorCode e, void * data, uintmax_t count) {
20             //...
21         }
22     };
  •  功能概述

  fstream中有一個很是有趣的函數,readsome()方法,這個方法提出的目的是儘量減小同步讀取的阻塞時間。該方法會讀取fstream內部緩衝區裏的數據,讀取的字節數取決於fstream內部緩衝區和做爲參數傳遞給它的緩衝區的大小。舉個例子,若是fstream緩衝區裏只有10字節,我參數給出的緩衝區大小爲100字節,無論這個文件後面還有沒有數據,它只會去讀10個字節。操做系統和函數庫可能會使用由硬件引起的中斷來讀取數據,運行原理相似,但其具體實現未知,還要進一步瞭解。

  既然IO操做要阻塞線程很長一段時間,那麼就把IO操做扔給線程或纖程,主線程只負責提交任務。

--- 未完待續

相關文章
相關標籤/搜索