Ch 14. System Thread
Contents 시스템 쓰레드의 정의와 사용 쓰레드 동기화 디스패쳐 객체 사용하기 예제코드 : 쓰레드 기반의 드라이버 Summary Ch 14. System Thread
1. 시스템 쓰레드의 정의와 사용
Thread 란? 실행의 단위 독립적인 프로그램 카운터와 개별적인 CPU레지스터의 집합을 가지는 하드웨어 컨텍스트를 유지 사용자 모드 Thread와 커널 모드 Thread를 가짐 시스템 Thread는 커널 모드에서만 접근 가능 => 사용자모드 context가 없고 사용자주소공간에 접근불가 Ch 14. System Thread
Thread를 사용해야 하는 경우 디바이스의 동작이 느리고 자주 접근 되지 않는 경우 디바이스가 상태변화를 하기 위해서 너무 오랜 시간이 소요되고 드라이버 상태 변화가 일어나기를 기다려야 하는 경우 디바이스가 하나의 작업을 마치기 위해서 여러 번의 상태변화를 해야 하는 경우 디바이스가 상태 변화를 알리기 위한 인터럽트를 발생시키지 않고 드라이버가 지정된 시간 동안 디바이스를 정기적으로 폴링 해야 하는 경우 Ch 14. System Thread
System Thread의 생성 Function Prototype for an PsCreateSystemThread Routine NTSTATUS PsCreateSystemThread IRQL == PASSIVE_LEVEL Parameter Description OUT PHANDLE ThreadHandle 새로운 쓰레드에 대한 핸들 IN ULONG DesiredAccess 드리이버가 만든 쓰레드일 경우 0 IN POBJECT_ATTRIBUTES Attrib 드라이버가 생성했으면 NULL IN HANDLE ProcessHandle OUT PCLIENT_ID ClientId IN PKSTART_ROUTINE StartAddr 쓰레드의 진입지점 IN PVOID Context 쓰레드 루틴에 전달할 Context Return value STATUS_SUCCESS : 생성시 STATUS_XXX Ch 14. System Thread
System Thread의 소멸 시스템 쓰레드는 드라이버가 메모리로 부터 없어질 때 반드시 제거해야 함 강제적으로 쓰레드를 소멸시킬 수 없음 ( Event 객체를 활용) Function Prototype for an PsTerminateSystemThread Routine NTSTATUS PsTerminateSystemThread IRQL == PASSIVE_LEVEL Parameter Description IN NTSTATUS ExitStatus 종료될때의 상태값 Return value STATUS_SUCCESS : 제거시 Ch 14. System Thread
Thread 우선 순위 시스템 쓰레드는 실시간 범위 중에서 낮은 값( LOW_REALTIME_PRIORITY)를 가져야 함 실시간 쓰레드 할당된 TimeOut값이 없으므로 자발적으로 block 거나 더 높은 우선순위의 쓰레드에 의해 선점될 경우만 Switching이 일어남 드라이버 는 라운드-로빈 스케줄링 방식에 의존하지 않음 Ch 14. System Thread
System Worker Threads 짧은 작업을 수행하기 위해서 콜백 기법을 통해 사용 WORK_QUEUE_ITEM 구조체를 메모리에 할당 드라이버에있는 콜백 함수를 WORK_QUEUE_ITEM과 연결 ( ExInitializeWorkItem 루틴) 시스템 작업 대기열에 WORK_QUEUE_ITEM을 요청하는 블록을 삽입 실행시간이 많이 걸리는 작업은 다른 드라이버의 작업 실행을 지연 시킬 수 있으므로 독립적인 드라이버 쓰레드를 사용해야 함 Ch 14. System Thread
2. 쓰레드 동기화
시간 동기화 지정한 시간이 지날때까지 쓰레드의 실행을 멈추는것 (Timer 객체 or KeDelayExecutionThread 함수) Function Prototype for an KeDelayExecutionThread Routine NTSTATUS KeDelayExecutionThread IRQL == PASSIVE_LEVEL Parameter Description IN KPROCESSOR_MODE WaitMode 드리이버는 Kernel Mode IN BOOLEAN bAlertable 드라이버는 false IN PLARGE_INTEGER Interval 절대나 상대적인 시간 Return value STATUS_SUCCESS : 대기완료 Ch 14. System Thread
일반적인 동기화 Dispatcher 객체를 기다림 ( signaling되지 않은 dispatcher객체 ) Funtion Prototype for an KeWaitForSingleObject Routine NTSTATUS KeWaitForSingleObject Parameter Description IN PVOID Object Pointer to Driver Object IN KWAIT_REASON Reason 드라이버는 Executive IN KPROCESSOR_MODE WaitMode 드라이버는 Kernel Mode IN BOOLEAN Alertable 드라이버는 False IN PLARGE_INTERGER Timeout 절대 or 상대적인 Timeout값 Return value STATUS_SUCCESS STATUS_ALERTED STATUS_TIMEOUT Ch 14. System Thread
일반적인 동기화 (cont.) Funtion Prototype for an KeWaitForMultipleObjects Routine NTSTATUS KeWaitForMultipleObjects Parameter Description IN ULONG Count 기다릴 객체의 개수 IN PVOID Object[] 디스패쳐 객체에 대한 포인터 배열 IN KWAIT_REASON Reason 드라이버는 Executive IN KPROCESSOR_MODE WaitMode 드라이버는 Kernel Mode IN BOOLEAN Alertable 드라이버는 False IN PLARGE_INTERGER Timeout 절대 or 상대적인 Timeout값 IN PKWAIT_BLOCK WaitBlocks[] 이 동작을 위한 대기 블록의 배열 Return value STATUS_SUCCESS STATUS_ALERTED STATUS_TIMEOUT Ch 14. System Thread
일반적인 동기화 (cont.) 쓰레드는 Timeout값을 지정하여서 다시 깨울 수 있음 Multiple일 경우 쓰레드가 기다릴 수 있는 객체 수 제한 ( MAXIMUM_WAIT_OBJECTS ) THREAD_WAIT_OBJECTS 객체 수만큼 기다릴 수 있음 PASSIVE_LEVEL 이나 DISPATCH_LEVEL에서 호출 DISPATCH_LEVEL에서는 Timeout값이 0 => 시그널링 된 객체를 정기적으로 확인하기 위한 기법 Ch 14. System Thread
4. Dispatcher 객체 사용하기
Dispatcher Object 메모리에 지속적으로 상주( Nonpaged space ) Device Extension or Controller Extension에 공간 할당 사용 전 초기화 ( KeInitializeXxx ) Ch 14. System Thread
Event Object Signaling or Nonsignaling상태로 반드시 설정 되어야 하는 객체 (KEVENT로 할당) Signaling으로 설정해서 다를 쓰레드의 발생을 부추길 수 있다 Thread B Thread A 설정 EVENT Thread C Thread D 알림 이벤트 -> Signaling되기를 기다리는 모든 쓰레드를 깨움 동기화 이벤트 -> 하나의 쓰레드만 깨움 Ch 14. System Thread
Event Object (cont.) 이벤트 객체를 조작하는 함수들 사용 시기 호출 함수 IRQL 이벤트 생성 KeInitializeEvent PASSIVE_LEVEL 이름을 갖는 이벤트 생성 IoCreateSynchronizationEvent IoCreatNotificationEvent 이벤트 상태 수정 KeSetEvent KeClearEvent KeResetEvent <=DISPATCH_LEVEL 타이머를 대기 KeWaitForSingleObject KeWaitForMultipleObjects 이벤트 상태 질의 KeReadStateEvent Ch 14. System Thread
드라이버간 이벤트 공유 이벤트 공유를 위한 포인터의 전달 과 메모리 상주 문제 IoCreateSynchronizationEvent 와 IoCreatNotificationEvent 함수를 이용하여 이름을 갖는 Event를 생성 동일한 Event name을 가지면 동일한 Event객체의 포인터를 얻을 수 있음 CreateEvent 처럼 동작함 Ch 14. System Thread
드라이버간 이벤트 공유 (cont.) IoCreateXxxEvent KEVENT 객체의 메모리 할당은 시스템에서 함 할당된 메모리의 핸들이 return됨 -> 핸들에서 포인터를 얻어내야함 ObReferenceObjectByHandle 함수를 호출 -> 객체 포인터를 얻고, 참조 카운트를 증가 핸들이 필요하지 않게 되면 핸들을 해제( ZwClose 함수) 이벤트 객체가 필요하지 않게 되면 ObDereferenceObject 함수를 호출하여 참조 카운트를 감소, 이벤트 객체를 삭제 Ch 14. System Thread
Mutex Object (Mutual Exclusion) 오직 하나의 쓰레드에서만 소유될 수 있음 소유하면 Nonsignaling -> 사용가능 하면 Signaling 공유 자원에 대한 상호 배타적인 접근을 조절 KMUTEX 객체는 Nonpaged space에 할당 counter 1 소유 요청 counter 1 소유 소유 요청 사용가능 소유 요청 소유 사용가능 Thread B Thread A 해제 Blocked Mutex Thread C 종료 or Block counter 1 Blocked counter 1 Thread D 소유 요청 Blocked Ch 14. System Thread
Mutex Object (cont.) 뮤텍스를 초기화하면 초기 상태는 signaling Mutex 객체를 조작하는 함수들 사용 시기 호출 함수 IRQL 뮤텍스 생성 KeInitializeMutex PASSIVE_LEVEL 뮤텍스 소유권 요구 KeWaitForSingleObject KeWaitForMultipleObjects 뮤텍스 소유권 포기 KeReleaseMutex 뮤텍스 상태 질의 KeReadStateMutex <=DISPATCH_LEVEL 뮤텍스를 모두 해제를 하고 드라이버는 커널모드에서 사용자 모드로 이동 Ch 14. System Thread
Semaphore Object 카운트를 관리하는 디스패쳐 객체 Count가 0보다 크면 Signaling 0이면 Nonsignaling KSEMAPHORE객체를 메모리에 할당 대기 수행 대기 수행 대기 수행 Thread B Thread A 해제 Blocked Semaphore Thread C 종료 or Block count 2 1 Blocked 대기 Thread D Blocked Ch 14. System Thread
Semaphore Object (cont.) 사용 시기 호출 함수 IRQL 세마포어 생성 KeInitializeSemaphore PASSIVE_LEVEL 세마포어 감소 KeWaitForSingleObject KeWaitForMultipleObjects 세마포어 증가 KeReleaseSemaphore <=DISPATCH_LEVEL 세마포어 상태 질의 KeReadStateSemaphore Any Ch 14. System Thread
Timer Object 타임 아웃 값을 가지는 디스패쳐 객체 타이머가 시작된 후 타임아웃이 될때까지 Nonsignaling KTIMER객체를 메모리에 할당 Thread A Clock SetTimer Wait Blocked Timer Continue Ch 14. System Thread
Timer Object (cont.) Timer 객체를 조작하는 함수들 사용 시기 호출 함수 IRQL 타이머 생성 KeInitializeTimerEx PASSIVE_LEVEL 타이머를 한번 시작 KeSetTimer <= DISPATCH_LEVEL 타이머를 반복 시작 KeSetTimerEx 타이머 정지 KeCancelTimer 타이머를 기다림 KeWaitForSingleObject KeWaitForMultipleObjects 세마포어 상태 질의 KeReadTimerState Ch 14. System Thread
Thread Object 시스템 쓰레드도 디스패쳐 객체 쓰레드가 소멸되면 Signaling 드라이버가 종료 될 때 쓰레드 객체를 기다림 ObReferenceObjectByHandle 함수를 호출 -> 객체 포인터를 얻고, 포인터 참조 카운트를 증가 핸들이 필요하지 않게 되면 핸들을 해제( ZwClose 함수) -> 객체의 핸들 참조 카운트를 감소 이벤트 객체가 필요하지 않게 되면 ObDereferenceObject 함수를 호출하여 포인터 참조 카운트를 감소, 쓰레드 객체를 삭제 Ch 14. System Thread
뮤텍스의 변형 (1) 고속 뮤텍스 ( Fast Mutex ) 반복적인 소유권 요청을 불가 고속 뮤텍스를 조작하는 함수들 사용 시기 호출 함수 IRQL 뮤텍스 생성 ExInitializeFastMutex <= DISPATCH_LEVEL 고속 뮤택스 소유권 요구 ExAcquireFastMutex 고속 뮤텍스 소유권 포기 ExReleaseFastMutex Ch 14. System Thread
뮤텍스의 변형 (2) 실행부 자원 ( Executive resources ) 하나의 쓰레드에 의해 독점적으로 소유 가능 여러 쓰레드가 읽기 접근을 공유 가능 실행부 자원을 조작하는 함수들 사용시기 호출 함수 IRQL 생성 ExInitializeResourceLite PASSIVE_LEVEL 획득 ExAcquireResourceExclusiveLite ExAcquireResourceSharedLite ExTryToAcquireResourceExclusiveLite ExConvertExclusiveToSharedLite <= DISPATCH_LEVEL 해제 ExReleaseResourceforThreadLite 질의 ExIsResourceAcquiredSharedLite ExIsResourceAcquiredExclusiveLite 삭제 ExDeleteResourceLite Ch 14. System Thread
Deadlock 뮤텍스나 세마포어를 사용하여 발생 타임아웃 인자를 사용하여 기다리는 시간을 제한 동일한 순서로 자원을 요청함 할당 대기 Resource X Thread A Thread B 대기 할당 Resource Y Acquire X Acquire Y … Release X Release Y Acquire Y Acquire X … Release Y Release X Acquire X Acquire Y … Release Y Release X Ch 14. System Thread
4. 예제 코드 : 쓰레드 기반의 드라이버
PsCreateSystemThread 샘플 드라이버의 구조 PsCreateSystemThread I/O request KeReleaseSemaphore KeWaitFor Singleobject Semaphore Thread Dispatch 루틴 Count 1 Wait Wake up IRP IRP ISR 루틴 Queue Wait Wake up DPC 루틴 Event KeWaitFor Singleobject Nonsignaling Signaling Ch 14. System Thread
샘플 드라이버의 객체 및 함수 DEVICE_EXTENSION AddDevice routine 드라이버에서 정의된 모든 데이터 구조체를 포함 드라이버가 시스템 쓰레드와 작업 대기열을 관리하기 위해 필요한 작업자 쓰레드 객체 어댑터 객체를 얻었을 때 알릴 이벤트 객체 IRP 큐를 관리하는 세미포어와 스핀락 객체 AddDevice routine I/O 요청을 처리 하기 위해 사용 되는 쓰레드 객체, 작업 대기열 객체 및 동기화 객체를 초기화 드라이브를 load할때 한번 호출됨 Ch 14. System Thread
샘플 드라이버의 객체 및 함수 DispatchReadWrite routine 디바이스에 대해 읽거나 쓰고자 하는 사용자 요청에 응답 전송할 데이터가 있는 지 확인 IRP를 종료되지 않았다는 표시를 한 뒤 작업 대기열에 삽입 세마포어가 쓰레드를 동작 할 수 있게 count를 증가 (KeReleaseSemaphore) Ch 14. System Thread
Thread.cpp 모듈 주 쓰레드 함수와 쓰레드를 관리 하기 위해 필요한 루틴 포함 WorkerThreadMaine 함수 IRP를 처리하는 엔진 드라이버의 Context를 받아서 DEVICE_EXTENSION의 작업 대기열에서 I/O 요청을 가지고 오고 데이터 전송 작업을 수행 자신 ( 쓰레드 ) 는 사용자 보다 높은 우선순위로 설정 RemoveDevice함수에서 멈추라는 지시가 있을 때까지 리턴이 없이 무한 루프로 동작 IRP처리를 위해서 semaphore가 유효할때까지 대기 ( KeWaitForSingleObject ) 실제 IRP처리를 실시(PerformDataTransfer)하고, IRP를 해지한뒤, 다른 IRP요청을 처리하기 위해 루프를 돔 Ch 14. System Thread
Thread.cpp 모듈 KillThread 함수 특정 디바이스 객체와 관련된 쓰레드를 중지 하라는 통보역활 목적 쓰레드가 정지 할때 까지 멈추어서 기다림 정지 플래그를 설정 쓰레드가 살아 있는 지 확인( KeReleaseSemaphore ) 쓰레드가 끝나기를 기다림 ( KeWaitForSingleObject ) Ch 14. System Thread
Thread.c 모듈 I/O작업을 수행하는 루틴을 포함 쓰레드의 데이터 전송 루틴이 시작할 수 있도록 이벤트 객체를 설정 PerformDataTransfer 함수 DMAC의 역할을 수행함 한번에 데이터를 다 처리 할 수 없다면 데이터 전송을 나눔 작업이 완료 될때까지 계속 수행 I/O 방향을 설정 , 예약 값을 설정, CPU캐쉬를 플러쉬 데이터 분할 전송의 크기를 계산 해서 분할 전송 처리( PerformSynchromousTransfer ) 처리후 예약 정보를 수정 분할 전송이 끝날때까지 I/O동작을 수행 처리량을 조절하면서 수행 후 IRP를 반환 Ch 14. System Thread
Thread.c 모듈 AcquirAdapterObject 와 AdapterControl AcquireAdapterObject 어댑터 객체의 소유권을 얻기 위한 동기화 기법을 쓰레드에 제공 AcquireAdapterObject 시스템 쓰레드의 컨텍스트에서 실행 되기 때문에 일정한 시간 간격을 멈추고 기다릴 수 있게 구현 어댑터 객체를 요청하기 위해 DISPATCH_LEVEL에서 수행 이벤트 객체를 세팅하는 AdapterControl 루틴을 위해 정지하고 기다림, 어댑터 객체를 다른 디바이스에서 사용 못하도록 어댑터에 대한 이벤트 객체를 기다림 ( KeWaitForSingleObject ) AdapterControl 맵핑 레지스터에 대한 핸들을 저장하고 데이터 전송을 설정 Ch 14. System Thread
Thread.c 모듈 PerformSynchronousTransfer 함수 한번의 데이터 전송 작업을 수행 전송이 끝날때까지 리턴 되지 않음 디바이스 인터럽트 발생 여부를 확인 하기 위해 이벤트를 사용 DMA컨트롤러를 설정하고 디바이스를 구동 I/O 처리 완료 인터럽트에 대한 이벤트를 기다림( KeWaitForSingleObject ) 어댑터 객체 캐쉬의 데이터를 플러쉬, 디바이스의 에러 체크 Ch 14. System Thread
Thread.c 모듈 DpcForIsr 디바이스가 인터럽트를 발생 시키면 인터럽트 처리 루틴은 하드웨어 상태값을 저장하고 DPC를 요청 이벤트 객체를 시그널링 상태로 변경 PerforSynchronousTransfer 함수가 실행됨 Ch 14. System Thread
Summary
Summary 다른 작업 들과 병렬로 특정 작업을 수행 드라이버 내에 시스템 쓰레드를 사용 하는 방법 다중 쓰레드 기법을 적절하게 활용 할 것 Ch 14. System Thread