이 문서의 저작권은 GPL 라이센스를 따릅니다(This document is released under the GPL license).
Documentation/timers/timers-howto.txt
번역: 양정석(dasomoli@gmailREMOVETHIS.com)
지연시간 – 다양한 커널 딜레이(delay) / 슬립(sleep) 메카니즘의 정보
——————————————————————
이 문서는 공통적인 질문에 대해 답변하려고 합니다: “무엇이 지연 시간을
추가하는 옳은 방법(RightWay (TM)) 인가?”
이 질문은 하드웨어 지연 시간을 처리해야만 하지만, 커널의 내부
동작과는 거의 직접적으로 관련이 없을 드라이버 작성자들에 의해서
거의 제기됩니다.
딜레이 추가하기
—————
먼저, 가장 중요한, 여러분에게 물을 필요가 있는 질문은 “내 코드가
어토믹 컨텍스트(atomic context) 안에 있는가?” 입니다. 이것은 “정말로
어토믹 컨텍스트 안의 딜레이가 필요한가”라는 질문이 따라 붙어야 합니다.
만약 그렇다면…
어토믹 컨텍스트:
여러분은 *delay 계열의 함수를 사용해야만 합니다. 이들 함수는
클럭 속도의 지피(jiffie) 추정하기를 사용하고, 원하는 지연
시간을 채우기 위해서 충분한 루프 사이클을 바쁘게 대기(busy
wait)할 것입니다:
ndelay(unsigned long nsecs)
udelay(unsigned long usecs)
mdelay(unsigned long msecs)
udelay 는 일반적으로 선호되는 API 입니다; ndelay-레벨
정밀도는 많은 PC가 아닌(non-PC) 기기들에서 실제로 존재하지
않을 것입니다.
mdelay 는 udelay에 큰 인자를 주었을 때 가능한 오버 플로우
(overflow)를 세는, 돌고 도는 udelay의 매크로입니다.
일반적으로, mdelay의 사용은 권장되지 않고, 코드는 msleep를
사용할 수 있도록 리팩토링되는 것이 좋습니다.
어토믹이 아닌(NON-ATOMIC) 컨텍스트:
여러분은 *sleep[_range] 계열의 함수를 사용하는 것이 좋습니다.
여기에 몇가지 더 많은 옵션이 있습니다. 여기 있는 어떤 것들도
정확히 동작할 것이긴 하지만, “알맞은” 슬립 함수를 사용하는 것은
스케줄러와 전력 관리를 돕고, 여러분의 드라이버를 더 좋게
만들어줄 것입니다. 🙂
— 바쁜 대기(busy-wait)루프로 뒷받침:
udelay(unsigned long usecs)
— hrtimers로 뒷받침:
usleep_range(unsigned long min, unsigned long max)
— 지피 / 기존 타이머(legacy timer)로 뒷받침:
msleep(unsigned long msecs)
msleep_interruptible(unsigned long msecs)
*dealy 계열과 다르게, 이들 각각의 호출을 수행하는 바닥에 깔린
메카니즘이 다양하기 때문에 여러분이 알고 있어야 할 특성이 있습니다.
적은 USECS ( < ~10us? ) 를 위한 슬립:
* udelay를 사용하세요.
– usleep은 왜 아닌가요?
느린 시스템에서, (임베디드, 또는 아마도 스피드-스텝
PC!) usleep 을 위해서 hrtimers를 셋팅하는 오버헤드
(overhead)가 *아마* 그만큼 가치가 없을 것입니다. 앞과
같은 측정은 명확히 여러분의 재원적 사항에 달려 있습니다만,
알아둘 만 한 것은 아닙니다.
~USECS 나 작은 MSECS ( 10us – 20ms ) 를 위한 슬립:
* usleep_range를 사용하세요.
– (1ms – 20ms)를 위한 msleep은 왜 아닌가요?
원래는 여기서 설명했습니다:
http://lkml.org/lkml/2007/8/3/250
msleep(1~20) 은 호출자가 의도한 것을 하지 않을 수 있고,
종종 더 길게(1~20ms 사이의 어떤 값에 대해서도 실제
~20 ms 슬립) 슬립할 것입니다. 많은 경우에 이것은 원하는
동작이 아닙니다.
– “usleep” 은 왜 없나요 / 좋은 범위는 무엇인가요?
usleep_range 는 hrtimers의 꼭대기에서 빌드되므로, 그
웨이크업(wakeup)은 매우 정밀할 것입니다. 그래서 간단한
usleep 함수는 거의 많은 수의 원치 않는 인터럽트를 일으킵니다.
범위의 도입으로, 스케줄러는 여러분의 웨이크업을 다른 이유로
인해 일어날 수 있는, 혹은, 최악의 경우에 여러분의
상한(upper bound)을 위한 인터럽트를 점화하는 다른
웨이크업과 합치는데 자유롭게 됩니다.
여러분이 범위를 더 넓게 할 수록, 여러분이 인터럽트를
일으키지 않을 기회가 더 커집니다; 이것은 여러분의 코드
경로에서 지연 시간/성능 상의 허락할만한 상한값과
균형을 맞추어야 합니다. 여기에서 정확한 허용값은 매우 상황에
의존적입니다. 그래서 이는 호출자에게 이유가 있는 범위를
결정하도록 남겨둡니다.
더 큰 MSEC ( 10ms+ ) 를 위한 슬립
* msleep 이나 가능하면 msleep_interruptible 을 사용하세요.
– 무슨 차이인가요?
msleep 은 현재 태스크를 TASK_UNINTERRUPTIBLE 로 셋팅하는데
반해, msleep_interruptible 은 현재 태스크를 그 슬립을
스케줄링하기 전에 TASK_INTERRUPTIBLE 로 셋팅합니다.
요약하면, 차이는 슬립이 시그널에 의해 일찍 끝날 수 있는지
입니다. 일반적으로, 여러분이 인터럽트 가능한 파생 형태가
필요하다는 것을 알기 전까지는 그냥 msleep을 사용하세요.