이 문서에서는 IOCTL(고유한 I/O 제어 코드)을 만드는 방법을 설명합니다. IOCTL은 다음과 같습니다.
- 공용 IOCTL은 일반적으로 시스템 정의 및 Microsoft에서 문서화합니다.
- 프라이빗 IOCTL은 일반적으로 공급업체의 소프트웨어 구성 요소에서만 서로 통신하는 데 사용됩니다. 일반적으로 공급업체의 헤더 파일에 정의되며 Microsoft에서 문서화하지 않습니다.
IOCTL 레이아웃
IOCTL은 여러 필드로 구성된 32비트 값입니다. 다음 그림에서는 IOCTL의 비트 단위 레이아웃을 보여 줍니다.
IOCTL의 각 필드에는 다음 표에 설명된 대로 특정 목적이 있습니다.
| 분야 | IOCTL의 비트 | 설명 |
|---|---|---|
| 보통 | 31 | 공급업체는 DeviceType에 대해 공급업체 할당 값을 사용할 때 이 비트를 설정해야 합니다. |
| DeviceType | 16-30 | 디바이스 유형을 식별합니다. 이 값은 드라이버의 DEVICE_OBJECT 구조체의 DeviceType 멤버에 설정된 값과 일치해야 합니다. 공급업체는 32768에서 65535(0x8000~0xffff) 값을 사용해야 하며 Common 비트를 설정해야 합니다. 값 0~32767(0x0000~0x7fff)은 Microsoft용으로 예약되어 있습니다. 자세한 내용은 디바이스 유형 지정을 참조하세요. |
| 접근 | 14-15 | 디바이스를 나타내는 파일 개체를 열 때 호출자가 요청해야 하는 액세스 유형을 나타냅니다(IRP_MJ_CREATE참조). I/O 관리자는 호출자가 지정된 액세스 권한을 요청한 경우에만 IRP를 만들고 특정 IOCTL을 사용하여 드라이버를 호출합니다. 이 필드는 시스템 정의 상수인 FILE_ANY_ACCESS, FILE_READ_DATA 및 FILE_WRITE_DATA 사용하여 지정됩니다. |
| 사용자 지정 | 13 | 설정되면 IOCTL이 공급업체에서 정의한 IOCTL임을 나타냅니다. |
| 기능 | 2-12 | 수행할 함수를 식별하는 드라이버의 고유 코드입니다. 공급업체에서 만든 IOCTL의 경우 2048~4095(0xfff 0x800) 값을 사용하고 사용자 지정 비트를 설정합니다. 2048보다 작은 값(0x000~0x7ff)은 Microsoft용으로 예약되어 있습니다. |
| 메서드 | 0-1 | 시스템이 DeviceIoControl (또는 IoBuildDeviceIoControlRequest)의 호출자와 IRP를 처리하는 드라이버 간에 데이터를 전달하는 방법을 나타냅니다. 자세한 내용은 메서드 비트를 설정하기 위한 지침을 참조하세요. |
I/O 컨트롤 코드를 정의하는 매크로
시스템에서 제공하는 CTL_CODE 매크로를 사용하여 새 I/O 컨트롤 코드를 정의합니다. 이 매크로는 다음과 같이 devioctl.h 에 정의됩니다.
#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)
DeviceType, Function, Method 및 Access에 대한 설명은 이전 섹션을 참조하세요.
새 I/O 컨트롤 코드를 정의할 때 다음 규칙을 염두에 두세요.
사용자 모드 소프트웨어 구성 요소에 새 IOCTL을 사용할 수 있는 경우 IRP_MJ_DEVICE_CONTROL 요청과 함께 사용해야 합니다. 사용자 모드 구성 요소는 DeviceIoControl 을 호출하여 IRP_MJ_DEVICE_CONTROL 요청을 보냅니다.
커널 모드 드라이버 구성 요소에만 새 IOCTL을 사용할 수 있는 경우 IRP_MJ_INTERNAL_DEVICE_CONTROL 요청과 함께 사용해야 합니다. 커널 모드 구성 요소는 IoBuildDeviceIoControlRequest를 호출하여 IRP_MJ_INTERNAL_DEVICE_CONTROL 요청을 만들 수 있습니다. 자세한 내용은 드라이버 IOCTL 요청 만들기참조하세요.
IRP_MJ_DEVICE_CONTROL 또는 IRP_MJ_INTERNAL_DEVICE_CONTROL 요청에 사용할지 여부에 관계없이 새 IOCTL 코드의 정의는 다음 형식을 사용합니다.
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
IOCTL 상수에 대한 설명적 이름으로 IOCTL_Device_Function을 선택하세요. 여기서 Device는 디바이스 유형을 나타내고, Function은 작업을 나타냅니다. 예를 들어 시스템에서 제공하는 IOCTL_VIDEO_ENABLE_CURSOR 상수는 디바이스 에 "VIDEO"를 사용하고 Function에는 "ENABLE_CURSOR"를 사용합니다.
Access 비트를 설정하기 위한 지침
새 IOCTL을 정의할 때 디바이스를 나타내는 파일 개체를 열 때 호출자가 요청해야 하는 액세스 유형을 나타내는 Access 비트 필드의 값을 선택해야 합니다. I/O 관리자는 호출자가 지정된 액세스 권한을 요청한 경우에만 IRP를 만들고 특정 IOCTL을 사용하여 드라이버를 호출합니다.
액세스 는 다음 시스템 정의 상수로 지정됩니다.
FILE_ANY_ACCESS
I/O 관리자는 대상 디바이스 개체를 나타내는 파일 개체에 핸들이 있는 모든 호출자에 대해 IRP를 보냅니다. 새 IOCTL 코드에 대한 FILE_ANY_ACCESS 지정하기 전에 디바이스에 무제한 액세스를 허용해도 악의적인 사용자가 시스템을 손상시킬 수 있는 경로가 만들어지지 않는다는 것을 반드시 확인해야 합니다.
파일_읽기_데이터
I/O 관리자는 읽기 액세스 권한이 있는 호출자에 대해서만 IRP를 보내 기본 디바이스 드라이버가 디바이스에서 시스템 메모리로 데이터를 전송할 수 있도록 합니다.
파일_쓰기_데이터
I/O 관리자는 쓰기 액세스 권한이 있는 호출자에 대해서만 IRP를 보내 기본 디바이스 드라이버가 시스템 메모리에서 해당 디바이스로 데이터를 전송할 수 있도록 합니다.
호출자에게 읽기 및 쓰기 액세스 권한이 모두 있어야 하는 경우 FILE_READ_DATA 및 FILE_WRITE_DATA 함께 ORed할 수 있습니다.
일부 시스템 정의 I/O 제어 코드에는 액세스 값이 FILE_ANY_ACCESS 있으므로 호출자가 디바이스에 부여된 액세스 권한에 관계없이 특정 IOCTL을 보낼 수 있습니다. 예를 들어 전용 디바이스드라이버로 전송되는 I/O 제어 코드가 있습니다.
다른 시스템 정의 I/O 제어 코드는 호출자에게 읽기 액세스 권한, 쓰기 액세스 권한 또는 둘 다를 요구합니다. 예를 들어 다음 공용 IOCTL_DISK_SET_PARTITION_INFO IOCTL 정의는 호출자가 읽기 및 쓰기 액세스 권한을 모두 가지고 있는 경우에만 이 I/O 요청을 드라이버로 보낼 수 있음을 보여 갖습니다.
#define IOCTL_DISK_SET_PARTITION_INFO\
CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
FILE_READ_DATA | FILE_WRITE_DATA)
드라이버는 IoValidateDeviceIoControlAccess 를 사용하여 IOCTL의 Access 비트에서 제공하는 것보다 더 엄격한 액세스 검사를 수행할 수 있습니다.
메서드 비트를 설정하기 위한 지침
새 IOCTL을 정의할 때 시스템이 DeviceIoControl(또는 IoBuildDeviceIoControlRequest)의 호출자와 IRP를 처리하는 드라이버 간에 데이터를 전달하는 방법을 나타내는 메서드 비트 필드 값을 선택해야 합니다.
다음 시스템 정의 상수 중 하나를 사용하여 메서드 필드를 설정합니다.
METHOD_BUFFERED
일반적으로 요청당 소량의 데이터를 전송하는 데 사용되는 버퍼링된 I/O 메서드를 지정합니다. 디바이스 및 중간 드라이버에 대한 대부분의 I/O 제어 코드는 이 값을 사용합니다.
시스템에서 METHOD_BUFFERED I/O 컨트롤 코드에 대한 데이터 버퍼를 지정하는 방법에 대한 자세한 내용은 I/O 컨트롤 코드에 대한버퍼 설명을 참조하세요.
버퍼링된 I/O에 대한 자세한 내용은 버퍼링된 I/O 사용하는참조하세요.
METHOD_IN_DIRECT 또는 METHOD_OUT_DIRECT
일반적으로 신속하게 전송해야 하는 DMA 또는 PIO를 사용하여 대량의 데이터를 읽거나 쓰는 데 사용되는 직접 I/O 메서드를 지정합니다.
DeviceIoControl 또는 IoBuildDeviceIoControlRequest 호출자가 드라이버에 데이터를 전달할지 여부를 METHOD_IN_DIRECT 지정합니다.
DeviceIoControl 또는 IoBuildDeviceIoControlRequest 호출자가 드라이버에서 데이터를 받을지 여부를 METHOD_OUT_DIRECT 지정합니다.
시스템에서 METHOD_IN_DIRECT 및 METHOD_OUT_DIRECT I/O 컨트롤 코드에 대한 데이터 버퍼를 지정하는 방법에 대한 자세한 내용은 I/O 컨트롤 코드 버퍼 설명을 참조하세요.
직접 I/O에 대한 자세한 정보는 직접 I/O 사용을 참조하세요.
메서드_네이더
I/O 메서드가 버퍼링되거나 직접 지정되지 않도록 지정합니다. I/O 관리자는 시스템 버퍼 또는 MDL을 제공하지 않습니다. IRP는 DeviceIoControl 또는 IoBuildDeviceIoControlRequest에 지정된 입력 및 출력 버퍼의 사용자 모드 가상 주소를 유효성 검사나 매핑을 하지 않고 제공합니다.
시스템에서 METHOD_NEITHER I/O 컨트롤 코드에 대한 데이터 버퍼를 지정하는 방법에 대한 자세한 내용은 I/O 컨트롤 코드 대한버퍼 설명을 참조하세요.
이 메서드는 드라이버가 I/O 컨트롤 요청을 시작한 스레드의 컨텍스트에서 실행되도록 보장된 경우에만 사용할 수 있습니다. 최고 수준의 커널 모드 드라이버만 이 조건을 충족하도록 보장되므로 METHOD_NEITHER 하위 수준 디바이스 드라이버에 전달되는 IOCTL에는 거의 사용되지 않습니다.
이 메서드를 사용하면 가장 높은 수준의 드라이버가 표시됩니다.
- 요청을 수신할 때 사용자 데이터에 대한 버퍼링된 액세스 또는 직접 액세스를 설정할지 여부를 결정해야 합니다.
- 사용자 버퍼를 잠글 필요가 있을 수 있습니다.
- 구조화된 예외 처리기에서 사용자 버퍼에 대한 액세스를 래핑해야 합니다( 예외 처리 참조).
그렇지 않으면 원래 사용자 모드 호출자가 버퍼링된 데이터를 드라이버에서 사용하기 전에 변경하거나 드라이버가 사용자 버퍼에 액세스하는 것처럼 호출자를 교체할 수 있습니다.
자세한 내용은 버퍼링 및 직접 I/O를 사용하지 않는 에 관한 내용을에서 참조하세요.
기타 유용한 매크로
다음 매크로는 IOCTL에서 16비트 DeviceType 및 2비트 Method 필드를 추출하는 데 유용합니다.
#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode) (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
#define METHOD_FROM_CTL_CODE(ctrlCode) ((ULONG)(ctrlCode & 3))
이러한 매크로는 Wdm.h 및 Ntddk.h에 정의되어 있습니다.