<1> 커널의 시간 개념 


커널에서는 시스템 타이머라고 하는 장치를 이용하여 시간을 측정한다. 시스템 타이머는 전자 시계 등의 전기적 신호를 이용해 일정 시간마다 '울린다'. 시스템 타이머가 울리게 되면 즉시 시스템 타이머 인터럽트가 발동하며 이를 이용해 시간을 측정할 수 있다. 시스템 타이머는 진동수에 따라 울리게 되며 이는 커널이 이미 알고 있는 상수이다. 두 시스템 타이머 인터럽트 사이를 tick이라고 부르며 이 값은 당연히 진동수 분의 1초가 된다.


<2> 진동수


시스템 타이머의 진동수는 정적 매크로인 HZ에 의해 정해진다. 현재 리눅스 커널에서는 100Hz로 설정되어 있다. 진동수를 크게 하는 데에는 몇가지 장점이 존재하는데 다음과 같다.


  • 더 정교한 시간 해상도 및 정확도
  • 타임아웃 값을 선택적으로 사용할 수 있는 poll()이나 select()같은 시스템 콜을 더 정밀하게 실행할 수 있다.
  • 자원 사용률, 시스템 가동 시간 등의 측정값을 더 세밀하게 측정할 수 있다.
  • 프로세스 스케줄링이 더 정확히 처리된다.


단점은 명확하다 : 시스템 타이머 인터럽트가 더 자주 발생하게 되므로 이에 따른 부담이 더욱 증가하게 된다. 현대적인 시스템에서는 대략 1000Hz정도의 진동수는 시스템에 크게 부담되지 않는다고 여겨진다. 


<3> jiffies과 xtime


jiffies는 unsigned long형태의 전역변수로서 시스템 시작 후의 틱수가 저장된다. 

xtime은 1970년 1월 1일 이후의 초를 저장하는 구조체이다. xtime은 seqlock을 사용해서 접근해야 한다.



<4> 시스템 타이머 인터럽트 핸들러


타이머 인터럽트 핸들러는 아키텍처 종속적인 부분과 독립적인 부분으로 나눌 수 있다. 우선 아키텍처 종속적인 부분은 인터럽트 핸들러 형태로 되어있으며 다음의 역할을 처리한다.


  • 필요에 따라 시스템 타이머를 확인하고 재설정한다.
  • 갱신된 현재 시간을 주기적으로 실시간 시계에 반영한다.
  • jiffies 밑 현재 시간을 저장하는 xtime 변수에 접근하기 위한 xtime_lock을 얻는다.
  • 아키텍처 독립적인 함수 tick_periodic()을 호출한다.


아키텍처 독립적인 부분은 tick_periodic()에서 처리되며 다음과 같은 일을 한다.


  • jiffies의 값을 1 증가시킨다. 
  • 현재 실행 중인 프로세스가 소모한 시스템 시간 사용자 시간과 같은 자원 통계값을 갱신한다.
  • 설정 시간이 지난 동적 타이머를 실행한다
  • scheduler_tick()을 실행한다.
  • xtime에 저장된 현재 시간을 갱신한다.
  • 평균 로드를 갱신한다.


타이머 인터럽트 핸들러에선 프로세스의 실행시간을 갱신할때 해당 프로세스가 하나의 틱 전체를 사용한 것으로 간주한다. 즉 하나의 틱에서 여러 프로세스가 선점되며 실행되었어도, 마지막에 실행된 프로세스가 전체 틱을 사용한 것으로 간주한다는 뜻이다. 진동수가 증가하게 되면 이러한 불공평함이 다소 해소될 수 있다.



<5> 타이머


때때로, 커널에서는 일정한 시간 이후에 함수가 실행되어야 하는 경우가 있다. 이를 위해 리눅스에서는 타이머를 지원한다. 타이머에 만료시간과 함수를 지정하면, 만료 시간 이후에 타이머가 '울리게' 되며 함수가 실행되고 타이머는 종료된다. 이 때, 만료시간 이전에 타이머가 울리지 않는 것은 보장되지만, 만료 시간 직후에 지연이 존재할 수 있다. 타이머는 링크드 리스트의 형태로 저장되며, 빠른 검색을 위해 5개의 리스트로 나뉜다.



<6> 실행 지연


리눅스에서 타이머와 같은 실행 지연은 실제로 어떻게 구현될까? 몇 가지 방법들을 생각해 볼 수 있다. 우선 가장 쉽게 생각할 수 있는 것으로


1) 루프 반복

- 원하는 만큼의 진동수가 지날때까지 의미없는 루프를 반복한다.


가 있을 수 있겠다. 하지만 이는 명백하게 프로세서 시간 낭비이며 너무 비효율적으라 실제로 사용되지 않는다. 1)을 개선한 방법으로


2)resched()

- 루프를 반복하되, 무의미한 루프를 돌지 않고 현재 프로세스를 resched()함수로 리스케줄하게 함.

- 스케줄러가 호출되므로 프로세스 컨텍스트에서만 사용 가능


가장 최적화된 방법으로 다음과 같은 함수를 사용할 수도 있다.


3)schedule_timeout

- 지정한 시간까지 현재 프로세스를 휴면 상태로 전환한다.

- 휴면상태로 전환되므로 프로세스 컨텍스트에서, lock을 사용하지 않는 경우에만 사용가능하다.


위와 같은 방법이 있을 수 있다. 위의 방법들 모두 지연 시간이 진동수보다 큰 경우를 다루고 있다. 즉 위의 두 방법 모두 jiffies값을 이용해 지연 시간이 만료되었는지 확인한다. 만약 지연 시간이 1진동수보다 작다면 어떻게 지연할 수 있을까? 이를 위해 커널에서는 다음과 같은 함수들을 제공한다.


4)udelay(), ndelay(), mdealy()

- ndealy()와 mdelay()는 각각 나노초와 마이크로초 단위로 실행을 지연한다.

- udelay()는 주어진 시간을 지연하려면 얼마나 루프를 돌아야 하는지 계산해놓은 루프로 구현되어 있다.







<참고>

http://hooneyo.tistory.com/entry/%EC%8B%A4%ED%96%89%EC%A7%80%EC%97%B0

+ Recent posts