파일 버퍼링
이 항목에서는 버퍼링되지 않은 파일 입력/출력(I/O)으로도 알려진 파일 버퍼링의 애플리케이션 제어에 대한 다양한 고려 사항에 대해 설명합니다. 파일 버퍼링은 일반적으로 백그라운드 시스템에서 처리되며 달리 지정하지 않는 한 Windows 운영 체제 내에서 파일 캐싱의 일부로 간주됩니다. 캐싱 및 버퍼링이라는 용어가 혼용되는 경우도 있지만, 이 항목에서는 시스템에서 캐시(버퍼링)되지 않는 데이터와 상호 작용(그렇지 않으면 사용자 모드 애플리케이션을 직접 제어할 수 없음)하는 방법을 설명하는 컨텍스트에서 버퍼링이라는 용어를 사용합니다.
CreateFile 함수를 사용하여 파일을 열거나 만들 때 파일에서 읽거나 파일에 쓰는 데이터의 시스템 캐싱을 사용하지 않도록 FILE_FLAG_NO_BUFFERING 플래그를 지정할 수 있습니다. 이렇게 하면 데이터 I/O 버퍼링에 대한 완전하고 직접적인 제어가 가능하지만, 파일 및 유사한 디바이스의 경우 고려해야 할 데이터 정렬 요구 사항이 있습니다.
참고
이 정렬 정보는 검색을 지원하는 파일 및 파일 위치 포인터(또는 오프셋)의 개념과 같은 디바이스의 I/O에 적용됩니다. 명명된 파이프 또는 통신 디바이스와 같이 검색하지 않는 디바이스의 경우 버퍼링을 해제하는 데 특정 정렬이 필요하지 않을 수 있습니다. 이 경우 정렬로 얻을 수 있는 제한 사항 또는 효율성은 기본 기술에 따라 달라집니다.
간단한 예제에서 애플리케이션은 FILE_FLAG_NO_BUFFERING 플래그를 사용하여 쓰기 액세스용 파일을 연 다음 애플리케이션 내에 정의된 데이터 버퍼를 사용하여 WriteFile 함수에 대한 호출을 수행합니다. 이 로컬 버퍼는 이러한 상황에서 이 작업을 위해 존재하는 유일한 파일 버퍼입니다. 물리적 디스크 레이아웃, 파일 시스템 스토리지 레이아웃 및 시스템 수준 파일 포인터 위치 추적으로 인해 로컬로 정의된 데이터 버퍼가 다음 섹션에서 설명하는 특정 정렬 조건을 충족하지 않으면 이 쓰기 작업이 실패합니다.
참고
캐싱에 대한 논의는 물리적 디스크 자체에 대한 하드웨어 캐싱을 고려하지 않으며, 어떤 경우에도 시스템의 직접 제어 내에 있다고 보장되지 않습니다. 이는 이 항목에 명시된 요구 사항에 영향을 주지 않습니다.
FILE_FLAG_NO_BUFFERING이 다른 캐시 관련 플래그와 상호 작용하는 방법에 대한 자세한 내용은 CreateFile을 참조하세요.
정렬 및 파일 액세스 요구 사항
앞에서 설명한 대로 애플리케이션은 FILE_FLAG_NO_BUFFERING을 통해 열린 파일로 작업할 때 특정 요구 사항을 충족해야 합니다. 다음 세부 사항이 적용됩니다.
- OVERLAPPED 구조체의 선택적 파일 오프셋을 포함한 파일 액세스 크기(지정된 경우)는 볼륨 섹터 크기의 정수 배수인 바이트 수에 대한 것이어야 합니다. 예를 들어 섹터 크기가 512바이트인 경우 애플리케이션은 512, 1,024, 1,536 또는 2,048바이트(335, 981 또는 7,171바이트는 아님)의 읽기 및 쓰기를 요청할 수 있습니다.
- 읽기 및 쓰기 작업을 위한 파일 액세스 버퍼 주소는 물리적 섹터에 맞춰 정렬해야 합니다. 즉, 볼륨의 물리적 섹터 크기의 정수 배수인 메모리의 주소에 맞춰 정렬됩니다. 디스크에 따라 이 요구 사항이 적용되지 않을 수 있습니다.
애플리케이션 개발자는 4,096바이트의 물리적 미디어 섹터 크기로 출시된 새로운 유형의 스토리지 디바이스를 메모해 두어야 합니다. 이러한 디바이스의 업계 이름은 "고급 형식"입니다. 4,096바이트를 미디어 주소 지정 단위로 직접 도입하는 경우 호환성 문제가 있을 수 있으므로 임시적인 호환성 솔루션은 일반 512바이트 섹터 스토리지 디바이스를 에뮬레이트하지만 표준 ATA 및 SCSI 명령을 통해 실제 섹터 크기에 대한 정보를 제공하는 디바이스를 도입하는 것입니다.
이 에뮬레이션의 결과로 개발자는 기본적으로 두 가지 섹터 크기를 이해해야 합니다.
- 논리 섹터: 미디어의 논리적 블록 주소 지정에 사용되는 단위입니다. 또한 이를 스토리지가 허용할 수 있는 가장 작은 쓰기 단위로 생각할 수도 있습니다. "에뮬레이션"입니다.
- 물리적 섹터: 디바이스에 대한 읽기 및 쓰기 작업이 단일 작업으로 완료되는 단위입니다. 이 단위는 원자성 쓰기 단위이며 최적의 성능 및 안정성 특성을 갖기 위해 버퍼되지 않은 I/O를 정렬해야 합니다.
IOCTL_DISK_GET_DRIVE_GEOMETRY 및 GetDiskFreeSpace와 같은 대부분의 최신 Windows API는 논리적 섹터 크기를 반환하지만, 물리적 섹터 크기는 STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR 구조체의 BytesPerPhysicalSector 멤버에 포함된 관련 정보와 함께 IOCTL_STORAGE_QUERY_PROPERTY 제어 코드를 통해 검색할 수 있습니다. 예제는 STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR의 샘플 코드를 참조하세요. Microsoft는 애플리케이션이 이 섹터 크기 전환에 대비할 수 있도록 개발자가 IOCTL_STORAGE_QUERY_PROPERTY 제어 코드를 통해 보고한 물리적 섹터 크기에 버퍼되지 않은 I/O를 정렬하는 것이 좋습니다.
Windows Server 2003 및 Windows XP:STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR 구조체를 사용할 수 없습니다. Windows Vista 및 Windows Server 2008에서 도입되었습니다.
읽기 및 쓰기 작업을 위한 버퍼 주소는 섹터 정렬되어야 하므로 애플리케이션은 이러한 버퍼가 할당되는 방식을 직접 제어해야 합니다. 버퍼를 섹터 정렬하는 한 가지 방법은 VirtualAlloc 함수를 사용하여 버퍼를 할당하는 것입니다. 다음을 살펴보세요.
- VirtualAlloc은 시스템 페이지 크기의 정수 배수인 주소에 정렬된 메모리를 할당합니다. x64 및 x86의 경우 페이지 크기는 4,096바이트이고 Itanium 기반 시스템의 경우 8,192바이트입니다. 자세한 내용은 GetSystemInfo 함수를 참조하세요.
- 섹터 크기는 일반적으로 직접 액세스 스토리지 디바이스(하드 드라이브)의 경우 512~4,096바이트, CD-ROM의 경우 2,048바이트입니다.
- 페이지 및 섹터 크기는 모두 2의 거듭제곱입니다.
따라서 섹터 크기가 페이지 크기보다 큰 경우는 드물기 때문에 대부분의 경우 페이지 정렬 메모리도 섹터 정렬됩니다.
수동으로 정렬된 메모리 버퍼를 가져오는 또 다른 방법은 C Run-Time 라이브러리에서 _aligned_malloc 함수를 사용하는 것입니다. 버퍼 정렬을 수동으로 제어하는 방법에 대한 예제는 WriteFile의 예제 코드 섹션에서 C++ 언어 코드 예제를 참조하세요.