天天看點

基于visual c++之windows核心程式設計代碼分析(20) 纖程與線程的互相轉換

在Windows2000/XP中,纖程(fiber)相當于使用者級别的線程或輕程序.纖程由Win32庫函數支援,對核心是不可見的.纖程可以通過SwitchToFiber顯示至另一合作纖程,以實作合作纖程之間的協同.

纖程包含獨立的目态棧,寄存器狀态的控制資訊.目态控制的纖程轉接要求較高的程式設計經驗.由于纖程屬于目态對象,一個纖程被封鎖意味着所線上程被封鎖.應用程式可以通過ConvertThreadToFiber将線程轉換為纖程.與線程對比,纖程具有切換速度快的特點.

Microsoft公司給Windows添加了一種纖程,以便能夠非常容易地将現有的UNIX伺服器應用程式移植到Windows中。UNIX伺服器應用程式屬于單線程應用程式(由Windows定義),但是它能夠為多個客戶程式提供服務。換句話說, UNIX應用程式的開發人員已經建立了他們自己的線程結構庫,他們能夠使用這種線程結構庫來仿真純線程。該線程包能夠建立多個堆棧,儲存某些C P U寄存器,并且在它們之間進行切換,以便為客戶機請求提供服務。

  顯然,若要取得最佳的性能,這些UNIX應用程式必須重新設計,仿真的線程庫應該用Windows提供的純線程來替代。然而,這種重新設計需要花費數月甚至更長的時間才能完成,是以許多公司首先将它們現有的UNIX代碼移植到Windows中,這樣就能夠将某些應用軟體推向Windows市場。

  使用纖程

  線程是在Windows核心中實作的,纖程是在使用者模式下實作的,核心對纖程一無所知,核心會根據我們定義的算法來對纖程進行排程。

  一個線程可以包含一個或多個纖程。

  轉化線程為纖程

  使用纖程的第一個步驟是将已有的線程轉換為一個纖程。ConvertThreadToFiber這個函數會為纖程的上下文配置設定記憶體,這個上下文的構成是:

  # 一個使用者自定義的值

  # 結構化異常處理鍊的頭

  # 纖程棧的頂部和底部的記憶體位址

  # 某些CPU寄存器,其中包括棧指針、指令指針以及其他寄存器

  當我們配置設定了纖程執行上下文并對其進行初始化之後,還必須将執行上下文的位址與線程關聯起來。這樣我們就将線程轉換成了一個纖程,該纖程在這個線程中執行。

  其實,除非我們打算建立更多的纖程,并讓它們在同一個線程中運作,否則沒有理由将一個線程轉換為纖程。

  CreateFiber:建立一個纖程

  SwitchToFiber:調用一個纖程(同一個線程中,同一時刻隻能執行一個纖程)

  DeleteFiber:通常為一個纖程調用,來删除另一個纖程

  GetCurrentFiber:得到目前正在運作的纖程

**************************************/
/* 預定義 */
#define _WIN32_WINNT 0x0501
/* 頭檔案 */
#include <windows.h>
#include <stdio.h>
/* 函數聲明 */
VOID WINAPI ReadFiberFunc( LPVOID lpParameter );
VOID WINAPI WriteFiberFunc( LPVOID lpParameter );
/* 結構定義 */
// 用于向纖程傳遞參數
// 本執行個體是使用讀、寫檔案來示範纖程的排程
// 使用者可根據實際情況自行定義
typedef struct _FIBERDATASTRUCT
{
	DWORD dwParameter;		// 預留給向纖程傳遞待定參數
	DWORD dwFiberResultCode;	// GetLastError() 值
	HANDLE hFile;				// 纖程所操作檔案的句柄
	DWORD dwBytesProcessed;		// 已經處理了的位元組
}FIBERDATASTRUCT, *LPFIBERDATASTRUCT;
/* 常量定義 */
#define RTN_OK 0			// 傳回值 成功
#define RTN_USAGE 1			// 傳回值 參數不正确
#define RTN_ERROR 2		// 傳回值 錯誤
#define BUFFER_SIZE 32768	// 緩沖區大小
#define FIBER_COUNT 3		// 主纖程、讀纖程、寫纖程,共三個
#define PRIMARY_FIBER 0		// 主纖程
#define READ_FIBER 1		// 讀纖程
#define WRITE_FIBER 2		// 寫纖程

LPVOID g_lpFiber[FIBER_COUNT];	// 纖程位址的數組
LPBYTE g_lpBuffer;				// 緩沖區
DWORD g_dwBytesRead;			// 已讀的位元組

