2009-11-20 3 views
3

Windows XP의 32 비트 주소 공간에서 실행되는 복잡하고 메모리가 많은 멀티 스레드 응용 프로그램을 생각해보십시오.실패 가능성없이 MapViewOfFileEx를 여러 번 호출 할 때 공간을 재활용 할 수 있습니까?

특정 작업에는 한 번에 하나의 버퍼 만 액세스해야하는 고정 크기의 대형 버퍼가 필요합니다.

응용 프로그램은 하나의 버퍼 크기가 초기에 예약되어 있고 현재 필요한 버퍼를 포함하는 데 사용되는 패턴을 사용합니다.

시퀀스를 따른다 : (초기 실행)에서 VirtualAlloc -> VirtualFree를 -> MapViewOfFileEx (버퍼 변경) UnMapViewOfFile -> MapViewOfFileEx 여기

버퍼 위치로 포인터에서 VirtualAlloc에 ​​대한 호출에 의해 제공되고, 다음 MapViewOfFileEx를 호출 할 때마다 동일한 위치가 사용됩니다.

문제는 Windows가 다른 사용자간에 메모리 공간을 전달하는 데 필요한 핸드 셰이크 유형 작업을 제공하지 않는다는 것입니다.

따라서 메모리가 잠기지 않고 다른 스레드가 건너 뛰고 버퍼 내에서 할당을 수행 할 수있는 작은 기회가 있습니다.

MapViewOfFileEx에 대한 다음 호출이 끊어지고 시스템에서 더 이상 버퍼의 주소 공간에 충분한 공간이 있음을 보장 할 수 없습니다.

더 작은 버퍼를 사용하도록 리팩토링하면 분명히 공간을 재 할당하는 속도가 줄어 듭니다.

HeapLock의 일부 사용에는 성공했지만 여전히 문제가 있습니다. 주소 공간에서 일부 메모리를 훔치기 위해 여전히 문제가 있습니다. (GetProcessHeaps를 호출 한 다음 HeapLock을 사용하여 모든 힙을 잠그려고했습니다.)

MapViewOfFileEx와 호환되는 주소 공간의 특정 블록을 잠그고 싶습니다.

편집 : 나는 결국 그를 추가해야이 코드는

+0

그리고 왜 MapViewOfFileEx 전에 VirtualFree를 호출합니까? – Sebastian

+0

전체 메모리 블록에 뮤텍스를 사용하지 않거나 처음 할당 된 메모리 내의 미리 정의 된 주소 공간에 뮤텍스 목록을 사용하는 이유가 있습니까? –

+0

아, 내가 이해할 것 같아. 동일한 힙 블록이 항상 무료임을 보장함으로써 MapViewOfFileEx를 그 뒤에서 최적화하려고합니다. – Sebastian

답변

0

당신이 HeapCreate를 통해 자신의 개인 힙을 만들어 봤어 내 컨트롤 외부의 응용 프로그램에 의해 호출되는 라이브러리에 살고? 힙을 원하는 버퍼 크기로 설정할 수 있습니다. 유일한 문제는 MapViewOfFile에서 기본 힙 대신 개인 힙을 사용하는 방법입니다.

MapViewOfFile은 내부적으로을 호출하여 기본 힙을 얻은 다음 연속 된 메모리 블록을 요청한다고 가정합니다. MapViewOfFile에 대한 호출을 우회로로 묶을 수 있습니다. 즉,호출을 다시 호출하여 메모리에있는 메소드를 덮어 쓰면 실제 비약을 반환 할 수있는 코드로 점프를 효과적으로 삽입 할 수 있습니다.

Microsoft는 Detour Library을 게시했는데 나는 직접적으로 익숙하지 않습니다. 우회는 놀랍게도 일반적이라는 것을 알고 있습니다. 보안 소프트웨어, 바이러스 스캐너 등 모든 프레임 워크를 사용합니다.그것은 꽤 아니지만, 작동 할 수 있습니다 :

HANDLE g_hndPrivateHeap; 

HANDLE WINAPI GetProcessHeapImpl() { 
    return g_hndPrivateHeap; 
}  


struct SDetourGetProcessHeap { // object for exception safety 
    SDetourGetProcessHeap() { 
     // put detour in place 
    } 

    ~SDetourGetProcessHeap() { 
     // remove detour again 
    } 
}; 


void MapFile() { 
    g_hndPrivateHeap = HeapCreate(...); 

    { 
     SDetourGetProcessHeap d; 
     MapViewOfFile(...); 
    } 
} 

는이 또한 도움이 될 수 있습니다 : 나는 코드의 조각으로 당신에게 온 경우

How to replace WinAPI functions calls in the MS VC++ project with my own implementation (name and parameters set are the same)?

How can I hook Windows functions in C/C++?

http://research.microsoft.com/pubs/68568/huntusenixnt99.pdf

+0

내가 원하는 것은 VirtualAlloc과 같은 방식으로 주소 공간을 예약 (하지만 커밋 안함)하는 것입니다.하지만 안전하게 넘겨 줄 수는 있습니다. to MapViewOfFileEx – morechilli

+0

보통 인 메모리 버퍼를 사용하면 성능상의 이점이 필요하지 않으므로 메모리를 사용할 수 있음을 보장하기 만하면됩니다. – Sebastian

