사용자 모드 예약

경고

Windows 11 현재 사용자 모드 예약은 지원되지 않습니다. 오류로 모든 호출이 실패합니다 ERROR_NOT_SUPPORTED.

UMS(사용자 모드 예약)는 애플리케이션이 자체 스레드를 예약하는 데 사용할 수 있는 간단한 메커니즘입니다. 애플리케이션은 시스템 스케줄러 를 포함하지 않고 사용자 모드에서 UMS 스레드 간에 전환하고 커널에서 UMS 스레드가 차단되는 경우 프로세서를 다시 제어할 수 있습니다. UMS 스레드는 단일 스레드의 스레드 컨텍스트를 공유하는 대신 각 UMS 스레드에 자체 스레드 컨텍스트가 있다는 점에서 파이버 와 다릅니다. 사용자 모드에서 스레드 간을 전환하는 기능은 시스템 호출이 거의 없는 많은 수의 단기 작업 항목을 관리하기 위해 스레드 풀 보다 UMS를 더 효율적으로 만듭니다.

UMS는 다중 프로세서 또는 다중 코어 시스템에서 여러 스레드를 동시에 효율적으로 실행해야 하는 고성능 요구 사항이 있는 애플리케이션에 권장됩니다. UMS를 활용하려면 애플리케이션이 애플리케이션의 UMS 스레드를 관리하고 실행 시기를 결정하는 스케줄러 구성 요소를 구현해야 합니다. 개발자는 애플리케이션 성능 요구 사항이 이러한 구성 요소 개발에 관련된 작업을 정당화하는지 여부를 고려해야 합니다. 보통 성능 요구 사항이 있는 애플리케이션은 시스템 스케줄러가 스레드를 예약할 수 있도록 하여 더 잘 처리될 수 있습니다.

UMS는 AMD64 및 Windows 7 및 Windows Server 2008 R2의 Itanium 버전에서 실행되는 64비트 애플리케이션에서 Windows 10 버전 21H2 및 Windows Server 2022까지 사용할 수 있습니다. 이 기능은 Arm64, 32비트 버전의 Windows 또는 Windows 11 사용할 수 없습니다.

자세한 내용은 다음 섹션을 참조하세요.

UMS 스케줄러

애플리케이션의 UMS 스케줄러는 UMS 스레드를 만들고, 관리하고, 삭제하고, 실행할 UMS 스레드를 결정합니다. 애플리케이션의 스케줄러는 다음 작업을 수행합니다.

  • 애플리케이션이 UMS 작업자 스레드를 실행할 각 프로세서에 대해 하나의 UMS 스케줄러 스레드를 만듭니다.
  • UMS 작업자 스레드를 만들어 애플리케이션의 작업을 수행합니다.
  • 실행할 준비가 된 작업자 스레드의 자체 준비 스레드 큐를 유지하고 애플리케이션의 예약 정책에 따라 실행할 스레드를 선택합니다.
  • 시스템이 커널에서 처리를 완료한 후 스레드를 큐에 대기하는 하나 이상의 완료 목록을 만들고 모니터링합니다. 여기에는 차단 해제되는 시스템 호출에서 이전에 차단된 새로 만든 작업자 스레드 및 스레드가 포함됩니다.
  • 시스템의 알림을 처리하는 스케줄러 진입점 함수를 제공합니다. 스케줄러 스레드가 만들어지거나, 작업자 스레드가 시스템 호출에서 차단되거나, 작업자 스레드가 명시적으로 제어를 생성할 때 시스템은 진입점 함수를 호출합니다.
  • 실행이 완료된 작업자 스레드에 대한 정리 작업을 수행합니다.
  • 애플리케이션에서 요청할 때 스케줄러를 순서대로 종료합니다.

UMS 스케줄러 스레드

UMS 스케줄러 스레드는 EnterUmsSchedulingMode 함수를 호출하여 자신을 UMS로 변환한 일반 스레드입니다. 시스템 스케줄러는 다른 준비 스레드에 비해 우선 순위에 따라 UMS 스케줄러 스레드가 실행되는 시기를 결정합니다. 스케줄러 스레드가 실행되는 프로세서는 UMS가 아닌 스레드와 동일한 스레드 선호도의 영향을 받습니다.

EnterUmsSchedulingMode의 호출자는 UMS 스케줄러 스레드와 연결할 완료 목록 및 UmsSchedulerProc 진입점 함수를 지정합니다. 호출 스레드를 UMS로 변환하는 작업이 완료되면 시스템에서 지정된 진입점 함수를 호출합니다. 스케줄러 진입점 함수는 지정된 스레드에 대한 적절한 다음 작업을 결정합니다. 자세한 내용은 이 항목의 뒷부분에 있는 UMS 스케줄러 진입점 함수 를 참조하세요.

