程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> [轉]重疊IO

[轉]重疊IO

編輯:C++入門知識

本章回答了如下幾個問題:

  ◆ 什麼是Overlapped I/O?為什麼需要Overlapped I/O?如何讓數據傳輸支持Overlapped I/O?

  ◆ 數據傳輸結束後,Win32提供了哪些方式對用戶進行通告,以便進行適當的善後?

  ◆ 影響線程優先級的因素有哪些?如何獲取或設置進程線程優先級?優先級的改變容易帶來哪些問題?又該如何應對?

  ◆ 什麼是被激發的文件句柄?什麼是被激發的事件?什麼是異步進程調用(APCs)?這些方式各是如何實現Overlapped I/O的?各有何優缺點?

  ◆ 使用Overlapped I/O的初衷是使“受制於I/O的程序”中獲得高效率。但是否是各種情況下Overlapped I/O都能提高系統效率嗎?

  ◆ 什麼是I/O completion port?這個機制是怎麼工作的?有什麼優點?為什麼說此方式能夠很好地支持scalable(可升級的)系統?而對於工作於此模式下的文件,從提高系統效率考慮,怎樣才能避免無謂的completion packet通告呢?

 

什麼是Overlapped I/O?

Overlapped I/O,常被設計為多線程處理,以便在一個“受制於I/O的程序”中獲得高效率。

利用Win32所謂的overlapped I/O特性,可以讓I/O操作並行處理,並且當一個I/O完成時,程序會收到一個通告。有些系統把這個特性稱為非阻塞I/O,或異步I/O。

overlapped I/O是Win32的一項技術,你可以要求操作系統為你傳送數據,並且傳送完畢後通知你。這項技術使你的程序在I/O進行中仍然能夠繼續處理事務。事實上,操作系統內部正是以線程來完成overlapped I/O。這樣,你可以獲得線程的所有利益,而不需付出痛苦代價。

 

在Windows 95環境下,Overlapped I/O使用有些限制。它不支持磁盤或光盤中的文件操作。

 

Win32文件操作函數

HANDLE CreateFile(

LPCTSTR lpFileName,          // pointer to name of the file

DWORD dwDesiredAccess,       // access (read-write) mode

DWORD dwShareMode,           // share mode

LPSECURITY_ATTRIBUTES lpSecurityAttributes,// pointer to security attributes

DWORD dwCreationDisposition,  // how to create

DWORD dwFlagsAndAttributes,  // file attributes

HANDLE hTemplateFile         // handle to file with attributes to copy

);

BOOL ReadFile(

HANDLE hFile,                // handle of file to read

LPVOID lpBuffer,             // pointer to buffer that receives data

DWORD nNumberOfBytesToRead,  // number of bytes to read

LPDWORD lpNumberOfBytesRead, // pointer to number of bytes read

LPOVERLAPPED lpOverlapped    // pointer to structure for data

);

BOOL WriteFile(

HANDLE hFile,                    // handle to file to write to

LPCVOID lpBuffer,                // pointer to data to write to file

DWORD nNumberOfBytesToWrite,     // number of bytes to write

LPDWORD lpNumberOfBytesWritten,  // pointer to number of bytes written

LPOVERLAPPED lpOverlapped        // pointer to structure for overlapped I/O

);

BOOL CloseHandle(

HANDLE hObject   // handle to object to close

);

 

一些說明:

CreateFile()可處理的對象包括:files、pipes、mailslots、communication resource、disk device(Windows NT only)、consoles、directories(open only)。

如果要使用異步I/O,CreateFile()時設置dwFlagsAndAttributes含FILE_FLAG_OVERLAPPED標志,通知操作系統對此文件的訪問采取異步I/O模式。

由於異步I/O模式下,可以在同一時間讀(或寫)文件的許多部分,所以沒有“目前的文件位置”這一概念。每次讀寫都必須包含其文件位置。

 

OVERLAPPED結構

typedef struct _OVERLAPPED {

DWORD  Internal;

DWORD  InternalHigh;

DWORD  Offset;

DWORD  OffsetHigh;

HANDLE hEvent;

} OVERLAPPED;

 

說明:

Internal

通常它被保留。然而,當GetOverlappedResult()返回FALSE,GetLastError()返回ERROR_IO_PENDING時,這個欄位將內含一個視系統而定的狀態。

 

InternalHigh

