2016-10-27 1 views
-1

Windows 파일 API, 특히 중복 된 IO가있는 ReadFile을 사용할 때 이상한 동작이 발생했습니다.Windows 파일 API를 올바르게 사용하고 있습니까? (중복 된 요청이 여러 개임)

특정 조건에서 GetOverlappedResult는 제공된 버퍼로 데이터를 성공적으로 읽지 만 읽은 올바른 양 대신 lpNumberOfBytesTransferred를 0으로 설정합니다.

이것은 파일이 이전에 FILE_FLAG_NO_BUFFERING으로 열렸을 때 동일한 핸들에서 중복 된 읽기 요청이 발행 된 경우에만 발생합니다.

여기에 문제를 보여 코드의 전체 샘플 ... 내 시스템에서

#include <Windows.h> 
#include <string> 
#include <iostream> 

const int PageSize = 4096; 
const int BufferSize = PageSize * 4; 

struct OperationSlot 
{ 
    OVERLAPPED state; 
    unsigned char* buffer; 
}; 

bool enableFail; 

bool ReadTest(std::string filename, DWORD flags, int queueSize) 
{ 
    bool result = true; 

    if (enableFail) 
    { 
     HANDLE temp = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0); 
     CloseHandle(temp); 
    } 

    OperationSlot* slots = new OperationSlot[queueSize]; 
    for (int i = 0; i < queueSize; ++i) 
    { 
     slots[i].buffer = (unsigned char*)_aligned_malloc(BufferSize, PageSize); 
     ZeroMemory(slots[i].buffer, BufferSize); 
    } 

    HANDLE file = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, flags, NULL); 
    HANDLE controlFile = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 
    unsigned char* controlBuffer = new unsigned char[BufferSize]; 

    // Start async read operations... 
    for (int i = 0; i < queueSize; ++i) 
    { 
     ZeroMemory(&slots[i].state, sizeof(OVERLAPPED)); 
     slots[i].state.Offset = i * BufferSize; 
     bool ok = ReadFile(file, slots[i].buffer, BufferSize, NULL, &slots[i].state); 
     if (!ok) 
     { 
      DWORD err = GetLastError(); 
      if (err != ERROR_IO_PENDING) 
      { 
       std::cout << "ReadFile set error code " << err << std::endl; 
      } 
     } 
    } 

    int readId = 0; 
    while (true) 
    { 
     OperationSlot& active_slot = slots[readId % queueSize]; 
     DWORD bytes = 0; 
     bool ok = GetOverlappedResult(file, &active_slot.state, &bytes, true); 
     DWORD err = GetLastError(); 

     DWORD controlBytes = 0; 
     ReadFile(controlFile, controlBuffer, BufferSize, &controlBytes, NULL); 

     bool dataok = memcmp(active_slot.buffer, controlBuffer, controlBytes) == 0; 
     if (!dataok) 
      std::cout << "Data mismatch." << std::endl; 

     if (bytes != controlBytes) 
     { 
      std::cout << "Error with QueueSize (" << queueSize << ") and flags: "; 
      if (flags & FILE_FLAG_OVERLAPPED) 
      { 
       std::cout << "FILE_FLAG_OVERLAPPED"; 
      } 
      if (flags & FILE_FLAG_NO_BUFFERING) 
      { 
       std::cout << " | FILE_FLAG_NO_BUFFERING"; 
      } 
      if (flags & FILE_FLAG_SEQUENTIAL_SCAN) 
      { 
       std::cout << " | FILE_FLAG_SEQUENTIAL_SCAN"; 
      } 
      std::cout << std::endl; 

      std::cout << "Read size error, expected " << controlBytes << ", got " << bytes << std::endl; 
      std::cout << "GetOverlappedResult returned " << ok << " with error code " << err << std::endl; 

      result = false; 
     } 

     if (controlBytes < BufferSize) 
      break; 

     ZeroMemory(&active_slot.state, sizeof(OVERLAPPED)); 
     active_slot.state.Offset = (readId + queueSize) * BufferSize; 
     ReadFile(file, active_slot.buffer, BufferSize, NULL, &active_slot.state); 

     ++readId; 
    } 

    CloseHandle(file); 
    CloseHandle(controlFile); 
    delete[] controlBuffer; 
    for (int i = 0; i < queueSize; ++i) 
    { 
     _aligned_free(slots[i].buffer); 
    } 
    delete[] slots; 

    return !result; 
} 
int main() 
{ 
    enableFail = false; 
    int totalfail = 0; 

    std::cout << "Testing without fail." << std::endl; 

    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED, 1); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 1); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 1); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED, 4); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 4); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 4); 

    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED, 1); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 1); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 1); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED, 4); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 4); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 4); 

    std::cout << totalfail << " calls failed." << std::endl; 

    enableFail = true; 
    totalfail = 0; 
    std::cout << "Testing with fail enabled." << std::endl; 

    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED, 1); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 1); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 1); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED, 4); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 4); 
    totalfail += ReadTest("C:\\Test\\SmallFile.txt.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 4); 

    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED, 1); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 1); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 1); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED, 4); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, 4); 
    totalfail += ReadTest("C:\\Test\\LargeFile.txt", FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 4); 

    std::cout << totalfail << " calls failed." << std::endl; 

    system("pause"); 
    return 0; 
} 