+0

예 - 그게 아주 힘들어요 ... – morechilli

0

상상 이렇게 :

void *foo; 

foo = malloc(n); 
if (foo) 
    free(foo); 
foo = malloc(n); 

내가 너에게 와서 말하길, 도와 줘! foo 두 번째 할당에 동일한 주소가 없습니다!

나는 미쳤 겠지?

이것이 작동하지 않는 이유에 대해 이미 분명히 알고있는 것처럼 보입니다. 명시적인 주소가 매핑되는 모든 API에 대한 문서는 주소가 단지 제안 일 뿐이며이를 보장 할 수 없다는 것을 알 수 있습니다. 이것은 POSIX의 mmap()에도 해당됩니다.

주소 변경이 중요하지 않은 방식으로 프로그램을 작성하시기 바랍니다. 즉, 버퍼 안의 수량에 너무 많은 포인터를 저장하지 마십시오. 그렇지 않으면 재 할당 후 패치합니다. realloc()에 전달할 버퍼를 처리하는 것과 비슷한 방식입니다.

이 (운영 체제에서 사용되지 않음) 지금 안전 주소를 지정할 수 있지만

이 주소를 통해 안전하게 유지됩니다 보장은 없습니다 :

심지어 MapViewOfFileEx()에 대한 문서는 명시 적으로 제시 시각. 따라서 운영 체제가 주소를 선택하도록하는 것이 좋습니다. 이 경우 메모리 매핑 된 파일에 포인터를 저장하지 않으므로 매핑을 모든 주소에서 사용할 수 있도록 파일 매핑의 기본 부분에서 오프셋을 저장합니다. 이 경우 귀하의 의견

에서

업데이트, 나는 당신이 할 수 있다고 가정 :

  • 연속 블록에 매핑 할 수 없습니다. 아마도 청크로 매핑하고 어느 중간 읽기/쓰기를 결정할 함수를 작성할 수 있습니까?

  • 64 비트로 포팅을 시도하십시오.

+0

불행히도 포인터는 문제가 아닙니다. 문제는 주소 공간에 충분히 큰 구멍을 보장하는 것입니다. 나중에 열심히 시작하기 쉽습니다. – morechilli

+0

나는 새로운 것을 디자인하기보다는 성숙한 시스템을 지원한다고 덧붙였다. – morechilli

+0

불행히도 32 비트 지원은 아직 필요하지 않습니다. 리팩토링을 할 수있는 가능성에 동의합니다. - 제 질문에 대한 대안을 찾을 수 있기를 바랍니다. 감사합니다. malloc 유추의 경우 – morechilli

1

프로세스에서 맵핑을 수행하지 않는 모든 스레드를 일시 중단하고, 맵핑 해제/재 맵핑하고, 일시 중단 된 스레드를 일시 중단하십시오. 그것은 우아하지 않지만, 당신이 필요로하는 종류의 상호 배제를 제공하기 위해 손에서 생각할 수있는 유일한 방법입니다.

+0

스레드 안전 모드에서 모든 스레드를 어떻게 일시 중단 하시겠습니까? – morechilli

+0

모든 스레드 열거 : http://msdn.microsoft.com/en-us/library/ms686780(VS.85).aspx 경주가 있기 때문에 (스냅 샷을 찍은 후에 스레드가 다른 스레드를 생성 할 수 있지만 일시 중지) 각 스냅 샷이 더 이상의 스레드를 식별 할 때까지 프로세스를 반복해야합니다. 스냅 샷 (스레드 ID를 제공)에서 각 스레드를 OpenThread하여 핸들을 가져온 다음 해당 핸들에서 SuspendThread를 호출 할 수 있습니다. – DrPizza

+0

나는 제안을 감사하지만 이것이 해킹 대체에 대한 해킹입니다 걱정 - 나는 원래의 문제는 다른 프로세스에서 VirtualAllocEx 호출에 열려 있다는 것을 깨달 았어. 나는 API가 나를 도와주지 않을 것이라고 생각한다. – morechilli

0

이전 게시물에서 알 수 있듯이 메모리 매핑을 변경하는 동안 프로세스의 모든 스레드를 일시 중단 할 수 있습니다. SuspendThread()/ResumeThread()를 사용할 수 있습니다. 이것은 여러분의 코드가 다른 모든 쓰레드에 대해 알 필요가 있고 쓰레드 핸들을 유지해야한다는 단점이 있습니다.

다른 방법으로는 Windows debug API을 사용하여 모든 스레드를 일시 중단하는 것입니다.프로세스에 디버거가 연결되어 있으면 프로세스가 실패 할 때마다 디버거가 오류를 처리하고 프로세스를 다시 시작할 때까지 Windows는 프로세스의 모든 스레드를 일시 중단합니다.

도 매우 유사하다이 질문을 볼 수 있지만, 다른 말로 표현 : Replacing memory mappings atomically on Windows

+0

십자가 링크와 제안에 감사드립니다. 고맙게도 우리는 마침내 우리 고객이 32 비트 지원을 단계적으로 (새로운 작업을 위해) 단계적으로 없애 버렸기 때문에 주소 제한이 덜 중요해졌습니다. 그러나 누락 된 API 기능은 여전히 ​​저를 좌절시키고 유산 문제로 남아 있습니다. – morechilli