眾所周知,CPU是整個電腦系統中運算速度最快的部分,而外部裝置是最慢的部分,它們之間存在著很大的差別。然而,CPU卻時時刻刻可能要求訪問外設。如果CPU的每次操作都必須等待外設完成,那麼CPU寶貴的已耗用時間就會大大浪費。隨著現代電腦技術的發展,大多數現代作業系統都對這個問題進行了處理。下面就介紹Windows 2000中解決這個不匹配問題的方法:快取和非同步傳輸。
1.檔案快取
檔案快取是CPU訪問外設的一個“中間裝置”。說是裝置,其實它不是真正的物理裝置,而是一種核心級記憶體映像機制。由於它被設定在記憶體中,因此速度非常快,可以部分解決CPU與硬碟速度差異的問題。檔案系統的驅動程式通過調用“快取管理程式” 來使用檔案快取,然後快取管理程式執行快取的處理工作。
檔案快取的原理是:假設一個進程讀了檔案的第一個位元組,它常常會按照順序讀第二個第三個位元組,一直到讀出所有的位元組。利用這個原理可以進行“預取”,也就是說,在進程沒請求讀磁碟之前就先把檔案讀出來並放到快取中。這樣,當進程請求訪問磁碟時,快取可以快速地把已經取到記憶體中的檔案內容直接送給進程使用,從而大大加速了訪問磁碟的速度。另外,由於一個檔案可能會被多次讀入,因此可以在第一次讀入後,將檔案資料儲存在快取中。這樣,下次再讀時,就不必從硬碟而可以從緩衝中讀取。利用LRU(Least Recently Used)的原則,可以將不常使用的檔案從緩衝中刪除以節省快取空間。
2.非同步傳輸
與檔案快取不同,檔案的非同步傳輸是一種改變指令執行順序的機制。在以往的作業系統中,指令都是順序執行的,下一條指令必須在上一條指令執行完畢後才能執行。因此,如果CPU遇到一條放盤指令,那麼它就必須等待緩慢的磁碟訪問結束以後才能進行後續工作。如果它後面遇到的指令並不依賴於訪盤操作時,這個等待就很沒有必要。Windows2000中使用了一種非同步檔案傳輸機制來解決這個問題。它通過設定開啟檔案時的一個標誌位來使進程不等待讀些檔案操作而繼續執行。當後續指令必須用到磁碟訪問的結果資料時,它在通過一條Wait指令進行等待。這樣,在訪盤指令和等待指令之間的指令就可以與磁碟訪問同時進行了,從而大大加快了系統的整體速度。
實習要求:
設計一個函數int filter(char source, char *sink, int f),其中:
source:源檔案,即從哪個檔案讀。
sink:目標檔案,即寫到哪個檔案。
f:一個對檔案的操作(可以指定任何操作)
分別用3種方法實現一個對檔案的操作:
1)無緩衝方式:表示用的標誌位是FILE_FLAG_NO_BUFFERING。
2)緩衝方式:表示用的標誌位是FILE_FLAG_SEQUENTIAL_SCAN。
3)非同步方式:表示用的標誌位是FILE_FLAG_OVERLAPPED。
原始碼如下:
#include <iostream.h>
#include <windows.h>
// three partterns
void filter_nobuffer(char *source, char *sink, void (*func)(char *addr));
void filter_sequen(char *source, char *sink, void (*func)(char *addr));
void filter_overlp(char *source, char *sink, void (*func)(char *addr));
// five function operations
void f1(char *addr);
void f2(char *addr);
void f3(char *addr);
void f4(char *addr);
void f5(char *addr);
#define BUFFER_SIZE 1024
char *buffer;
void main()
{
// allocate the buffer
buffer = new char[BUFFER_SIZE];
DWORD tick;
DWORD nobuffer_average_time = 0;
DWORD sequen_average_time = 0;
DWORD overlp_average_time = 0;
cout<<"無檔案快取模式正在運行..."<<endl;
DWORD nobuffer_start_time = GetTickCount();
tick = nobuffer_start_time;
filter_nobuffer("source.txt", "nobuffer_1.txt", f1);
cout<<"nobuffer 0-1: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_1.txt", "nobuffer_2.txt", f2);
cout<<"nobuffer 1-2: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_2.txt", "nobuffer_3.txt", f3);
cout<<"nobuffer 2-3: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_3.txt", "nobuffer_4.txt", f4);
cout<<"nobuffer 3-4: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_4.txt", "nobuffer_5.txt", f5);
cout<<"nobuffer 4-5: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_5.txt", "nobuffer_6.txt", f1);
cout<<"nobuffer 5-6: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_6.txt", "nobuffer_7.txt", f2);
cout<<"nobuffer 6-7: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_7.txt", "nobuffer_8.txt", f3);
cout<<"nobuffer 7-8: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_8.txt", "nobuffer_9.txt", f4);
cout<<"nobuffer 8-9: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_nobuffer("nobuffer_9.txt", "nobuffer_10.txt", f5);
DWORD nobuffer_end_time = GetTickCount();
cout<<"nobuffer 9-10: "<<nobuffer_end_time - tick<<" ms."<<endl;
cout<<"使用檔案快取模式正在運行..."<<endl;
DWORD sequen_start_time = GetTickCount();
tick = sequen_start_time;
filter_sequen("source.txt", "sequen_1.txt", f1);
cout<<"sequen 0-1: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_1.txt", "sequen_2.txt", f2);
cout<<"sequen 1-2: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_2.txt", "sequen_3.txt", f3);
cout<<"sequen 2-3: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_3.txt", "sequen_4.txt", f4);
cout<<"sequen 3-4: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_4.txt", "sequen_5.txt", f5);
cout<<"sequen 4-5: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_5.txt", "sequen_6.txt", f1);
cout<<"sequen 5-6: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_6.txt", "sequen_7.txt", f2);
cout<<"sequen 6-7: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_7.txt", "sequen_8.txt", f3);
cout<<"sequen 7-8: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_8.txt", "sequen_9.txt", f4);
cout<<"sequen 8-9: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_sequen("sequen_9.txt", "sequen_10.txt", f5);
DWORD sequen_end_time = GetTickCount();
cout<<"sequen 9-10: "<<sequen_end_time - tick<<" ms."<<endl;
cout<<"非同步傳輸模式正在運行..."<<endl;
DWORD overlp_start_time = GetTickCount();
tick = overlp_start_time;
filter_overlp("source.txt", "overlp_1.txt", f1);
cout<<"overlp 0-1: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_1.txt", "overlp_2.txt", f2);
cout<<"overlp 1-2: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_2.txt", "overlp_3.txt", f3);
cout<<"overlp 2-3: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_3.txt", "overlp_4.txt", f4);
cout<<"overlp 3-4: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_4.txt", "overlp_5.txt", f5);
cout<<"overlp 4-5: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_5.txt", "overlp_6.txt", f1);
cout<<"overlp 5-6: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_6.txt", "overlp_7.txt", f2);
cout<<"overlp 6-7: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_7.txt", "overlp_8.txt", f3);
cout<<"overlp 7-8: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_8.txt", "overlp_9.txt", f4);
cout<<"overlp 8-9: "<<GetTickCount() - tick<<" ms."<<endl;
tick = GetTickCount();
filter_overlp("overlp_9.txt", "overlp_10.txt", f5);
DWORD overlp_end_time = GetTickCount();
cout<<"overlp 9-10: "<<overlp_end_time - tick<<" ms."<<endl;
// 輸出3種模式下的平均時間做對比
cout<<"3種模式的平均用時如下:"<<endl;
cout<<"1.無檔案快取模式平均用時:"
<<(nobuffer_end_time - nobuffer_start_time)/10<<" ms."<<endl;
cout<<"2.使用高速檔案快取模式平均用時:"
<<(sequen_end_time - sequen_start_time)/10<<" ms."<<endl;
cout<<"3.非同步傳輸模式平均用時:"
<<(overlp_end_time - overlp_start_time)/10<<" ms."<<endl;
return;
}
// --------------------------------------------------------------
// 對檔案內容進行的5種操作
// f1 +1
// f2 -1
// f3 *1
// f4 >>
// f5 <<
void f1(char *addr){ *addr = (unsigned char)*addr + 1;}
void f2(char *addr){ *addr = (unsigned char)*addr - 1;}
void f3(char *addr){ *addr = (unsigned char)*addr * 1;}
void f4(char *addr){ *addr = (unsigned char)*addr >> 1;}
void f5(char *addr){ *addr = (unsigned char)*addr << 1;}
// --------------------------------------------------------------
// 沒有檔案快取的filter函數
void filter_nobuffer(char *source, char *sink, void (*func) (char *addr))
{
HANDLE handle_src, handle_dst; // handles of source file and destination file
BOOL cycle;
DWORD NumberOfBytesRead, NumberOfBytesWrite, index;
// open the source file
handle_src = CreateFile(source, GENERIC_READ, NULL, NULL,
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
handle_dst = CreateFile(sink, GENERIC_WRITE, NULL, NULL,
CREATE_ALWAYS, NULL, NULL);
if(handle_src == INVALID_HANDLE_VALUE || handle_dst == INVALID_HANDLE_VALUE)
{
cout<<"CreateFile Invocation Error!"<<endl;
exit(1);
}
cycle = TRUE;
// use cycle to know when we finished reading the file
while(cycle)
{
// read data and send them into buffer from the source file
if(ReadFile(handle_src, buffer, BUFFER_SIZE, &NumberOfBytesRead, NULL) == FALSE)
{
cout<<"ReadFile Error!"<<endl;
exit(1);
}
if(NumberOfBytesRead < BUFFER_SIZE)
cycle = FALSE;
// do the operation to the file
for(index = 0; index < NumberOfBytesRead; index ++)
func(&buffer[index]);
// write the content of the buffer to the destination file
if(WriteFile(handle_dst, buffer, NumberOfBytesRead, &NumberOfBytesWrite, NULL) == FALSE)
{
cout<<"WriteFile Error!"<<endl;
exit(1);
}
}
// close the file handle
CloseHandle(handle_src);
CloseHandle(handle_dst);
}
void filter_sequen(char *source, char *sink, void (*func) (char *addr))
{
HANDLE handle_src, handle_dst; // handles of source file and destination file
BOOL cycle;
DWORD NumberOfBytesRead, NumberOfBytesWrite, index;
// open the source file
handle_src = CreateFile(source, GENERIC_READ, NULL, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
handle_dst = CreateFile(sink, GENERIC_WRITE, NULL, NULL,
CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if(handle_src == INVALID_HANDLE_VALUE || handle_dst == INVALID_HANDLE_VALUE)
{
cout<<"CreateFile Invocation Error!"<<endl;
exit(1);
}
cycle = TRUE;
// use cycle to know when we finished reading the file
while(cycle)
{
// read data and send them into buffer from the source file
if(ReadFile(handle_src, buffer, BUFFER_SIZE, &NumberOfBytesRead, NULL) == FALSE)
{
cout<<"ReadFile Error!"<<endl;
exit(1);
}
if(NumberOfBytesRead < BUFFER_SIZE)
cycle = FALSE;
// do the operation to the file
for(index = 0; index < NumberOfBytesRead; index ++)
func(&buffer[index]);
// write the content of the buffer to the destination file
if(WriteFile(handle_dst, buffer, NumberOfBytesRead, &NumberOfBytesWrite, NULL) == FALSE)
{
cout<<"WriteFile Error!"<<endl;
exit(1);
}
}
// close the file handle
CloseHandle(handle_src);
CloseHandle(handle_dst);
}
void filter_overlp(char *source, char *sink, void (*func) (char *addr))
{
HANDLE handle_src, handle_dst; // handles of source file and destination file
BOOL cycle;
DWORD NumberOfBytesRead, NumberOfBytesWrite, index, dwError;
OVERLAPPED overlapped; // overlapped 結構
// open the source file
handle_src = CreateFile(source, GENERIC_READ, NULL, NULL,
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
handle_dst = CreateFile(sink, GENERIC_WRITE, NULL, NULL,
CREATE_ALWAYS, NULL, NULL);
if(handle_src == INVALID_HANDLE_VALUE || handle_dst == INVALID_HANDLE_VALUE)
{
cout<<"CreateFile Invocation Error!"<<endl;
exit(1);
}
// initialize the overlapped struct
overlapped.hEvent = NULL;
overlapped.Offset = -BUFFER_SIZE;
overlapped.OffsetHigh = 0;
cycle = TRUE;
// use cycle to know when we finished reading the file
while(cycle)
{
// calculate the offset of the file
overlapped.Offset = overlapped.Offset + BUFFER_SIZE;
// read data and send them into buffer from the source file
if(ReadFile(handle_src, buffer, BUFFER_SIZE, &NumberOfBytesRead, &overlapped) == FALSE)
{
switch(dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
cycle = FALSE;
break;
case ERROR_IO_PENDING:
if(GetOverlappedResult(handle_src, &overlapped, &NumberOfBytesRead, TRUE) == FALSE)
{
cout<<"GetOverlappedResult Error!"<<endl;
exit(1);
}
break;
default:
break;
}
}
if(NumberOfBytesRead < BUFFER_SIZE)
cycle = FALSE;
// do the operation to the file
for(index = 0; index < NumberOfBytesRead; index ++)
func(&buffer[index]);
// write the content of the buffer to the destination file
if(WriteFile(handle_dst, buffer, NumberOfBytesRead, &NumberOfBytesWrite, NULL) == FALSE)
{
cout<<"WriteFile Error!"<<endl;
exit(1);
}
}
// close the file handle
CloseHandle(handle_src);
CloseHandle(handle_dst);
}
實際測試當中,採用一個37KB的文字檔,輸出結果如下:
當然,這些資料還需要更多的測試才準確,但是整體看來還是使用快取時速度最快啊。不過我們還是應該根據情況採用不同的檔案操作模式。