파일 매핑 [Windows System Programming]

다루는 내용

Windows System에서는 파일을 프로세스의 메모리에 매핑할 수 있는 기능을 제공합니다.

1. CreateFileMapping
2. MapViewOfFile
3. UnmapViewOfFile
4. 코드 설명

설명에 사용할 코드

콘솔 응용 프로그램으로 파일 매핑을 이용합니다.

파일에 최대 100개의 상품 정보를 기록할 수 있습니다.

파일 매핑을 통해 파일의 데이터를 프로세스의 메모리에 매핑하여 사용합니다.

#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib,"Shlwapi.lib")
#include <stdio.h>
#include <conio.h>
#define FNAME	TEXT("product.txt")
#define MAX_PNAME_LEN	256
#define MAX_PRODUCT	100
struct Product
{
	int pno;
	char pname[MAX_PNAME_LEN];
	int price;
};
Product* base = 0;
int last_pno = 0;

void MakeFile()
{
	if (PathFileExists(FNAME))
	{	
		return;
	}
	HANDLE hFile = CreateFile(FNAME, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);	
	Product products[MAX_PRODUCT] = {};
	WriteFile(hFile, products, sizeof(Product) * MAX_PRODUCT, 0, 0);
	CloseHandle(hFile);
}

void InitLastPNo()
{
	for (int i = 0; i < MAX_PRODUCT; i++)
	{
		if (base[i].pno==0)
		{
			last_pno = i;
			return;
		}
	}
	last_pno = MAX_PRODUCT;
}
int SelectMenu()
{
	system("cls");
	printf("[1]:상품 추가 [2]:전체 보기 [0]:종료\n");

	return _getch();
}
void AddProduct()
{
	printf("%d번 상품 추가를 선택하였습니다. \n", last_pno + 1);
	Product* pro = base + last_pno;
	last_pno++;
	pro->pno = last_pno;
	printf("상품 명:");
	scanf_s("%s", pro->pname, MAX_PNAME_LEN);
	printf("가격:");
	scanf_s("%d", &(pro->price));
}
void ListProduct()
{
	for (int i = 0; i < MAX_PRODUCT; i++)
	{
		if (base[i].pno)
		{
			printf("%02d %20s %d원", base[i].pno, base[i].pname, base[i].price);
			return;
		}
	}
}
void DoIt()
{
	InitLastPNo();
	int key = 0;
	while ((key = SelectMenu()) != '0')
	{
		switch (key)
		{
		case '1': AddProduct(); break;
		case '2': ListProduct(); break;
		default: printf("잘못 선택하였습니다.");break;
		}
		printf("아무 키나 누르세요.\n");
		_getch();
	}
}
int main()
{
	MakeFile();
	HANDLE hFile = CreateFile(FNAME, GENERIC_ALL, 0, 0, OPEN_EXISTING, 0, 0);	
	HANDLE hMapping = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, 0, 0);//매핑 핸들 발급
	base = (Product*)MapViewOfFile(hMapping, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0);
	DoIt();
	UnmapViewOfFile(base);
	CloseHandle(hMapping);
	CloseHandle(hFile);
	return 0;
}

1. CreateFileMapping

파일 매핑 개체를 생성하는 함수입니다.

HANDLE
WINAPI
CreateFileMapping(
    HANDLE hFile,
    LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
    DWORD flProtect,
    DWORD dwMaximumSizeHigh,
    DWORD dwMaximumSizeLow,
    LPCWSTR lpName
    )                                           msdn 바로가기

반환 값

파일 매핑 개체 핸들을 반환합니다.

실패하면 NULL을 반환합니다.

HANDLE hFile

파일 매핑 개체를 만들 파일 개체 핸들입니다.

LPSECURITY_ATTRIBUTES lpFileMappingAttributes

보안 설명자입니다.

NULL을 전달하면 기본 보안 설명자를 사용합니다.

DWORD flProtect

파일 매핑 개체의 페이지 보호 지정입니다.

매핑 뷰의 보호 지정과 호환합니다.

파일 매핑 페이지의 내용을 읽기 전용으로 하려면 PAGE_READONLY를 사용합니다.

읽기 쓰기를 모두 하고자 한다면 PAGE_READWRITE를 사용합니다.

