Windows作業系統中的I/O(讀/寫 輸入/輸出)

來源:互聯網
上載者:User
導言

寫一個Windows平台下的應用程式大多時候都是離不開讀寫檔案,網路通訊的。
比如一個服務應用程式來說,它可能從網路介面卡接受使用者的請求,對請求進行處理計算,最終將使用者端所需的資料返回,中間可能還涉及到對磁碟的讀寫,這些都是I/O操作,所以,要設計一個穩健的,高效的,伸縮性好的應用程式,就必須將Windows的I/O機制搞清楚。

一、 兩種 讀/寫 機制

輸入Input / 輸出Output,有兩種機制,他們是:

1 同步I/O: 線程執行一個輸入輸出函數時,輸入輸出工作執行完畢後,函數返回繼續執行以後的代碼。
2 非同步I/O: 線程執行一個輸入輸出函數時,函數不等待讀/寫操作完成便立即返回,線程可先去執行下文,執行下文時可查詢剛剛發起的讀/寫操作是否完成。

解讀:

可以看出,非同步I/O是更加高效的,同步I/O將會阻塞線程的執行,不應該被提倡。

有時候我們會認為,我們為每次執行I/O操作的時候,新開一個線程,讓他去執行同步I/O函數,然後我們的原線程去執行工作,這樣做是可以的,但是其實這樣就是我們自己類比實現了非同步I/O,其實我們調用非同步I/O函數的時候,作業系統內部也是維護了一個新線程去為我們進行I/O操作的。

但是,建立線程是一件很耗費CPU資源的事情,而且I/O操作時,CPU需要計算,需要從磁碟或網路介面卡的緩衝區,將資料讀取到記憶體中,或者將記憶體中的資料寫到磁碟或網路介面卡的緩衝區裡,所以,I/O操作的線程不是睡眠的,而是忙碌的,所以我們得出了一個結論,如果我們的電腦上只有兩個CPU,同時執行的I/O線程超過兩個以上時,CPU會因為I/O線程不是睡眠的而分配給他們時間片,於是線程間的頻繁切換,也會降低我們的應用程式的效能。

那麼,我們得出結論:同時執行的I/O線程的數量不應該大於CPU數量,客戶發起的I/O請求需要一個隊列,每次並發處理的I/O操作應該等於CPU數量,如果並發處理的I/O請求數量太多,CPU切換過於頻繁,會將CPU資源浪費線上程切換上,從而嚴重降低程式效能。

二、讀/寫 前後的工作:建立與釋放要讀/寫的裝置的核心對象

要對裝置進行讀/寫,必須Crowdsourced Security Testing道建立一個用於該裝置的讀寫的核心對象,在讀/寫之後,釋放該核心對象。

1. 建立I/O核心對象的函數: CreateFile(...)  與其他核心對象一樣,我們擷取的僅僅是一個控制代碼。
2. 關閉I/O核心對象的函數: CloseHandle(...)

解讀:

HANDLE CreateFile(
LPCTSTR lpFileName,     //要讀寫的裝置
DWORD dwDesiredAccess,  //訪問模式:0(不讀寫,如改變裝置配置)  GENERIC_READ,GENERIC_WRITE(可異或)
DWORD dwShareMode,      //共用模式:FILE_SHARE_DELETE FILE_SHARE_READ FILE_SHARE_WRITE
LPSECURITY_ATTRIBUTES lpSecurityAttributes,  //安全屬性  裡面可設定核心對象的繼承性
DWORD dwCreationDistribution,  //建立方式  CREATE_NEW  CREATE_ALWAYS  OPEN_EXISTING  OPEN_ALWAYS  TRUNCATE_EXISTING
DWORD dwFlagsAndAttributes,    //檔案屬性和標誌
HANDLE hTemplateFile    //檔案屬性模板,如指定了這個參數,則忽略上個參數中的檔案屬性
);

/*
參數 dwFlagsAndAttributes
1)檔案屬性:
  (FILE_ATTRIBUTE_)ARCHIVE COMPRESSED HIDDEN NORMAL OFFLINE READONLY SYSTEM TEMPORARY
2)標誌:
  (FILE_FLAG_)WRITE_THROUGH OVERLAPPED NO_BUFFERING RANDOM_ACCESS SEQUENTIAL_SCAN DELETE_ON_CLOSE BACKUP_SEMANTICS POSIX_SEMANTICS
*/這個CreateFile函數返回了一個檔案核心物件控點,這裡說的檔案不是狹義上的磁碟檔案,也包括一些裝置,比如串口,並口,郵件槽,具名管道和匿名管道,另外,有些裝置控制代碼不是通過CreateFile建立的,下面是一個建立裝置核心對象的函數的說明表。

裝置            建立裝置核心對象的函數

檔案            CreateFile(pszName為路徑名或UNC路徑名)。

邏輯磁碟機       CreateFile(pszName為\\.\x:)。x是盤符,開啟磁碟機可以格式化或者檢測該磁碟機的大小。

物理磁碟機       CreateFile(pszName為\\.\PHYSICALDRIVEx)。x是物理磁碟機序號,比如PHYSICALDRIVE0

串口            CreateFile(pszName為"COMx")

並口            CreateFIle(pszName為"LPTx")

郵件槽伺服器        CreateMailslot(pszName為\\.\mailslot\mailslotname)

郵件槽用戶端        CreateFile(pszName為\\servername\mailslot\mailslotname)

具名管道伺服器       CreateNamedPipe(pszName為\\.\pipe\pipename)

具名管道用戶端       CreateFile(pszName為\\servername\pipe\pipename)

匿名管道          CreatePipe

通訊端           Socket,accept或AcceptEx

控制台           CreateConsoleScreenBuffer或GetStdHandle

以上這些函數,會建立一個I/O核心對象,並取得該對象的控制代碼值,然後我們可以調用與具體裝置相關的函數,並傳入得到的裝置核心物件控點,來和裝置進行通訊。

和其他核心對象一樣,可以調用函數CloseHandle來關閉CreateFile建立的I/O核心對象。

BOOL CloseHandle(HANDLE hObject);

但是如果裝置是通訊端,就必須調用closesocket。

int closesocket(SOCKET s);如果有一個裝置控制代碼,可以通過GetFileType來查處具體的裝置類型。DWORD GetFileType(HANDLE hDevice);
傳回值為:
描述
FILE_TYPE_UNKNOWN 未知類型
FILE_TYPE_DISK 磁碟檔案
FILE_TYPE_CHAR 字元檔案,一般是並口裝置或者控制台裝置
FIEL_TYPE_PIPE 指定的檔案時一個具名管道或匿名管道

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.