본문 바로가기

My Study/Programming&Theory

WorkItem Routine

 저번 문서에서는 Kernel에서 System Thread 사용하는 방법에 대해서 알아보았습니다.
이번에는 System Thread와 조금 다른 WorkItem 루틴에 대해서 알아보겠습니다.

드라이버 PsCreateSystemThread함수를 사용해 System Thread를 만들면 해당 스레드는 드라이버 것이므로 
그 안에서 무한 반복문을 돌려서 써버린다던지 해도 상관이 없습니다.

하지만 WorkItem 루틴은 동작 방식이 조금 다릅니다.
WorkItem 루틴은 IO관리자가 준비한 몇 개의 System Thread들 중 아무 스레드에서 호출이 되는 루틴입니다.
그렇기 때문에 한 WorkItem 루틴이 종료를 하지 않고 계속 반복문 같은 것을 돌려서 CPU를 사용하게 되면 다른 드라이버들이 WorkItem 루틴을 사용할 기회를 잃게 되는 것입니다. 그러므로 WorkItem 루틴은 할일만 딱 마치고 바로 종료를 해주는게 좋습니다.

일단 이 루틴을 왜 쓰는지 이유에 대해서는 나중에 보기로 하고 먼저 함수 정보들부터 봐보겠습니다.
WorkItem 루틴의 형태입니다.
IO_WORKITEM_ROUTINE WorkItem;

VOID WorkItem(
  __in      PDEVICE_OBJECT DeviceObject,
  __in_opt  PVOID Context
)
{ ... }
그리고 위 루틴을 호출하기 위해선 IoAllocateWorkItemIoQueueWorkItem 두 가지 함수를 사용하게 됩니다.
PIO_WORKITEM IoAllocateWorkItem(
  __in  PDEVICE_OBJECT DeviceObject
);
WorkItem을 할당하는 함수입니다.
IRQL <= DISPATCH_LEVEL

첫번째 인자로는 디바이스 오브젝트 포인터가 들어가야 합니다.
디바이스 오브젝트 포인터는 IoCreateDevice 함수를 사용해 얻어주면 됩니다.
VOID IoQueueWorkItem(
  __in      PIO_WORKITEM IoWorkItem,
  __in      PIO_WORKITEM_ROUTINE WorkerRoutine,
  __in      WORK_QUEUE_TYPE QueueType,
  __in_opt  PVOID Context
);
WorkItem루틴을 호출하는 함수입니다.
IRQL <= DISPATCH_LEVEL

첫번째 인자는 위 함수에서 얻어온 IO_WORKITEM포인터를 넣으면 되고 
두번째 인자는 WorkItem 루틴 포인터
세번째 인자는 해당 WorkItem 우선순위 입니다.
네번째는 나중에 파라미터로 사용 된답니다..

그러면 이 루틴을 언제 쓰면 좋을지에 대해서 알아보겠습니다.
잘 보시면 PsCreateSystemThread 함수와 다른점이 있는데 
PsCreateSystemThread의 IRQL은 PASSIVE_LEVEL 입니다.

하지만 IoAllocateWorkItemIoQueueWorkItem의 IRQL을 잘 보면 DISPATCH_LEVEL에서도 호출이 가능합니다.
훨씬 사용하는 시점이 System Thread보다 자유롭다는 것입니다..!!
저는 이 루틴을 이렇게 사용했었습니다.

보통 인터럽트를 처리할 때 빠른 속도를 위해 찌꺼기 코드??!! 같은 경우 DPC 루틴을 만들어서 DPC큐에 넣어둠으로서 처리를 할 수 있습니다. 이 때 DPC루틴이 실행되는 IRQL은 DISPATCH_LEVEL입니다. 즉, 안에서 스케쥴링이 일어날 만한 코드를 작성하면 안된다는 것입니다. 잘못 했다간 뻑이 나는거죠.

저는 DPC루틴에서 파일을 생성하기 위해 ZwCreateFile 함수를 호출을 하고 싶었습니다.
하지만 ZwCreateFile 함수의 IRQL은 PASSIVE_LEVEL입니다. DPC 루틴에서 호출했다간 파란화면 뿅뿅뿅 뜨겠지요..
그렇기 때문에 DPC루틴에서 호출가능한 WorkItem 루틴!!을 활용하는 것입니다.
WorkItem루틴은 System Thread에서 돌아가기 때문에 PASSIVE_LEVEL을 보장 받을 수 있습니다.

해결이 된거같군요. 아무튼 저는 이러한 이유로 WorkItem루틴을 사용했었습니다.
아마 다른 방법으로도 많이 사용이 되겠지요.

System Thread를 생성해서 쓸건지 WorkItem루틴을 쓸건지는 개발자의 몫인거 같습니다. 상황에 따라 잘 써야겠지요 ^^;

추가적으로 msdn에서 System Worker Threads에 대한 내용을 찾아보았습니다. 
그 중에 마지막 단락을 보니 어느정도 답이 나와있었습니다.


대충 읽어보면 system worker threads는 리소스가 제한되어 있어서 worktiem 또는 workitemex루틴은 짧은 시간 동안만 동작을 할 수 있다고 나와있습니다. 만약에 하나의 workitem 루틴이 오랜시간 동안 실행을 해버린다거나 오랫동안 일을 끝마치지 않고 기다린다면 시스템은 데드록에 걸릴 수 있다고 나와있네요. 고로~! 만약에 드라이버에서 오랫동안 
delayed processing을 사용할 거면 그냥 workitem루틴 대신에 PsCreateSystemThread를 사용해 그냥 하나의 스레드를 생성시켜서 거기서 작업을 하라는 것입니다. 어디서 workitem루틴의 적정 사용시간이 나와있는 문서를 본거 같은데 어디서 본건진 잘 모르겠고 적정 시간은 50 마이크로초 랍니다. 어디 나와있는지 아시는분은 알려주세요 :-)