애플리케이션은 UMS 스레드를 실행하는 데 사용할 각 프로세서에 대해 하나의 UMS 스케줄러 스레드를 만들 수 있습니다. 애플리케이션은 특정 논리 프로세서에 대해 각 UMS 스케줄러 스레드의 선호도를 설정할 수도 있습니다. 이 경우 관련 없는 스레드가 해당 프로세서에서 실행되지 않도록 제외하여 해당 스케줄러 스레드에 대해 효과적으로 예약하는 경향이 있습니다. 이러한 방식으로 스레드 선호도를 설정하면 시스템에서 실행될 수 있는 다른 프로세스가 부족하여 전반적인 시스템 성능에 영향을 줄 수 있습니다. 스레드 선호도에 대한 자세한 내용은 여러 프로세서를 참조하세요.

UMS 작업자 스레드, 스레드 컨텍스트 및 완료 목록

UMS 작업자 스레드는 PROC_THREAD_ATTRIBUTE_UMS_THREAD 특성을 사용하여 CreateRemoteThreadEx 를 호출하고 UMS 스레드 컨텍스트 및 완료 목록을 지정하여 만들어집니다.

UMS 스레드 컨텍스트는 작업자 스레드의 UMS 스레드 상태를 나타내며 UMS 함수 호출에서 작업자 스레드를 식별하는 데 사용됩니다. CreateUmsThreadContext를 호출하여 만듭니다.

CreateUmsCompletionList 함수를 호출하여 완료 목록을 만듭니다. 완료 목록은 커널에서 실행을 완료하고 사용자 모드에서 실행할 준비가 된 UMS 작업자 스레드를 받습니다. 시스템만 작업자 스레드를 완료 목록에 큐에 대기할 수 있습니다. 새 UMS 작업자 스레드는 스레드를 만들 때 지정된 완료 목록에 자동으로 큐에 대기됩니다. 이전에 차단된 작업자 스레드도 더 이상 차단되지 않을 때 완료 목록에 큐에 대기됩니다.

각 UMS 스케줄러 스레드는 단일 완성 목록과 연결됩니다. 그러나 동일한 완료 목록을 임의의 수의 UMS 스케줄러 스레드와 연결할 수 있으며 스케줄러 스레드는 포인터가 있는 완료 목록에서 UMS 컨텍스트를 검색할 수 있습니다.

각 완료 목록에는 하나 이상의 작업자 스레드를 빈 목록에 큐에 대기할 때 시스템에서 신호를 받는 연결된 이벤트가 있습니다. GetUmsCompletionListEvent 함수는 지정된 완료 목록에 대한 이벤트에 대한 핸들을 검색합니다. 애플리케이션은 애플리케이션에 적합한 다른 이벤트와 함께 둘 이상의 완료 목록 이벤트를 대기할 수 있습니다.

UMS 스케줄러 진입점 함수

애플리케이션의 스케줄러 진입점 함수는 UmsSchedulerProc 함수로 구현됩니다. 시스템은 다음 시간에 애플리케이션의 스케줄러 진입점 함수를 호출합니다.

  • UMS가 아닌 스레드가 EnterUmsSchedulingMode를 호출하여 UMS 스케줄러 스레드로 변환되는 경우
  • UMS 작업자 스레드가 UmsThreadYield를 호출하는 경우
  • 시스템 호출 또는 페이지 오류와 같은 시스템 서비스에서 UMS 작업자 스레드가 차단되는 경우

UmsSchedulerProc 함수의 Reason 매개 변수는 진입점 함수가 호출된 이유를 지정합니다. 새 UMS 스케줄러 스레드가 만들어졌기 때문에 진입점 함수가 호출된 경우 SchedulerParam 매개 변수는 EnterUmsSchedulingMode의 호출자가 지정한 데이터를 포함합니다. UMS 작업자 스레드가 생성되어 진입점 함수가 호출된 경우 SchedulerParam 매개 변수에는 UmsThreadYield 호출자가 지정한 데이터가 포함됩니다. 커널에서 UMS 작업자 스레드가 차단되어 진입점 함수가 호출된 경우 SchedulerParam 매개 변수는 NULL입니다.

스케줄러 진입점 함수는 지정된 스레드에 대한 적절한 다음 작업을 결정합니다. 예를 들어 작업자 스레드가 차단된 경우 스케줄러 진입점 함수는 사용 가능한 다음 준비 UMS 작업자 스레드를 실행할 수 있습니다.