이며,이 호출 '실패'의 4 초래한다. (두 개 이상의 '대기열 크기'가있는 파일)

중복 된 버전에서는 '보통'파일 핸들이 20 바이트를 읽는 반면 0 바이트 만 읽는 것으로보고됩니다. "이것은 테스트"라고 말합니다.) 다른 이상한 점은 실제로 데이터를 올바르게 읽는다는 것입니다. 올바른 버퍼가 올바른 데이터로 채워지고 전송 된 데이터 양만 잘못되었습니다. ...

파일을 열기 직전에 FILE_FLAG_NO_BUFFERING을 사용하여 파일을 열고 닫은 경우에만 발생합니다.

이전에 파일을 건드린다면 후속 액세스가 이와 같이 다르게 동작하게되는 이유는 무엇입니까?

내가 지원하지 않는 작업을하고 있거나 API가 정상적으로 작동하지 않습니다. (또는 아마도 ... 내가 간과 내 코드에서 실수가?)

편집 : 내가 나는 기적적으로 대부분의 조건에서 일하고 있다는 사실 제대로 API를 사용하여 한 생각으로 달래 된 것 같다 . 이를 수행하는 올바른 방법은 허용 된 답변에서 지적한대로 각 중첩 구조에 대해 고유 한 이벤트를 지정하는 것입니다. 각 중복 된 요청을 자체 이벤트로 제공 한 후에도 일관되게 작동합니다.

+4

당신은'OVERLAPPED' 구조체의 고유 한 이벤트를 제공하지 않기 때문에'GetOverlappedResult'는 파일 핸들을 기다려야합니다 - 그리고 여러개의 요청이 남아있을 때 여러분이 요청한 요청은 사실상 보장 될 것입니다 파일 핸들이 신호를 받으면 완료됩니다. –

+0

당신은 절대적으로 맞습니다. 나는 대기 이벤트를 완전히 잊었다. 이것이 처음에는 잘 작동하지 않았지만 ... 파일이 이전에 열리지 않았고 통화 직전에 닫히지 않는 한 일관되게 작동합니다. 이상한. 나는 그 의견을 답으로 표시 할 수 있었으면 좋겠다. – Meta

+0

내가 Remy Lebeau가 가지고 있지 않았기 때문에 대답으로 게시했습니다.) –

답변

4

OVERLAPPED 구조에 고유 한 이벤트를 제공하지 않으므로 GetOverlappedResult()은 파일 핸들을 기다려야합니다. 여러 개의 뛰어난 요청이 있으면 요청한 요청이 실제로 완료되었을 때 보장되지는 않습니다. 파일 핸들이 신호를받습니다.

OVERLAPPED 구조는 으로 만든 새 이벤트 핸들에 초기화 된 hEvent 멤버 여야합니다.