구리의 창고

커널모드에서 타이머 사용하기 본문

Window Driver

커널모드에서 타이머 사용하기

구리z 2010. 8. 11. 09:51

1. 개요

드라이버에서 개발을 하다보면 주기적으로 호출되는 함수가 필요할 경우가 있다. 예를들면 서버와 클라이언트간의 연결상태가 유효한지를 1분단위로 확인할 수도 있고 드라이버 내부적으로 행이 발생한 곳이 있는지의 여부를 일정한 시간 간격으로 확인할 수도 있다. 실제로 NDIS Miniport 드라이버는 CheckForHang 이라는 함수가 그런 역할을 한다. 이처럼 주기적인 함수호출이 요구될 때 이를 구현하는 방법에 대해서 기본적인 내용으로 예제를 사용하여 설명하겠다.

2. 초기화

우선 DDK에서 제공되는 함수는 다음과 같다. 아래 나열한 함수들을 타이머 연산을 구현하기 위한 기본 함수들로 이외에 함수도 DDK에서는 제공하고 있다.

VOID 
  KeInitializeTimer(  // 타이머객체를 초기화한다.
    IN PKTIMER  Timer
    );
VOID 
  KeInitializeDpc(   // DPC 객체를 초기화한다.
    IN PRKDPC  Dpc,
    IN PKDEFERRED_ROUTINE  DeferredRoutine,
    IN PVOID  DeferredContext
    );
BOOLEAN 
  KeSetTimer(     // 타이머를 시작한다.
    IN PKTIMER  Timer,
    IN LARGE_INTEGER  DueTime,
    IN PKDPC  Dpc  OPTIONAL
    );
BOOLEAN 
  KeCancelTimer(  // 타이머를 취소한다.
    IN PKTIMER  Timer
    );

위의 네가지 함수를 사용하면 기본적인 타이머 연산을 구현할 수가 있는데 이에 대한 초기화시에는 KeInitializeTimer, KeInitializeDpc 함수를 호출해주면된다. 다음은 예제이다.

// 헤더파일에 선언
KTIMER Timer;
KDPC Dpc;

// 초기화연산시 호출
KeInitializeTimer(&Timer);

KeInitializeDpc(
	&Dpc,
	(PKDEFERRED_ROUTINE)DpcRoutine,
	pContext
	);
	
BOOLEAN 
  KeSetTimer(
    IN PKTIMER  Timer,
    IN LARGE_INTEGER  DueTime,
    IN PKDPC  Dpc  OPTIONAL
    );

KeInitializeDpc 함수의 두번째 인자(DpcRoutine)는 나중에 KeSetTimer 함수가 호출되면 주기적으로 호출되게되는 함수의 포인터이다. 그리고 세번째 인자(pContext)는 DpcRoutine 함수의 두번째 인자로 입력되는 값으로 일반적으로 많이 사용하는 값은 pDeviceObject 혹은 pDeviceExtension 이다.

3. 타이머 설정

이제 초기화가 되었으니 타이머를 설정하여 시작할 수 있다. 타이머를 설정하고 시작하게하는 함수는 KeSetTimer 함수이다. KeSetTimer 함수의 예는 다음과 같다.

KeSetTimer(&Timer, // KeInitializeTimer 함수에서 설정

TimeInterval,

&Dpc); // KeInitializeDpc 함수에서 설정

위와 같이 해주면 KeSetTimer 함수가 호출된 후에 TimeInterval 이 지나면 초기화시 KeInitializeDpc 함수의 두번째 인자로 등록해준 DpcRoutine 에 해당하는 함수가 호출된다. 여기서 주의해야할 점은 일반적으로 생각할 때 타이머 함수는 주기적으로 호출될 것 같은데 그렇지가 않다. DpcRoutine 은 한번만 호출된다. 이를 해결하는 한가지 방법이 있다. ‘항목4.’ 를 보기 바란다. 또한가지 주의해야할 것이 있다. TimeInterval 에 대한 것이다.

1초(100-nanosecond intervals)를 설정한다고 하면 보통 1000*1000*10 의 값으로 대입할 것이다. 하지만 이렇게 하면 1초 주기로 발생하지 않고 무한루프를 돌듯이 계속적으로 발생한다. 해결방법은 nagative 값으로 설정해주는 것이다. 이것의 의미는 현재 시스템 시간을 기준으로하여 1초후에 DpcRoutine 이 호출된다는 것이다. 다음은 DDK 도움말에서 인용한 것이다.

BOOLEAN 
  KeSetTimer(
    IN PKTIMER  Timer,
    IN LARGE_INTEGER  DueTime,
    IN PKDPC  Dpc  OPTIONAL
    );

DueTime

Specifies the absolute or relative time at which the timer is to expire. If the value of the DueTime parameter is negative, the expiration time is relative to the current system time. Otherwise, the expiration time is absolute. The expiration time is expressed in system time units (100-nanosecond intervals). Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.

4. DpcRoutine 처리

우선 DpcRoutine 은 다음과 같은 형식으로 선언되어야 한다.

VOID
(*PKDEFERRED_ROUTINE)(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    );

그러면 이제 DpcRoutine 이 호출되고 개발자가 하고자하는 연산을 함수내부에서 구현해주면 되는 것이다.

‘항목3.’에서 언급한 문제(DpcRoutine 이 한번만 호출되는 문제)를 해결하는 한가지 방법은 DpcRoutine 이 리턴되기 전에 다시 KeSetTimer 함수를 호출하면 주기적으로 발생하는 타이머를 구현할 수 있다.

 

 

출처 : HanTechSnS <커널모드 드라이버에서 타이머 사용하기>

Comments