通常它被保留。然而,當GetOverlappedResult()返回TRUE時,這個欄位將內含“被傳輸的數據長度”。

 

Offset

定義文件開始讀(或寫)的開始位置,以字節為單位。該偏離位置從文件頭開始起算。如果目標設備不支持文件位置(比如管道),此欄忽略。

 

OffsetHigh

64位的文件偏離量,較高的32位。如果目標設備不支持文件位置(比如管道),此欄忽略。

 

hEvent

一個手動事件。當overlapped I/O完成後即被激發。ReadFileEx()和WriteFileEx()忽略這個欄位,彼時它可能用來傳輸一個用戶自定義的指針。

注意: OVERLAPPED結構的生命周期應超越ReadFile()和WriteFile()函數。

 

被激發的File Handles

注意,此方式在Windows95/98下不被支持。詳看MSDN中關於GetOverlappedResult()函數的說明。

在Windows NT下,最簡單的overlapped I/O類型,是使用它自己的文件句柄作為同步機制。大致流程如下:

BOOL GetOverlappedResult(

HANDLE hFile,                       // handle to file, pipe, or comm device

LPOVERLAPPED lpOverlapped,          // pointer to overlapped structure

LPDWORD lpNumberOfBytesTransferred, // pointer to actual bytes count

BOOL bWait                          // wait flag

);

說明:如果bWait設置為FALSE,如果overlapped I/O還沒完成,函數返回FALSE,調用GetLastError()會返回ERROR_IO_INCOMPLETE。所以,調用GetOverlappedResult()時如果bWait設置為FALSE,可即時查詢overlapped I/O的狀態。

 

舉例:

hFile = CreateFile( szPath,

GENERIC_READ,

FILE_SHARE_READ|FILE_SHARE_WRITE,

NULL,

OPEN_EXISTING,

FILE_FLAG_OVERLAPPED,

NULL);

if (hFile == INVALID_HANDLE_VALUE)

{

return -1;

}

 

// Initialize the OVERLAPPED structure

memset(&overlap, 0, sizeof(overlap));

overlap.Offset = 1500;

 

// Request the data

rc = ReadFile( hFile,buf,READ_SIZE,&numread,&overlap);

 

if (rc)

{

// The data was read successfully

}

else

{

if (GetLastError() == ERROR_IO_PENDING)

{

VERIFY(WAIT_OBJECT_0 == WaitForSingleObject(hFile, INFINITE));

rc = GetOverlappedResult(hFile, &overlap,&numread,FALSE);

}

else

{

// Something went wrong

}

}

 

CloseHandle(hFile);

 

