본문 바로가기

지식/VC++

다중의 메모리 맵파일 사용시 주의 사항

용량이 큰 binary data를 핸들링하는데 있어서 메모리 맵파일 만큼 편리한것도 없을 것같다.
현재 이 메모리 맵파일을 이용하여 대용량 DB를 설계하고 구현중이다.

메모리 맵파일을 사용함에 있어서 몇가지 주의 사항을 남기고자 한다.

ex)

extern "C"__declspec(dllexport) int EXSIDB_CreateDB(char *filename, int classification, int sub_class,int db_id, unsigned int data_num = EXSIDB_DEFAULT_DATANUM)
{
 if(data_num <= 0)
  return EXSIDB_DATANUM_ERROR;
 
 if(data_num > EXSIDB_MAXDATA)
  return EXSIDB_DATANUM_ERROR;

 FILE *fp;
 fp = fopen(filename,"w+b");
 
 if(fp == NULL)
  return EXSIFILE_OPEN_ERROR;
 
 EXSIDB_HEADER exhdr;
 exhdr.verify_code = EXSIDB_VERIFYCODE;
 exhdr.clssification = classification;
 exhdr.sub_class  = sub_class;
 exhdr.db_id   = db_id;
 exhdr.verison  = EXSIDB_VERSION;
 exhdr.total_dat_num = data_num;
 exhdr.cur_data_num = 0;

 fwrite(&exhdr,sizeof(EXSIDB_HEADER),1,fp);
 
 EXSIDB_DATA exdata;
 memset(&exdata,0,sizeof(EXSIDB_DATA));

 for(unsigned int i=0;i < data_num; i++)
 {
  exdata.data_id  = i;
  fseek(fp,0,SEEK_END);
  fwrite(&exdata,sizeof(EXSIDB_DATA),1,fp);
 }
 
 fclose(fp);
 return EXSI_OK;
}


extern "C"__declspec(dllexport) int EXSIDB_LoadDB(char *filename,HANDLE *hFile,HANDLE *hMap, EXSIDB_HEADER **pHdr,EXSIDB_DATA **pData)
{
 *hFile = CreateFile(filename,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if(*hFile == INVALID_HANDLE_VALUE)
 {
  return EXSIFILE_OPEN_ERROR;
 }
 *(hMap) = CreateFileMapping(*(hFile), NULL, PAGE_READWRITE,0,0,NULL); //EXSIDB_SMMNAME);
 if(*(hMap) == NULL)
 {
  CloseHandle(*(hFile));
  return EXSIFILE_OPEN_ERROR;
 }

 *pHdr = (EXSIDB_HEADER*)MapViewOfFile(*(hMap), FILE_MAP_ALL_ACCESS,0,0,0);

 if(*pHdr== NULL)
  return EXSI_ERROR;

 *pData = (EXSIDB_DATA*)*pHdr+sizeof(EXSIDB_HEADER);

 return EXSI_OK;
}

위의 소스는 본인이 개발하여 사용하고 있는 DLL에서 발췌하였다.
먼저 CreateDB함수를 호출하여 원하는 크기의 파일을 생성한다.
그 이유는 메모리처럼 realloc을 사용하여 중간에 크기를 늘릴수 없기 때문이다.
따라서 미리 사용할 용량만큼의 파일을 생성하여야한다.

그다음에 해당파일을 메모리맵에 맵핑하고 파일의 포인터를 얻어오면 반환된 포인터를 통하여
파일을 마치 포인터처럼 사용 할 수 있게된다.
이 때 주의 할 점은

CreateFileMapping 의 함수 인자중에 제일마지막 인자이다.
MSDN에 나온 함수의 원형이다.

HANDLE CreateFileMapping(
  HANDLE hFileLPSECURITY_ATTRIBUTES lpAttributesDWORD flProtectDWORD dwMaximumSizeHighDWORD dwMaximumSizeLowLPCTSTR lpName);


lpName
[in] A pointer to a null-terminated string that specifies the name of a mapping object.

If this parameter matches the name of an existing mapping object that is named, the function requests access to the mapping object with the protection that flProtect specifies.

If this parameter is NULL, the mapping object is created without a name.

If lpName matches the name of an existing event, semaphore, mutex, waitable timer, or job object, the function fails, and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same namespace.

Terminal Services:   The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character (\). For more information, see Kernel Object Namespaces.
Windows XP:   Fast user switching is implemented by using Terminal Services sessions. The first user to log on uses session 0 (zero), the next user to log on uses session 1 (one), and so on. Kernel object names must follow the guidelines that are outlined for Terminal Services so that applications can support multiple users.
Windows 2000:   If Terminal Services is not running, the "Global\" and "Local\" prefixes are ignored. The remainder of the name can contain any character except the backslash character.
Windows NT:  The name can contain any character except the backslash character.
Windows Me/98/95:   The name can contain any character except the backslash character. An empty string ("") is a valid object name.

간단히 설명하자면 맵핑 오브젝트의 이름이라고 설명할 수 있는데
메모리를 공유할 시 에 사용한다고 써있다. 즉 파일 하나를 두고 서로 다른 프로세서 끼리 통신시
오브젝트 이름을 통하여 맵핑오브젝트의 접근을 보호한다.

뭐 보통은 공유하지 않을 시 NULL을 입력하여 사용한다고 하는데..
어쨋든 본인은 하나의 시스템에서 몇개의 메모리 맵파일을 생성하여 사용하려고 테스트
중에 1번 데이터 베이스에 파일을 쓰면 2,3,4...번의데이터 베이스에도 같은 데이터가 입력되는
황당한? 경우가 발생하였다.

한참을 디버깅끝에 찾아낸 결론은
비록 물리적으로 다른 파일을 열어서 메모리 맵파일화 하여 사용한다 하더라도
맵핑 오브젝트의 이름이 같다면 모든 파일이 하나의 파일로 취급된다는것이다.

이말은 좋게 말하면 하나의 포인터로 몇개의 파일을 핸들링 할 수 있다는 말이기도하다.
아직은 버그를 발견한 성과(ㅋ)지만 몇개의 파일을 하나의 포인터로 핸들릴 할 수있다는
잇점을 살려서 사용해봐야겠다.