#define PAGE_NOACCESS           0x01    
#define PAGE_READONLY           0x02    
#define PAGE_READWRITE          0x04    
#define PAGE_WRITECOPY          0x08    
#define PAGE_EXECUTE            0x10    
#define PAGE_EXECUTE_READ       0x20    
#define PAGE_EXECUTE_READWRITE  0x40    
#define PAGE_EXECUTE_WRITECOPY  0x80    
#define PAGE_GUARD             0x100    
#define PAGE_NOCACHE           0x200    
#define PAGE_WRITECOMBINE      0x400    
#define PAGE_GRAPHICS_NOACCESS           0x0800    
#define PAGE_GRAPHICS_READONLY           0x1000    
#define PAGE_GRAPHICS_READWRITE          0x2000    
#define PAGE_GRAPHICS_EXECUTE            0x4000    
#define PAGE_GRAPHICS_EXECUTE_READ       0x8000    
#define PAGE_GRAPHICS_EXECUTE_READWRITE 0x10000    
#define PAGE_GRAPHICS_COHERENT          0x20000    
#define PAGE_GRAPHICS_NOCACHE           0x40000    
#define PAGE_ENCLAVE_THREAD_CONTROL 0x80000000  
#define PAGE_REVERT_TO_FILE_MAP     0x80000000  
#define PAGE_TARGETS_NO_UPDATE      0x40000000  
#define PAGE_TARGETS_INVALID        0x40000000  
#define PAGE_ENCLAVE_UNVALIDATED    0x20000000  
#define PAGE_ENCLAVE_MASK           0x10000000

DWORD dwMaximumSizeHigh

파일 매핑 개체의 최대 크기의 상위 DWORD입니다.

DWORD dwMaximumSizeLow

파일 매핑 개체의 최대 크기의 하위 DWORD입니다.

만약 dwMaximumSizeHigh인자와 dwMaximumSizeLow 모두 0이면 현재 파일 크기와 같습니다.

LPCWSTR lpName

파일 매핑 개체의 이름이며 NULL이면 이름 없이 만들어집니다.

2. MapViewOfFile

파일 매핑 보기를 프로세스의 메모리 주소에 매핑하는 함수입니다.

LPVOID
WINAPI
MapViewOfFile(
    HANDLE hFileMappingObject,
    DWORD dwDesiredAccess,
    DWORD dwFileOffsetHigh,
    DWORD dwFileOffsetLow,
    SIZE_T dwNumberOfBytesToMap
    )                                               msdn 바로가기

반환 값

성공하면 매핑 뷰의 시작 주소를 반환합니다.

파일의 데이터가 있는 곳이 프로세스의 메모리에 매핑한 주소입니다.

파일 입출력 함수를 사용하지 않고 매핑한 주소를 설정한 변수를 통해 파일 내용을 읽어오거나 쓰기를 할 수 있습니다.

실패하면 NULL을 반환합니다.

HANDLE hFileMappingObject

파일 매핑 개체의 핸들입니다.

DWORD dwDesiredAccess

매핑 페이지의 보호를 결정하는 액세스 유형입니다.

쓰기 액세스는 FILE_MAP_WRTIE입니다.

읽기 액세서는 FILE_MAP_READ입니다.

읽기/쓰기 액세스를 모두 하려면 FILE_MAP_READ|FILE_MAP_WRTIE를 사용하세요.

#define FILE_MAP_WRITE            SECTION_MAP_WRITE
#define FILE_MAP_READ             SECTION_MAP_READ
#define FILE_MAP_ALL_ACCESS       SECTION_ALL_ACCESS

#define FILE_MAP_EXECUTE          SECTION_MAP_EXECUTE_EXPLICIT  // not included in FILE_MAP_ALL_ACCESS

#define FILE_MAP_COPY             0x00000001

#define FILE_MAP_RESERVE          0x80000000
#define FILE_MAP_TARGETS_INVALID  0x40000000
#define FILE_MAP_LARGE_PAGES      0x20000000

DWORD dwFileOffsetHigh

파일 매핑 보기의 상위 offset입니다.

DWORD dwFileOffsetLow

파일 매핑 보기의 하위 offset입니다.

SIZE_T dwNumberOfBytesToMap

매핑할 바이트 수입니다.

0을 설정하면 매핑 개체의 전체 범위를 매핑합니다.

3. UnmapViewOfFile

프로세스이 주소에서 매핑 뷰를 해제합니다.

BOOL
WINAPI
UnmapViewOfFile(
    LPCVOID lpBaseAddress
    );                                        msdn 바로가기

반환 값

성공하면 0이 아닌 값을 반환합니다.

실패하면 0을 반환합니다.

LPCVOID lpBaseAddress

프로세스에 매핑한 주소입니다.

MapViewOfFile에서 반환한 값과 같아야 합니다.

4. 코드 설명

PathFileExistsA

파일이 존재하는지 확인하는 PathFileExists 함수를 사용하기 위한 헤더 포함문과 라이브러리 참조문입니다.

#include <Shlwapi.h>
#pragma comment(lib,"Shlwapi.lib")

파일 명을 입력하였을 때 존재하면 TRUE를 반환합니다.

존재하지 않으면 FALSE를 반환합니다.

BOOL PathFileExistsA(
  LPCSTR pszPath
);

파일 이름과 상품 형식 정의

파일 이름과 상품 형식을 정의합니다.

