2009-12-10 6 views
10

256MB RAM (Windows 2000 이상)을 사용하는 데스크톱 시스템을 대상으로하는 응용 프로그램을 개발 중입니다. 내 응용 프로그램에서는 약 160 바이트/고정 된 레코드가 들어있는이 큰 파일 (> 256MB)이 있습니다. 이 응용 프로그램은 시간이 지남에 따라 파일의 약 90 % (읽기 및 쓰기 용)에 무작위로 액세스하는 다소 오랜 프로세스가 있습니다. 주어진 레코드 쓰기는 해당 레코드의 읽기로부터 1,000 레코드 액세스를 벗어나지 않습니다 (이 값을 조정할 수 있음).CreateFileMapping, MapViewOfFile, 시스템 메모리를 지키지 않는 방법

이 프로세스에는 정규 I/O (FileRead, FileWrite) 및 메모리 매핑 (CreateFileMapping, MapViewOfFile)의 두 가지 옵션이 있습니다. 후자는 메모리가 충분한 시스템에서 훨씬 더 효율적이어야하지만, 메모리가 부족한 시스템에서는 다른 응용 프로그램의 메모리 대부분을 교체합니다.이 응용 프로그램의 메모리는 아니오입니다. 프로세스가 모든 메모리를 다 사용하지 못하도록하는 방법이 있습니까 (예 : 더 이상 액세스하지 않는 메모리 페이지를 강제로 비우는 것처럼)? 이것이 가능하지 않다면, 나는 보통 I/O로 돌아 가야한다. 쓰기 부분에 겹쳐진 I/O를 사용하고 싶었지만 (액세스가 너무 무작위 적이기 때문에) 문서에 writes of less than 64K are always served synchronously이 나와 있습니다.

I/O 개선을위한 아이디어는 환영합니다.

+0

아마도 VirtualFree (MEM_DECOMMIT)가 도움이 될 수 있습니까? 나는 그것에 익숙하지 않다. –

+1

아니요, MMF의 경우 VirtualFree (MEM_DECOMMIT)가 실패합니다. 방금 확인 했어. –

답변

10

마침내 길을 찾았습니다. 스레드에서 파생되었습니다 here. 트릭은 VirtualUnlock()을 커밋하지 않아도되는 범위에서 사용하고 있습니다. 이 함수는 오류 0x9e ("세그먼트가 이미 잠금 해제 됨")와 함께 FALSE를 반환하지만 페이지가 수정 되었더라도 (파일이 올바르게 업데이트 됨) 메모리가 실제로 해제됩니다. 당신이 볼 수 있듯이

#include "stdafx.h" 

void getenter(void) 
{ 
    int  ch; 
    for(;;) 
    { 
     ch = getch(); 
     if(ch == '\n' || ch == '\r') return; 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    char* fname = "c:\\temp\\MMFTest\\TestFile.rar";  // 54 MB 
    HANDLE hfile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); 
    if(hfile == INVALID_HANDLE_VALUE) 
    { 
     fprintf(stderr, "CreateFile() error 0x%08x\n", GetLastError()); 
     getenter(); 
     return 1; 
    } 

    HANDLE map_handle = CreateFileMapping(hfile, NULL, PAGE_READWRITE | SEC_RESERVE, 0, 0, 0); 
    if(map_handle == NULL) 
    { 
     fprintf(stderr, "CreateFileMapping() error 0x%08x\n", GetLastError()); 
     getenter(); 
     CloseHandle(hfile); 
     return 1; 
    } 

    char* map_ptr = (char*) MapViewOfFile(map_handle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); 
    if(map_ptr == NULL) 
    { 
     fprintf(stderr, "MapViewOfFile() error 0x%08x\n", GetLastError()); 
     getenter(); 
     CloseHandle(map_handle); 
     CloseHandle(hfile); 
     return 1; 
    } 

    // Memory usage here is 704KB 
    printf("Mapped.\n"); getenter(); 

    for(int n = 0 ; n < 10000 ; n++) 
    { 
     map_ptr[n*4096]++; 
    } 

    // Memory usage here is ~40MB 
    printf("Used.\n"); getenter(); 

    if(!VirtualUnlock(map_ptr, 5000 * 4096)) 
    { 
     // Memory usage here is ~20MB 
     // 20MB already freed! 
     fprintf(stderr, "VirtualUnlock() error 0x%08x\n", GetLastError()); 
     getenter(); 
     UnmapViewOfFile(map_ptr); 
     CloseHandle(map_handle); 
     CloseHandle(hfile); 
     return 1; 
    } 

    // Code never reached 
    printf("VirtualUnlock() executed.\n"); getenter(); 

    UnmapViewOfFile(map_ptr); 
    CloseHandle(map_handle); 
    CloseHandle(hfile); 

    printf("Unmapped and closed.\n"); getenter(); 

    return 0; 
} 

