요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
Real-time application monitors
==============================
- Name: rtapp
- Type: container for multiple monitors
- Author: Nam Cao <[email protected]>
Description
-----------
Real-time applications may have design flaws such that they experience
unexpected latency and fail to meet their time requirements. Often, these flaws
follow a few patterns:
- Page faults: A real-time thread may access memory that does not have a
mapped physical backing or must first be copied (such as for copy-on-write).
Thus a page fault is raised and the kernel must first perform the expensive
action. This causes significant delays to the real-time thread
- Priority inversion: A real-time thread blocks waiting for a lower-priority
thread. This causes the real-time thread to effectively take on the
scheduling priority of the lower-priority thread. For example, the real-time
thread needs to access a shared resource that is protected by a
non-pi-mutex, but the mutex is currently owned by a non-real-time thread.
The `rtapp` monitor detects these patterns. It aids developers to identify
reasons for unexpected latency with real-time applications. It is a container of
multiple sub-monitors described in the following sections.
Monitor pagefault
+++++++++++++++++
The `pagefault` monitor reports real-time tasks raising page faults. Its
specification is::
RULE = always (RT imply not PAGEFAULT)
To fix warnings reported by this monitor, `mlockall()` or `mlock()` can be used
to ensure physical backing for memory.
This monitor may have false negatives because the pages used by the real-time
threads may just happen to be directly available during testing. To minimize
this, the system can be put under memory pressure (e.g. invoking the OOM killer
using a program that does `ptr = malloc(SIZE_OF_RAM); memset(ptr, 0,
SIZE_OF_RAM);`) so that the kernel executes aggressive strategies to recycle as
much physical memory as possible.
Monitor sleep
+++++++++++++
The `sleep` monitor reports real-time threads sleeping in a manner that may
cause undesirable latency. Real-time applications should only put a real-time
thread to sleep for one of the following reasons:
- Cyclic work: real-time thread sleeps waiting for the next cycle. For this
case, only the `clock_nanosleep` syscall should be used with `TIMER_ABSTIME`
(to avoid time drift) and `CLOCK_MONOTONIC` (to avoid the clock being
changed). No other method is safe for real-time. For example, threads
waiting for timerfd can be woken by softirq which provides no real-time
guarantee.
- Real-time thread waiting for something to happen (e.g. another thread
releasing shared resources, or a completion signal from another thread). In
this case, only futexes (FUTEX_LOCK_PI, FUTEX_LOCK_PI2 or one of
FUTEX_WAIT_*) should be used. Applications usually do not use futexes
directly, but use PI mutexes and PI condition variables which are built on
top of futexes. Be aware that the C library might not implement conditional
variables as safe for real-time. As an alternative, the librtpi library
exists to provide a conditional variable implementation that is correct for
real-time applications in Linux.
Beside the reason for sleeping, the eventual waker should also be
real-time-safe. Namely, one of:
- An equal-or-higher-priority thread
- Hard interrupt handler
- Non-maskable interrupt handler
This monitor's warning usually means one of the following:
- Real-time thread is blocked by a non-real-time thread (e.g. due to
contention on a mutex without priority inheritance). This is priority
inversion.
- Time-critical work waits for something which is not safe for real-time (e.g.
timerfd).
- The work executed by the real-time thread does not need to run at real-time
priority at all. This is not a problem for the real-time thread itself, but
it is potentially taking the CPU away from other important real-time work.
Application developers may purposely choose to have their real-time application
sleep in a way that is not safe for real-time. It is debatable whether that is a
problem. Application developers must analyze the warnings to make a proper
assessment.
The monitor's specification is::
RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
and ((not WAKE) until RT_FRIENDLY_WAKE)
RT_VALID_SLEEP_REASON = FUTEX_WAIT
or RT_FRIENDLY_NANOSLEEP
RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
and NANOSLEEP_TIMER_ABSTIME
and NANOSLEEP_CLOCK_MONOTONIC
RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
or WOKEN_BY_HARDIRQ
or WOKEN_BY_NMI
or KTHREAD_SHOULD_STOP
ALLOWLIST = BLOCK_ON_RT_MUTEX
or FUTEX_LOCK_PI
or TASK_IS_RCU
or TASK_IS_MIGRATION
Beside the scenarios described above, this specification also handle some
special cases:
- `KERNEL_THREAD`: kernel tasks do not have any pattern that can be recognized
as valid real-time sleeping reasons. Therefore sleeping reason is not
checked for kernel tasks.
- `KTHREAD_SHOULD_STOP`: a non-real-time thread may stop a real-time kernel
thread by waking it and waiting for it to exit (`kthread_stop()`). This
wakeup is safe for real-time.
- `ALLOWLIST`: to handle known false positives with the kernel.
- `BLOCK_ON_RT_MUTEX` is included in the allowlist due to its implementation.
In the release path of rt_mutex, a boosted task is de-boosted before waking
the rt_mutex's waiter. Consequently, the monitor may see a real-time-unsafe
wakeup (e.g. non-real-time task waking real-time task). This is actually
real-time-safe because preemption is disabled for the duration.
- `FUTEX_LOCK_PI` is included in the allowlist for the same reason as
`BLOCK_ON_RT_MUTEX`.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
설명
1-28`rtapp`은 Nam Cao가 작성한 여러 monitor의 컨테이너이다. 실시간 애플리케이션의 설계 결함 때문에 예기치 않은 지연이 발생하고 시간 요구 사항을 지키지 못하는 흔한 패턴을 탐지한다.
페이지 폴트는 실시간 thread가 물리 메모리에 아직 매핑되지 않았거나 copy-on-write 때문에 먼저 복사해야 하는 메모리에 접근할 때 발생한다. 커널이 비용이 큰 처리를 먼저 수행하므로 실시간 thread가 크게 지연된다.
우선순위 역전은 실시간 thread가 더 낮은 우선순위의 thread를 기다리며 차단될 때 생긴다. 예를 들어 공유 자원을 보호하는 non-PI mutex를 비실시간 thread가 소유하고 있으면 실시간 thread는 사실상 그 낮은 thread의 scheduling 우선순위로 실행되는 것과 같은 영향을 받는다.
`rtapp` monitor는 이런 패턴을 탐지해 개발자가 실시간 애플리케이션의 예상 밖 지연 원인을 찾도록 돕는다. 아래에서 설명하는 여러 하위 monitor를 묶는다.
실시간 thread의 지연을 메모리 준비와 scheduling 의존성으로 나누어 보여준다.
Real-time application monitors
==============================
- Name: rtapp
- Type: container for multiple monitors
- Author: Nam Cao <[email protected]>
Description
-----------
Real-time applications may have design flaws such that they experience
unexpected latency and fail to meet their time requirements. Often, these flaws
follow a few patterns:
- Page faults: A real-time thread may access memory that does not have a
mapped physical backing or must first be copied (such as for copy-on-write).
Thus a page fault is raised and the kernel must first perform the expensive
action. This causes significant delays to the real-time thread
- Priority inversion: A real-time thread blocks waiting for a lower-priority
thread. This causes the real-time thread to effectively take on the
scheduling priority of the lower-priority thread. For example, the real-time
thread needs to access a shared resource that is protected by a
non-pi-mutex, but the mutex is currently owned by a non-real-time thread.
The `rtapp` monitor detects these patterns. It aids developers to identify
reasons for unexpected latency with real-time applications. It is a container of
multiple sub-monitors described in the following sections.
pagefault monitor
29-46`pagefault` monitor는 실시간 task가 page fault를 발생시키면 보고한다. 명세는 다음과 같다.
RULE = always (RT imply not PAGEFAULT)
보고된 경고를 해결하려면 `mlockall()` 또는 `mlock()`으로 메모리의 물리 backing을 미리 보장할 수 있다.
시험 중 실시간 thread가 쓰는 page가 우연히 즉시 사용 가능하면 이 monitor에 false negative가 생길 수 있다. 이를 줄이려면 `ptr = malloc(SIZE_OF_RAM); memset(ptr, 0, SIZE_OF_RAM);` 같은 프로그램으로 OOM killer를 유발하는 등 시스템에 메모리 압박을 가해, 커널이 물리 메모리를 최대한 회수하는 공격적인 전략을 실행하게 할 수 있다.
실시간 task의 page fault를 관찰하고 물리 backing을 사전에 고정한다.
Monitor pagefault
+++++++++++++++++
The `pagefault` monitor reports real-time tasks raising page faults. Its
specification is::
RULE = always (RT imply not PAGEFAULT)
To fix warnings reported by this monitor, `mlockall()` or `mlock()` can be used
to ensure physical backing for memory.
This monitor may have false negatives because the pages used by the real-time
threads may just happen to be directly available during testing. To minimize
this, the system can be put under memory pressure (e.g. invoking the OOM killer
using a program that does `ptr = malloc(SIZE_OF_RAM); memset(ptr, 0,
SIZE_OF_RAM);`) so that the kernel executes aggressive strategies to recycle as
much physical memory as possible.
sleep monitor의 판단 기준
47-92`sleep` monitor는 바람직하지 않은 지연을 일으킬 수 있는 방식으로 실시간 thread가 잠들 때 보고한다. 실시간 애플리케이션은 주기 작업을 기다리거나 특정 사건을 기다리는 두 부류의 이유로만 실시간 thread를 재워야 한다.
주기 작업에서는 시간 drift를 피하도록 `TIMER_ABSTIME`, 시계 변경 영향을 피하도록 `CLOCK_MONOTONIC`을 지정한 `clock_nanosleep` syscall만 사용해야 한다. timerfd를 기다리는 thread는 실시간 보장이 없는 softirq에 의해 깨어날 수 있으므로 안전하지 않다.
공유 자원의 해제나 다른 thread의 완료 신호를 기다릴 때는 `FUTEX_LOCK_PI`, `FUTEX_LOCK_PI2`, 또는 `FUTEX_WAIT_*` 중 하나인 futex만 사용해야 한다. 애플리케이션은 보통 futex를 직접 쓰지 않고 그 위에 구현된 PI mutex와 PI condition variable을 쓴다.
C library의 condition variable이 실시간 안전성을 보장하지 않을 수 있음에 유의해야 한다. 대안인 `librtpi`는 Linux 실시간 애플리케이션에 올바른 condition variable 구현을 제공한다.
잠드는 이유뿐 아니라 최종적으로 깨우는 주체도 실시간에 안전해야 한다. 허용되는 주체는 같거나 더 높은 우선순위의 thread, hard interrupt handler, non-maskable interrupt handler이다.
경고는 보통 non-PI mutex 경합 같은 우선순위 역전, timerfd처럼 실시간에 안전하지 않은 사건의 대기, 또는 해당 작업 자체에 실시간 우선순위가 필요하지 않음을 뜻한다. 마지막 경우 그 thread 자체의 문제는 아니지만 다른 중요한 실시간 작업의 CPU 시간을 빼앗을 수 있다.
개발자가 의도적으로 실시간에 안전하지 않은 sleep 방식을 선택할 수도 있으므로 경고 자체만으로 결함을 확정할 수 없다. 애플리케이션 개발자가 각 경고를 분석해 적절히 판단해야 한다.
sleep 이유와 wake 주체를 모두 검사해야 한다.
Monitor sleep
+++++++++++++
The `sleep` monitor reports real-time threads sleeping in a manner that may
cause undesirable latency. Real-time applications should only put a real-time
thread to sleep for one of the following reasons:
- Cyclic work: real-time thread sleeps waiting for the next cycle. For this
case, only the `clock_nanosleep` syscall should be used with `TIMER_ABSTIME`
(to avoid time drift) and `CLOCK_MONOTONIC` (to avoid the clock being
changed). No other method is safe for real-time. For example, threads
waiting for timerfd can be woken by softirq which provides no real-time
guarantee.
- Real-time thread waiting for something to happen (e.g. another thread
releasing shared resources, or a completion signal from another thread). In
this case, only futexes (FUTEX_LOCK_PI, FUTEX_LOCK_PI2 or one of
FUTEX_WAIT_*) should be used. Applications usually do not use futexes
directly, but use PI mutexes and PI condition variables which are built on
top of futexes. Be aware that the C library might not implement conditional
variables as safe for real-time. As an alternative, the librtpi library
exists to provide a conditional variable implementation that is correct for
real-time applications in Linux.
Beside the reason for sleeping, the eventual waker should also be
real-time-safe. Namely, one of:
- An equal-or-higher-priority thread
- Hard interrupt handler
- Non-maskable interrupt handler
This monitor's warning usually means one of the following:
- Real-time thread is blocked by a non-real-time thread (e.g. due to
contention on a mutex without priority inheritance). This is priority
inversion.
- Time-critical work waits for something which is not safe for real-time (e.g.
timerfd).
- The work executed by the real-time thread does not need to run at real-time
priority at all. This is not a problem for the real-time thread itself, but
it is potentially taking the CPU away from other important real-time work.
Application developers may purposely choose to have their real-time application
sleep in a way that is not safe for real-time. It is debatable whether that is a
problem. Application developers must analyze the warnings to make a proper
assessment.
sleep monitor 명세
93-116최상위 규칙은 실시간 task가 잠들 때 `RT_FRIENDLY_SLEEP` 또는 `ALLOWLIST` 조건을 충족하도록 요구한다.
RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
and ((not WAKE) until RT_FRIENDLY_WAKE)
RT_VALID_SLEEP_REASON = FUTEX_WAIT
or RT_FRIENDLY_NANOSLEEP
RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
and NANOSLEEP_TIMER_ABSTIME
and NANOSLEEP_CLOCK_MONOTONIC
RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
or WOKEN_BY_HARDIRQ
or WOKEN_BY_NMI
or KTHREAD_SHOULD_STOP
ALLOWLIST = BLOCK_ON_RT_MUTEX
or FUTEX_LOCK_PI
or TASK_IS_RCU
or TASK_IS_MIGRATION
`RT_FRIENDLY_SLEEP`은 유효한 sleep 이유 또는 kernel thread라는 조건과, `RT_FRIENDLY_WAKE`가 발생할 때까지 `WAKE`가 발생하지 않는다는 시간 조건을 결합한다.
유효한 sleep 이유는 futex wait 또는 안전한 `CLOCK_NANOSLEEP`이며, 안전한 nanosleep은 `NANOSLEEP_TIMER_ABSTIME`과 `NANOSLEEP_CLOCK_MONOTONIC`을 모두 요구한다.
안전한 wake는 동일 이상 우선순위, hard IRQ, NMI, 또는 `KTHREAD_SHOULD_STOP`이다. 알려진 예외는 `BLOCK_ON_RT_MUTEX`, `FUTEX_LOCK_PI`, `TASK_IS_RCU`, `TASK_IS_MIGRATION`으로 허용 목록에 둔다.
sleep 원인과 미래의 wake 주체를 LTL 하위 표현식으로 조합한다.
The monitor's specification is::
RULE = always ((RT and SLEEP) imply (RT_FRIENDLY_SLEEP or ALLOWLIST))
RT_FRIENDLY_SLEEP = (RT_VALID_SLEEP_REASON or KERNEL_THREAD)
and ((not WAKE) until RT_FRIENDLY_WAKE)
RT_VALID_SLEEP_REASON = FUTEX_WAIT
or RT_FRIENDLY_NANOSLEEP
RT_FRIENDLY_NANOSLEEP = CLOCK_NANOSLEEP
and NANOSLEEP_TIMER_ABSTIME
and NANOSLEEP_CLOCK_MONOTONIC
RT_FRIENDLY_WAKE = WOKEN_BY_EQUAL_OR_HIGHER_PRIO
or WOKEN_BY_HARDIRQ
or WOKEN_BY_NMI
or KTHREAD_SHOULD_STOP
ALLOWLIST = BLOCK_ON_RT_MUTEX
or FUTEX_LOCK_PI
or TASK_IS_RCU
or TASK_IS_MIGRATION
특수 사례
117-133`KERNEL_THREAD`는 유효한 실시간 sleep 이유로 식별할 수 있는 공통 패턴이 없으므로 sleep 이유를 검사하지 않는다.
`KTHREAD_SHOULD_STOP`은 비실시간 thread가 실시간 kernel thread를 깨우고 종료를 기다리는 `kthread_stop()` 경로이다. 이 wakeup은 실시간에 안전하다.
`ALLOWLIST`는 커널에서 알려진 false positive를 처리한다. `BLOCK_ON_RT_MUTEX`는 rt_mutex 해제 경로가 waiter를 깨우기 전에 boosted task의 boost를 해제하므로 허용한다. monitor에는 비실시간 task가 실시간 task를 깨우는 것처럼 보일 수 있지만, 그 구간에는 preemption이 비활성화되어 실제로는 안전하다.
`FUTEX_LOCK_PI`도 `BLOCK_ON_RT_MUTEX`와 같은 구현상의 이유로 허용 목록에 포함한다.
Beside the scenarios described above, this specification also handle some
special cases:
- `KERNEL_THREAD`: kernel tasks do not have any pattern that can be recognized
as valid real-time sleeping reasons. Therefore sleeping reason is not
checked for kernel tasks.
- `KTHREAD_SHOULD_STOP`: a non-real-time thread may stop a real-time kernel
thread by waking it and waiting for it to exit (`kthread_stop()`). This
wakeup is safe for real-time.
- `ALLOWLIST`: to handle known false positives with the kernel.
- `BLOCK_ON_RT_MUTEX` is included in the allowlist due to its implementation.
In the release path of rt_mutex, a boosted task is de-boosted before waking
the rt_mutex's waiter. Consequently, the monitor may see a real-time-unsafe
wakeup (e.g. non-real-time task waking real-time task). This is actually
real-time-safe because preemption is disabled for the duration.
- `FUTEX_LOCK_PI` is included in the allowlist for the same reason as
`BLOCK_ON_RT_MUTEX`.
요약·해설
monitor_rtapp.rst:1-133실시간 애플리케이션의 page fault와 부적절한 sleep 및 wakeup으로 생기는 지연을 pagefault·sleep 하위 monitor가 탐지하는 규칙을 설명합니다.