#define FNAME	TEXT("product.txt")
#define MAX_PNAME_LEN	256
#define MAX_PRODUCT	100
struct Product
{
	int pno;
	char pname[MAX_PNAME_LEN];
	int price;
};

매핑한 주소

파일 매핑할 데이터의 메모리 주소는 base로 선언할게요.

상품은 순차 보관할 것이며 가장 최근에 보관한 상품 번호는 last_pno 변수에 설정할게요.

Product* base = 0; 
int last_pno = 0;

MakeFile 함수

100(MAX_PRODUCT) 개의 상품 데이터를 기록할 파일을 생성하는 함수입니다.

만약 이미 파일이 있으면 함수를 종료합니다.

파일은 쓰기 모드로 생성하고 MAX_PRODUCT 개수의 Product를 파일에 기록합니다.

이 때 모든 내용은 0으로 초기화한 상태의 데이터입니다.

*구조체 배열을 선언할 때 초기화 의도 {}를 나타내면 0으로 초기화해 줍니다.*

void MakeFile()
{
	if (PathFileExists(FNAME))
	{	
		return;
	}
	HANDLE hFile = CreateFile(FNAME, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);	
	Product products[MAX_PRODUCT] = {};
	WriteFile(hFile, products, sizeof(Product) * MAX_PRODUCT, 0, 0);
	CloseHandle(hFile);
}

InitLastPNo 함수

여기에서는 상품을 순차 보관합니다.

따라서 앞에서 부터 첨으로 pno가 0인 위치가 이번에 데이터를 기록할 위치입니다.

pno은 배열의 인덱스 + 1로 설정할 것입니다.

따라서 첨으로 pno가 0인 위치의 인덱스가 last_pno입니다.

void InitLastPNo()
{
	for (int i = 0; i < MAX_PRODUCT; i++)
	{
		if (base[i].pno==0)
		{
			last_pno = i;
			return;
		}
	}
	last_pno = MAX_PRODUCT;
}

SelectMenu 함수

최종 사용자가 기능을 선택하는 함수입니다.

여기에서는 상품 추가, 전체 보기, 종료 중에 하나를 선택할 수 있습니다.

int SelectMenu()
{
	system("cls");
	printf("[1]:상품 추가 [2]:전체 보기 [0]:종료\n");

	return _getch();
}

AddProduct 함수

여기서는 상품을 순차 보관합니다.

따라서 last_pno 인덱스를 통해 추가할 메모리 주소를 계산할 수 있습니다.

last_pno을 1 추가한 값으로 상품 번호를 설정합니다.

해당 주소에 상품 명과 가격을 입력받습니다.

void AddProduct()
{
	printf("%d번 상품 추가를 선택하였습니다. \n", last_pno + 1);
	Product* pro = base + last_pno;
	last_pno++;
	pro->pno = last_pno;
	printf("상품 명:");
	scanf_s("%s", pro->pname, MAX_PNAME_LEN);
	printf("가격:");
	scanf_s("%d", &(pro->price));
}

ListProduct 함수

전체 상품 정보를 출력하는 함수입니다.

void ListProduct()
{
	for (int i = 0; i < MAX_PRODUCT; i++)
	{
		if (base[i].pno)
		{
			printf("%02d %20s %d원", base[i].pno, base[i].pname, base[i].price);
			return;
		}
	}
}

DoIt 함수

최종 사용자가 종료를 선택하기 전까지 반복합니다.

최종 사용자가 선택한 기능에 따라 상품 추가 혹은 전체 보기를 수행합니다.

void DoIt()
{
	InitLastPNo();
	int key = 0;
	while ((key = SelectMenu()) != '0')
	{
		switch (key)
		{
		case '1': AddProduct(); break;
		case '2': ListProduct(); break;
		default: printf("잘못 선택하였습니다.");break;
		}
		printf("아무 키나 누르세요.\n");
		_getch();
	}
}

진입점 main 함수

MakeFile 함수를 호출하여 상품 파일이 없으면 새로 생성합니다.

파일 개체를 생성합니다.

파일 매핑 개체를 생성합니다.

프로세스 메모리 주소로 매핑 뷰를 설정합니다.

DoIt 함수 호출을 통해 최종 사용자와 상호 작용합니다.

사용이 끝난 매핑 뷰를 해제합니다.

매핑 핸들 개체와 파일 개체를 닫습니다.

int main()
{
	MakeFile();
	HANDLE hFile = CreateFile(FNAME, GENERIC_ALL, 0, 0, OPEN_EXISTING, 0, 0);	
	HANDLE hMapping = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, 0, 0);//매핑 핸들 발급
	base = (Product*)MapViewOfFile(hMapping, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0);
	DoIt();
	UnmapViewOfFile(base);
	CloseHandle(hMapping);
	CloseHandle(hFile);
	return 0;
}
파일 매핑
파일 매핑