것은 프로그램의 작업 집합 VirtualUnlock는(), 내가 필요한대로 실행 한 후 감소 :

여기 내 샘플 테스트 프로그램입니다. 나는 필요한만큼 잠금을 해제하기 위해 내가 변경 한 페이지를 추적하기 만하면됩니다.

+0

크게 공유해 주셔서 감사합니다. 메모리 맵된 파일에 사용 된 메모리 양을 제어하는 ​​방법을 필사적으로보고 있었고 이미 수행 할 수 있기를 바랬습니다. 나는 MS가 일부 미래 OS를 사용 중지하지 않기를 바랄 뿐이다. 결국 우리는 원하는대로 페이지를 커밋하고 디 코밋 할 수없는 이유는 무엇입니까? – Suma

+0

참고 : 설명하는 오류 코드는 VirtualUnlock doc remark : 에 기반하여 예상 한 것 같습니다. "지정된 범위의 페이지 중 하나라도 잠기지 않으면 VirtualUnlock은 작업 세트에서 해당 페이지를 제거하고 마지막 오류를 ERROR_NOT_LOCKED로 설정하고 FALSE를 반환합니다. " – Suma

+0

한 번 더 메모 : 실험은 오해의 소지가 있습니다. 프로세스의 작업 집합이 줄어들지 만 반드시 페이지가 삭제되는 것은 아닙니다. 다시 매핑하면 페이지 파일 작업없이 바로 가져올 수 있습니다. http://stackoverflow.com/questions/3525202/how-can-i-decommit-a-file-mapped-page/3525266#3525266에서 실험을보십시오. – Suma

1

MapViewOfFile을 사용하여 전체 파일을 하나의 블록으로 매핑합니까? 그렇다면 작은 부분을 매핑 해보십시오. FlushViewOfFile()을 사용하여 뷰를 플러시 할 수 있습니다.

+0

파일을 순차적으로 액세스하지 않습니다. 파일에 액세스 할 때 제어 할 수없는 임의의 패턴이 있으므로 작은 부분을 매핑하면 매우 비효율적입니다. 게다가 FlushViewOfFile()은 메모리를 해제하지 않습니다. 더티 페이지 작성 만 강제로 수행합니다. –

3

전체 파일을 메모리에 매핑하기 만하면됩니다. 이것은 가상 메모리는 사용하지만 실제 메모리는 사용하지 않습니다. 파일은 디스크에서 조각으로 읽히고 스왑 파일을 관리하는 정책과 동일한 정책에 의해 메모리에서 제거됩니다.

+4

글쎄, 그게 정확히 문제 야. 스왑 파일을 관리하는 정책은 파일 캐시보다 훨씬 많은 내용을 메모리에 저장하는 경향이 있으므로 매핑 된 파일에 액세스하면 결국 다른 프로세스 대부분이 스왑됩니다. –

2

VirtualUnlock이 작동하지 않는 것 같습니다. 필요한 것은 UnmapViewOfFile (map_ptr) 바로 전에 FlushViewOfFile (map_ptr, 0)을 호출하는 것입니다. Windows 작업 관리자는 실제 메모리 사용을 표시하지 않습니다. SysInternals의 ProcessExplorer 사용

관련 문제