1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[WindowsAPI]古典的非同期I/Oのサンプルコード

Posted at

CPUバウンド、かつI/Oバウンドな処理(ファイルからのハッシュ計算など)の場合、非同期I/Oを使用することで処理時間が短縮する場合がある。

一般的に、ワーカースレッドとキューを用いた実装をする場合が多いかと思うが、ここでは1スレッド、そして古典的なWindows APIである ReadFile(の滅多に使用しないlpOverlappedパラメータ) を用いて非同期I/Oを実装したサンプルコードを掲載する。

処理イメージ

大体こんなフローです。
処理フローチャート

注意

  • このサンプルコードが、本当に早い保証はありません。計測してください。
  • 闇雲に非同期I/Oを使用すべきではない。
    同期I/Oでも、OS等のキャッシュにより非同期I/Oとほぼ変わらない処理時間の場合が多い。
    低速なCPUと低速なI/Oが想定される場合のみ非同期I/Oを検討するべきである。
  • ハッシュ計算は古いAPIであるCryptoAPIを使用している。MicrosoftはCNG APIの使用を推奨している。

サンプルコード

CPUバウンドの処理の例として、SHA-512を行っている。
非同期I/OはWindows 2000 などでも可能だが、SHA-512はWindows XP SP3以降でないと対応していない。

main.c

# ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0502
# endif

# include <windows.h>
# include <tchar.h>
# include <wincrypt.h>
# include <bcrypt.h>

# pragma comment(lib, "winmm.lib")
# pragma comment(lib, "crypt32.lib")

# define BUFSIZE (4 * 1024 * 1024)
# define SHA512LEN (512/8)

struct BUF
{
	LPBYTE lpBuffer;
	OVERLAPPED ol;
	DWORD dwBufferSize;
	DWORD dwReaded;
	BOOL fPending;
};

static void initBuf(struct BUF * pBuf)
{
	memset(pBuf, 0x00, sizeof(struct BUF));
	pBuf->lpBuffer = malloc(BUFSIZE);
	pBuf->dwBufferSize = BUFSIZE;
	//pBuf->ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}

DWORD CalcHashAsync(LPCTSTR lpFileName, HCRYPTHASH hHash)
{
	struct BUF buffers[2] = {0};
	HANDLE hFile;
	BOOL fContinue = TRUE;
	BOOL index = 0;
	ULARGE_INTEGER offset = {0};
	DWORD dwError = ERROR_SUCCESS;

	hFile = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
	if(INVALID_HANDLE_VALUE == hFile)
	{
		dwError = GetLastError();
		DebugBreak();
		return dwError;
	}

	initBuf(&buffers[0]);
	initBuf(&buffers[1]);

	while(fContinue)
	{
		// -------------------------------

		if(buffers[index].fPending)
		{
			DWORD dwPendingTick = timeGetTime();

			//_tprintf(TEXT("[%d]Wait Pending I/O...\n"), index);
			if(!GetOverlappedResult(hFile, &buffers[index].ol, &buffers[index].dwReaded, TRUE))
			{
				DWORD overlappedResult = GetLastError();
				fContinue = FALSE;

				if(overlappedResult != ERROR_HANDLE_EOF) 
				{
					dwError = overlappedResult;
					DebugBreak();
					break;
				}
			}
			_tprintf(TEXT("[%d] %u\n"), index, timeGetTime() - dwPendingTick);
		}

		// -------------------------------
		if(fContinue)
		{
			BOOL nextIndex = !index;

			buffers[nextIndex].dwReaded = 0;
			buffers[nextIndex].ol.Offset = offset.LowPart;
			buffers[nextIndex].ol.OffsetHigh = offset.HighPart;

			memset(buffers[nextIndex].lpBuffer, 0xCC, buffers[nextIndex].dwBufferSize);
			buffers[nextIndex].fPending = FALSE;
			_tprintf(TEXT("[%d]Start ReadFile(offset:0x%X%08X)...\n"), nextIndex, offset.HighPart, offset.LowPart );
			if(!ReadFile(hFile, buffers[nextIndex].lpBuffer, buffers[nextIndex].dwBufferSize, &buffers[nextIndex].dwReaded, &buffers[nextIndex].ol))
			{
				DWORD dwReadFileResult = GetLastError();
				switch(dwReadFileResult)
				{
				case ERROR_IO_PENDING:
					buffers[nextIndex].fPending = TRUE;
					break;
				case ERROR_HANDLE_EOF:
					fContinue = FALSE;
					break;
				default:
					fContinue = FALSE;
					dwError = dwReadFileResult;
					break;
				}
			}
			else
			{
				_tprintf(TEXT("[%d]ReadFile completed synchronously.\n"), nextIndex);
			}
			offset.QuadPart += buffers[nextIndex].dwBufferSize;
		}

		// -------------------------------
		if(buffers[index].dwReaded)
		{
			_tprintf(TEXT("[%d]CryptHashData(0x%08X)...\n"), index, buffers[index].dwReaded);
			if(!CryptHashData(hHash,  buffers[index].lpBuffer, buffers[index].dwReaded, 0))
			{
				dwError = GetLastError();
				DebugBreak();
				break;
			}
		}

		index = !index; // 0, 1, 0, 1 ...
	}

	CloseHandle(hFile);
	free(buffers[0].lpBuffer);
	free(buffers[1].lpBuffer);
	return dwError;
}
int _tmain(int argc, _TCHAR* argv[])
{
	HCRYPTPROV hProv = 0;
	HCRYPTHASH hHash = 0;
	BYTE hashVal[SHA512LEN] = {0};
	DWORD dwHashSize = sizeof(hashVal);
	TCHAR hashStr[SHA512LEN * 4 + 1] = TEXT("");
	DWORD cchStr = _countof(hashStr);

	if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
	{
		DebugBreak();
		return 1;
	}

	if(!CryptCreateHash(hProv, CALG_SHA_512, 0, 0, &hHash))
	{
		DebugBreak();
		CryptReleaseContext(hProv, 0);
		return 1;
	}

	CalcHashAsync(argv[1], hHash);


	if(CryptGetHashParam(hHash, HP_HASHVAL, hashVal, &dwHashSize, 0))
	{
		CryptBinaryToString(hashVal, sizeof(hashVal),CRYPT_STRING_HEX | CRYPT_STRING_NOCR,hashStr, &cchStr);
		_tprintf(TEXT("%s\n"), hashStr);
	}
	else
	{
		_ftprintf(stderr, TEXT("CryptGetHashParam failed.(0x%08X)\n"), GetLastError());
	}

	CryptDestroyHash(hHash);
	CryptReleaseContext(hProv, 0);
	return 0;
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?