說明:

  • 來源:(http://blog.sina.com.cn/s/blog_5678943c0100d6fc.html) - 第六章 Overlapped I/O,在你身後變戲法(1)_流水_新浪博客 雖然你要求一個overlapped操作,但如果數據已被放入cache中,或操作系統認為它可以快速地取得那份你所要求的數據,那麼文件操作會在ReadFile()返回前完成。此時ReadFile()返回TRUE。
  • 如果你要求一個文件操作為overlapped操作,而操作系統把這個“操作請求”放到隊列中等待執行,那麼ReadFile()或WriteFile()都會傳回FALSE以示失敗。但函數返回FALSE有多種原因,可調用GetLastError()調查原因,如果是ERROR_IO_PENDING,那說明“操作請求”被放入隊列等待執行;其它值,那可能真正代表一個錯誤了。
  • OVERLAPPED結構中包含了“這個操作從哪裡開始的信息”,可以處理64位的偏移量。
  • WaitForSingleObject(hFile, INFINITE); rc = GetOverlappedResult(hFile, &overlap,&numread,FALSE);其實可合並為一句:rc = GetOverlappedResult(hFile, &overlap,&numread,TRUE);

 

被激發的事件對象

使用文件句柄作為激發機制,有一個明顯的限制:如果多個線程對同一個文件進行操作,由於只有一個相同的handle,對於每個可能進行的overlapped操作都調用GetOverlappedResult()查看操作是否完成,這將不是一個很有效率的做法——因為很多的時候並不是自己所期待的操作完成了。另外,Windows95/98下不可以使用文件句柄作為激發機制。

 

OVERLAPPED結構中的最後一個欄位是一個事件句柄。如果使用文件句柄作為激發對象,可將該位設置為NULL。如果該位被設定為一個事件對象時,系統核心會在overlapped操作完成後,自動設置此事件為激發。

由於每個overlapped操作都有它獨一無二的OVERLAPPED結構,每個結構都有它獨一無二的事件對象,用以代表該操作。

 

注意:OVERLAPPED結構中的事件必須是手動事件。否則,如果事件為自動事件,由於系統核心可能會在你有機會等待該事件前就激發它,而自動事件的激發狀態是不能保留的,於是事件遺失,這將導致你的等待永遠無法返回。

 

使用手動事件配搭overlapped I/O,就可以對同一個文件發出多個讀取操作和多個寫入操作,每個操作都有自己的事件對象。然後調用wait…()函數等待其中之一或全部完成。

 

異步過程調用(Asynchronous Procedure Calls,APCs)

使用overlapped I/O配搭手動事件,會產生兩個問題:

異步過程調用可解決此問題。此時只要使用“Ex”版的ReadFile()和WriteFile()。這兩函數可額外指定一個參數,定義一個callback函數。當一個overlapped I/O完成時,系統調用該callback函數。這個callback函數被稱為I/O completion routine,因為,系統是在一個特定的overlapped I/O完成後調用它的。

 

但是,需要注意的是,一個特定的overlapped I/O完成後,Windows並不會貿然中斷你的程序,然後調用你所提供的callback函數。那顯然可能會帶來新的問題。只有線程說“好,現在是一個安全時機”時系統才會調用你的callback函數。以Windows的說法,只有線程處於所謂的“alertabe”狀態,回調函數才會被執行,否則對I/O completion routine的調用會被暫時擱置下來。因此,當一個線程終於處於“alertabe”狀態時,可能有一堆儲備的APCs等待被處理。

 

如果線程因為以下5個函數而處於等待狀態,而其“alertabe”標志被設置為TRUE,則該現程就是處於“alertabe”狀態:

DWORD SleepEx(

DWORD dwMilliseconds,  // time-out interval in milliseconds

BOOL bAlertable        // early completion flag

);

DWORD WaitForSingleObjectEx(

HANDLE hHandle,        // handle to object to wait for

DWORD dwMilliseconds,  // time-out interval, in milliseconds

BOOL bAlertable        // return to execute I/O completion routine if TRUE

);

DWORD WaitForMultipleObjectsEx(

DWORD nCount,             // number of handles in handle array

CONST HANDLE *lpHandles,  // points to the object-handle array

BOOL fWaitAll,            // wait flag

DWORD dwMilliseconds,     // time-out interval in milliseconds

BOOL bAlertable           // alertable wait flag

)

DWORD MsgWaitForMultipleObjectsEx(

DWORD nCount,          // number of handles in handle array

LPHANDLE pHandles,     // pointer to an object-handle array

DWORD dwMilliseconds,  // time-out interval in milliseconds

DWORD dwWakeMask,      // type of input events to wait for

DWORD dwFlags          // wait flags

);

DWORD SignalObjectAndWait(

HANDLE hObjectToSignal,  // handle to object to signal

HANDLE hObjectToWaitOn,  // handle to object to wait for

DWORD dwMilliseconds,    // time-out interval in milliseconds

BOOL bAlertable          // alertable flag

);

只有使用上面這些函數進行等待ReadFileEx()或WriteFileEx()操作,回調函數才會被執行。

 

回調函數解釋

VOID CALLBACK FileIOCompletionRoutine(

DWORD dwErrorCode,                // completion code

DWORD dwNumberOfBytesTransfered,  // number of bytes transferred

LPOVERLAPPED lpOverlapped         // pointer to structure with I/O information

);

dwErrorCode:0表示操作完成,ERROR_HANDLE_EOF表示操作到文件尾

dwNumberOfBytesTransfered:真正被傳輸的數據字節數

lpOverlapped:指向OVERLAPPED結構,由開啟overlapped I/O操作的函數提供。由於APCs時OVERLAPPED結構中hEvent欄位不需要用來放置一個事件句柄,此欄程序員可自由運用,比如用作回調函數的入口參數。

 

對文件進行Overlapped I/O的缺點

在Windows NT測試時發現,Windows NT似乎是以“I/O請求”的大小來決定是否進行overlapped I/O。如果數據量較小,系統實際上總是采取非overlapped方式進行文件讀取;數據量略大些,采取Overlapped I/O其實比單純調用ReadFile()並無優越性,相反地,效率降低。如果文件數據量比較大,Overlapped I/O才能凸顯其優越性。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved