*비동기 I/O : I/O자체를 O/S에게 일임해버림.
Read후 Write요청을 함-> O/S가 Write마침 -> Callback함수 호출.
어찌할까? 비동기를 효율적으로 하려면
-> 동적으로 버퍼 할당후 O/S에 넘긴다. 그리고 return. Callback이 오면 다시 할당하고 넘김. 실제 Write는 커널이 알아서 해주니까. 더 빠르고 편하다.
구조 -> I/O를 걸고 callback이 올때까지 wait를 걸어준다. ->이거하지말고 그냥 리턴~~.
대신 callback에서 다음것을 읽으라고 호출해주면 좋다.
Overlapped I/O가 지원되야만 File I/O를 비동기로 할 수 있다. 그리고 비동기에서 모든것을 다 O/S에 맡겨버리면 이것이 IOCP이다.
*Overlapped I/O사용을 위해서는 LPOVERLAPPED구조체를 사용하여야한다.
LPOVERLAPPED->hEvent : 이벤트를 넘기면, 이벤트가 동작하고, 포인터를 넘기면 포인터자체를 써버린다. 단 이벤트를 사용하지않을 경우에 WriteFileEx의 마지막 인자가 완료시 호출될 함수 이름이어야한다. 단, 이함수는 원형을 지켜야한다!
*Process 순서 - 대용량 지원 안되는 버전
1) 파일을 Open하고 Handle을 받음
2) 동적할당된 메모리를 확보한다. (Open된파일의 크기만큼) -> Buffer임.
동적할당을 하는 이유는 호출하는 함수(Read)에서 write하라고 통보하고 닫음. 동적할당안해주면
버퍼가 날라가버림..
3) 2)에서 생성된 메모리에 1)에서 Open한 파일의 내용을 Copy
4) 비동기 I/O를 할 파일을 열어서 Handle을 리턴받음.
5) 비동기 I/O를 위한 OVERLAPPED구조체 객체를 생성한다.
6) WriteFileEx를 호출한다.
7) Write I/O가 완료될 시 -> Callback함수를 호출.
*호출부분
void CFileCopyUtilityDlg::OnButton()
{
// TODO: Add your control notification handler code here
CString tmp = _T("");
HANDLE hFileSrc = NULL, hFileDest = NULL;
LARGE_INTEGER llFileSize;
llFileSize.QuadPart = 0;
//HANDLE hMap = NULL;
//1. 원본파일 읽기모드 로 열기
hFileSrc = ::CreateFile((LPTSTR)(const char*)m_csSrcFile,
GENERIC_READ, //read
FILE_SHARE_READ, //읽기모드 공유허용
NULL, //보안속성없음
OPEN_EXISTING, //존재하는 파일 열기
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFileSrc == INVALID_HANDLE_VALUE){
tmp.Format(_T("원본 파일을 여는데 실패했습니다.\nError Code [%d]"),::GetLastError());
AfxMessageBox(tmp);
return;
}
if(!::GetFileSizeEx(hFileSrc,&llFileSize)){
AfxMessageBox(_T("cannot know original file size."));
return;
}
DWORD dwReadSize = 0;
BYTE *byFileBuffer = (BYTE*)malloc((UINT)llFileSize.QuadPart);
BOOL bResult = ::ReadFile(hFileSrc,byFileBuffer,(UINT)llFileSize.QuadPart,
&dwReadSize, NULL);
if(!bResult){
tmp.Format(_T("Failure to read file. [Error Code:%d]"),::GetLastError());
AfxMessageBox(tmp);
return;
}
//2. 대상파일 쓰기모드로 열기
hFileDest = ::CreateFile((LPTSTR)(const char*)m_csDestFile,
GENERIC_WRITE, //쓰기모드
0, //공유안함
NULL, //보안속성없음
CREATE_NEW, //새로생성
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if(hFileDest == INVALID_HANDLE_VALUE){
tmp.Format(_T("복사본 파일을 생성하는데 실패했습니다.\nError Code [%d]"),::GetLastError());
AfxMessageBox(tmp);
//::CloseHandle(hFileSrc);
return ;
}
/*
파일복사과정....
src의 한 block을 dest에 write. 그리고 write하는순간 Progress를 SetPos한다.
*/
LPOVERLAPPED pOverLapped = (LPOVERLAPPED)malloc(sizeof(OVERLAPPED));
COPY_DATA* pCopyData = (COPY_DATA*)malloc(sizeof(COPY_DATA));
pCopyData->pMapView = byFileBuffer;
pCopyData->hFileSrc = hFileSrc;
pCopyData->hFileDest = hFileDest;
::ZeroMemory(pOverLapped, sizeof(OVERLAPPED));
pOverLapped->OffsetHigh = 0;
pOverLapped->Offset = 0;
pOverLapped->hEvent = pCopyData;
if(!::WriteFileEx( hFileDest,
byFileBuffer,
(DWORD)llFileSize.QuadPart,
pOverLapped,
WriteFileIOCompleteRoutine)){
tmp.Format(_T("WriteFileEx [Error Code:%d]"),::GetLastError());
AfxMessageBox(tmp);
return;
}
::SleepEx(1,TRUE);
AfxMessageBox(_T("OnButtonAsync() returned."));
}
*함수구현부분
VOID CALLBACK WriteFileIOCompleteRoutine(
DWORD dwErrorCode,
DWORD dwNumberObBytesTramsferred,
LPOVERLAPPED lpOverLapped){
AfxMessageBox("비동기 I/O를 이용한 파일쓰기가 완료되었습니다.");
if(lpOverLapped->hEvent != NULL){
COPY_DATA* pCopyData = (COPY_DATA*)lpOverLapped->hEvent;
if(pCopyData->pMapView != NULL){
free(pCopyData->pMapView);
pCopyData->pMapView = NULL;
}
::CloseHandle(pCopyData->hFileSrc);
::CloseHandle(pCopyData->hFileDest);
if(pCopyData != NULL){
free(pCopyData);
pCopyData = NULL;
}
}
if( lpOverLapped != NULL){
free(lpOverLapped);
lpOverLapped = NULL;
}
}
댓글