스케줄러 진입점 함수가 호출되면 애플리케이션의 스케줄러는 DequeueUmsCompletionListItems 함수를 호출하여 연결된 완료 목록의 모든 항목을 검색하려고 시도해야 합니다. 이 함수는 커널에서 처리를 완료하고 사용자 모드에서 실행할 준비가 된 UMS 스레드 컨텍스트 목록을 검색합니다. 애플리케이션에서 예측할 수 없는 동작이 발생할 수 있으므로 애플리케이션의 스케줄러는 이 목록에서 직접 UMS 스레드를 실행해서는 안 됩니다. 대신 스케줄러는 각 컨텍스트에 대해 GetNextUmsListItem 함수를 한 번 호출하여 모든 UMS 스레드 컨텍스트를 검색하고, 스케줄러의 준비 스레드 큐에 UMS 스레드 컨텍스트를 삽입한 다음, 준비된 스레드 큐에서만 UMS 스레드를 실행해야 합니다.

스케줄러가 여러 이벤트를 기다릴 필요가 없는 경우 함수가 반환하기 전에 완료 목록 이벤트를 대기하도록 0이 아닌 시간 제한 매개 변수를 사용하여 DequeueUmsCompletionListItems 를 호출해야 합니다. 스케줄러가 여러 완료 목록 이벤트를 기다려야 하는 경우 완료 목록이 비어 있더라도 함수가 즉시 반환되도록 시간 제한 매개 변수가 0인 DequeueUmsCompletionListItems 를 호출해야 합니다. 이 경우 스케줄러는 예를 들어 WaitForMultipleObjects를 사용하여 완료 목록 이벤트를 명시적으로 기다릴 수 있습니다.

UMS 스레드 실행

새로 만든 UMS 작업자 스레드는 지정된 완료 목록에 큐에 대기되고 애플리케이션의 UMS 스케줄러가 실행하도록 선택할 때까지 실행을 시작하지 않습니다. 이는 호출자가 일시 중단된 스레드를 명시적으로 생성하지 않는 한 시스템 스케줄러가 자동으로 실행되도록 예약하는 비 UMS 스레드와 다릅니다.

스케줄러는 작업자 스레드의 UMS 컨텍스트를 사용하여 ExecuteUmsThread 를 호출하여 작업자 스레드를 실행합니다. UMS 작업자 스레드는 UmsThreadYield 함수, 블록 또는 종료를 호출하여 생성될 때까지 실행됩니다.

UMS 모범 사례

UMS를 구현하는 애플리케이션은 다음 모범 사례를 따라야 합니다.

  • UMS 스레드 컨텍스트에 대한 기본 구조는 시스템에서 관리되며 직접 수정해서는 안 됩니다. 대신 QueryUmsThreadInformationSetUmsThreadInformation 을 사용하여 UMS 작업자 스레드에 대한 정보를 검색하고 설정합니다.
  • 교착 상태를 방지하기 위해 UMS 스케줄러 스레드는 UMS 작업자 스레드와 잠금을 공유해서는 안 됩니다. 여기에는 애플리케이션에서 만든 잠금과 힙에서 할당 또는 DLL 로드와 같은 작업에 의해 간접적으로 획득되는 시스템 잠금이 모두 포함됩니다. 예를 들어 스케줄러가 DLL을 로드하는 UMS 작업자 스레드를 실행한다고 가정합니다. 작업자 스레드는 로더 잠금을 획득하고 차단합니다. 시스템은 스케줄러 진입점 함수를 호출한 다음 DLL을 로드합니다. 이로 인해 로더 잠금이 이미 유지되고 첫 번째 스레드가 차단 해제될 때까지 해제할 수 없으므로 교착 상태가 발생합니다. 이 문제를 방지하려면 UMS 작업자 스레드와 잠금을 공유할 수 있는 작업을 전용 UMS 작업자 스레드 또는 비 UMS 스레드에 위임합니다.
  • UMS는 대부분의 처리가 사용자 모드에서 수행되는 경우 가장 효율적입니다. 가능하면 UMS 작업자 스레드에서 시스템 호출을 하지 마세요.
  • UMS 작업자 스레드는 시스템 스케줄러가 사용되고 있다고 가정해서는 안 됩니다. 이 가정은 미묘한 영향을 미칠 수 있습니다. 예를 들어 알 수 없는 코드의 스레드가 스레드 우선 순위 또는 선호도를 설정하는 경우 UMS 스케줄러는 여전히 이를 재정의할 수 있습니다. 시스템 스케줄러가 사용 중이라고 가정하는 코드는 예상대로 작동하지 않을 수 있으며 UMS 스레드에서 호출할 때 중단될 수 있습니다.
  • 시스템에서 UMS 작업자 스레드의 스레드 컨텍스트를 잠가야 할 수 있습니다. 예를 들어 커널 모드 APC(비동기 프로시저 호출)는 UMS 스레드의 컨텍스트를 변경할 수 있으므로 스레드 컨텍스트를 잠가야 합니다. 스케줄러가 잠겨 있는 동안 UMS 스레드 컨텍스트를 실행하려고 하면 호출이 실패합니다. 이 동작은 의도적으로 수행되며 스케줄러는 UMS 스레드 컨텍스트에 대한 액세스를 다시 시도하도록 설계되어야 합니다.