int main( int argc, char *argv[] )
{
	LPFIBERDATASTRUCT fs;
	// 用法說明
	if (argc != 3)
	{
		printf("Usage: %s <SourceFile> <DestinationFile>\n", argv[0]);
		return RTN_USAGE;
	}
	// 配置設定FIBERDATASTRUCT空間,FIBER_COUNT個
	fs = (LPFIBERDATASTRUCT)HeapAlloc(
		GetProcessHeap(), 0,
		sizeof(FIBERDATASTRUCT) * FIBER_COUNT);
	if (fs == NULL)
	{
		printf("HeapAlloc error! (rc%=lu)\n", GetLastError());
		return RTN_ERROR;
	}
	//	配置設定讀、寫緩沖區
	g_lpBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
	if (g_lpBuffer == NULL)
	{
		printf("HeapAlloc error! (rc=%lu)\n", GetLastError());
		return RTN_ERROR;
	}
	// 打開源檔案,将句柄指派給fs結構的hFile成員,使纖程可以使用該句柄
	fs[READ_FIBER].hFile = CreateFile(
		argv[1], GENERIC_READ,
		FILE_SHARE_READ, NULL, OPEN_EXISTING,
		FILE_FLAG_SEQUENTIAL_SCAN, NULL
		);
	if (fs[READ_FIBER].hFile == INVALID_HANDLE_VALUE)
	{
		printf("CreateFile error! (rc=%lu)\n", GetLastError());
		return RTN_ERROR;
	}
	// 打開目标檔案
	fs[WRITE_FIBER].hFile = CreateFile(
		argv[2], GENERIC_WRITE,
		0, NULL, CREATE_NEW,
		FILE_FLAG_SEQUENTIAL_SCAN, NULL
		);
	if (fs[WRITE_FIBER].hFile == INVALID_HANDLE_VALUE)
	{
		printf("CreateFile error! (rc=%lu)\n", GetLastError());
		return RTN_ERROR;
	}

	// 将主線程切換為纖程,為主纖程,隻有轉換為纖程後才可以切換至其他纖程
	g_lpFiber[PRIMARY_FIBER]=ConvertThreadToFiber(&fs[PRIMARY_FIBER]);
	if (g_lpFiber[PRIMARY_FIBER] == NULL)
	{
		printf("ConvertThreadToFiber failed! rc=%lu\n", GetLastError());
		return RTN_ERROR;
	}
	// 主纖程資料
	fs[PRIMARY_FIBER].dwParameter = 0;
	fs[PRIMARY_FIBER].dwFiberResultCode = 0;
	fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE;
	// 建立讀纖程
	g_lpFiber[READ_FIBER]=CreateFiber(0,ReadFiberFunc,&fs[READ_FIBER]);
	if (g_lpFiber[READ_FIBER] == NULL)
	{
		printf("CreateFiber error! (rc=%lu)\n", GetLastError());
		return RTN_ERROR;
	}
	// 将纖程指針作為參數傳給纖程,沒有實際意義,為了顯示相關資訊時差別各纖程
	fs[READ_FIBER].dwParameter = (DWORD)g_lpFiber[READ_FIBER];
	// 建立寫纖程
	g_lpFiber[WRITE_FIBER]=CreateFiber(0,WriteFiberFunc,&fs[WRITE_FIBER]);

	if (g_lpFiber[WRITE_FIBER] == NULL)
	{
		printf("CreateFiber error! (rc=%lu)\n", GetLastError());
		return RTN_ERROR;
	}
	fs[WRITE_FIBER].dwParameter = (DWORD)g_lpFiber[WRITE_FIBER];
	// 切換到讀程式執行
	SwitchToFiber(g_lpFiber[READ_FIBER]);

	// 由讀纖程獲寫纖程切換回主纖程
	// 顯示相關資訊
	printf("ReadFiber result == %lu Bytes Processed == %lu\n",
		fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed);

	printf("WriteFiber result == %lu Bytes Processed == %lu\n",
		fs[WRITE_FIBER].dwFiberResultCode, fs[WRITE_FIBER].dwBytesProcessed);
	// 删除讀寫纖程
	DeleteFiber(g_lpFiber[READ_FIBER]);
	DeleteFiber(g_lpFiber[WRITE_FIBER]);
	// 關閉檔案句柄、釋放記憶體、傳回
	CloseHandle(fs[READ_FIBER].hFile);
	CloseHandle(fs[WRITE_FIBER].hFile);
	HeapFree(GetProcessHeap(), 0, g_lpBuffer);
	HeapFree(GetProcessHeap(), 0, fs);
	return RTN_OK;
}

VOID WINAPI ReadFiberFunc( LPVOID lpParameter )
{
	LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;

	// 判斷參數
	if (fds == NULL)
	{
		printf("Passed NULL fiber data. Exiting current thread.\n");
		return;
	}
	// 顯示纖程資訊
	printf("Read Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
	// 初始化處理的位元組數為0
	fds->dwBytesProcessed = 0;
	// 循環讀
	while (1)
	{
		if (!ReadFile(fds->hFile, g_lpBuffer, BUFFER_SIZE,
			&g_dwBytesRead, NULL))
		{
			break;
		}
		// 判斷檔案是否已經讀完
		if (g_dwBytesRead == 0) break;

		// 已經處理的位元組,累加
		fds->dwBytesProcessed += g_dwBytesRead;

		// 讀一次後切換到寫纖程,将讀出的資料寫入到目标檔案
		printf("Switch To Write");
		SwitchToFiber(g_lpFiber[WRITE_FIBER]);
	}
	// 讀操作完成,準備交出執行,傳回到主纖程中
	fds->dwFiberResultCode = GetLastError();
	SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}

VOID WINAPI WriteFiberFunc( LPVOID lpParameter )
{
	LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
	DWORD dwBytesWritten;

	// 判斷參數
	if (fds == NULL)
	{
		printf("Passed NULL fiber data. Exiting current thread.\n");
		return;
	}
	// 顯示纖程資訊
	printf("Write Fiber (dwParameter == 0x%lx)\n", fds->dwParameter);
	// 初始化,注意和讀纖程的不同
	fds->dwBytesProcessed = 0;
	fds->dwFiberResultCode = ERROR_SUCCESS;

	while (1)
	{
		// 寫入資料
		if (!WriteFile(fds->hFile, g_lpBuffer, g_dwBytesRead,
			&dwBytesWritten, NULL))
		{
			// 如果發生錯誤,退出循環
			break;
		}
		fds->dwBytesProcessed += dwBytesWritten;

		// 寫入完成,切換到讀纖程
		printf("Switch To Write");
		SwitchToFiber(g_lpFiber[READ_FIBER]);
	}
	// 出錯,切換到主纖程
	// 如果寫操作不出錯,是不可能由寫纖程切換回主纖程的
	fds->dwFiberResultCode = GetLastError();
	SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}