Realtime
Операционная система реального времени (в отличии от системы общего назначения) оптимизирована на уменьшение задержек реакции (latency) и детерминизм (maximum latency) при обработке событий. Применяется для построения систем в сферах телекоммуникаций, управления машинами, высокочастотной торговли, обработки звука и т.п. [1]
На данный момент в репозиторий Сизиф под архитектуру x86_64 собраны два ядра реального времени:
kernel-image-xenomai
«Двойное ядро» состоящее из высокоприоритетного ко-ядра Cobalt реализующего различные (skins) RTOS API Xenomai 3 и ядра линукс с I-Pipe (Adeos) патчем реализующим систему жёсткого реального времени. Xenomai 3 может эмулировать RTOS API: pSOS+, uITRON, VxWorks, RTAI, VRTX, а так же содержит нативное API Alchemy и поддерживает Real-Time Driver Model (RTDM). Документация на англ.
Юзерспейс и специализированные тесты для этого ядра находятся в пакете xenomai .
kernel-image-rt
Real Time Linux с PREEMPT_RT патчем (Ingo Molnar, Thomas Gleixner) реализующим POSIX real-time API. Иногда называемое -rt ядро.
Считается, что ядра данного типа наиболее оптимально работают с vanilla конфигом. Поэтому была использована следующая методология создания конфига: defconfig + все опциональные модули из std_def ядра + тюнинг RT (отключено NO_HZ , отключены многие опции _DEBUG + прочие мелкие оптимизации).
- Для облегчения тестирования это ядро содержит два дополнительных патча от консорциума OSADL:
- https://www.osadl.org/Latency-histograms.latencyhist.0.html
- https://www.osadl.org/Precise-load-measurement.precise-system-load.0.html
Для тестирования этого ядра можно использовать пакет linux-rt-tests .
# apt-get install update-kernel # update-kernel -t rt # reboot
Тестирование ядра
Очень важно первоначально определить пригодность вашей системы для задач реального времени.
cyclictest
Базовый способ тестирования ядра реального времени (POSIX API), это запуск утилиты cyclictest параллельно с созданием нагрузки на систему (генерацию нагрузки вы должны организовать сами, например, запуском unixbench , stress-ng , hackbench , gltestperf в цикле) в течении длительного времени (не менее 24 часов). Пример запуска:
Пример вывода на обычном ядре (std_def):
T: 0 ( 1931) P:99 I:1000 C:2018975 Min: 1 Act: 1 Avg: 2 Max: 2151 T: 1 ( 1932) P:99 I:1500 C:1345983 Min: 1 Act: 2 Avg: 3 Max: 2187 T: 2 ( 1933) P:99 I:2000 C:1009488 Min: 1 Act: 1 Avg: 3 Max: 2266 T: 3 ( 1934) P:99 I:2500 C: 807593 Min: 1 Act: 2 Avg: 3 Max: 1886
Для оценки результата следует смотреть на значения Max показывающие время реакции на прерывания в микросекундах. (Есть примеры, на обычной системе, где Max доходит до 5-значных чисел.) Пример вывода на -rt ядре (SMI прерываний не было):
T: 0 ( 4041) P:99 I:1000 C:4719726 Min: 1 Act: 2 Avg: 2 Max: 17 T: 1 ( 4042) P:99 I:1500 C:3146481 Min: 1 Act: 2 Avg: 2 Max: 26 T: 2 ( 4043) P:99 I:2000 C:2359859 Min: 2 Act: 2 Avg: 2 Max: 14 T: 3 ( 4044) P:99 I:2500 C:1887886 Min: 1 Act: 2 Avg: 2 Max: 14
(Есть сообщения, что в некоторых системах Max остается в пределах 1-значного числа.) Пример вывода на -rt ядре при наличии SMI прерываний, cyclictest запущен с дополнительной опцией —smi :
T: 0 (20422) P:99 I:1000 C:305390555 Min: 2 Act: 2 Avg: 3 Max: 297 SMI: 176 T: 1 (20423) P:99 I:1500 C:203593701 Min: 2 Act: 2 Avg: 3 Max: 318 SMI: 176 T: 2 (20424) P:99 I:2000 C:152695274 Min: 2 Act: 2 Avg: 2 Max: 274 SMI: 176 T: 3 (20425) P:99 I:2500 C:122156218 Min: 2 Act: 2 Avg: 3 Max: 340 SMI: 176
Опция —smi добавляет колонку SMI , которая показывает сколько было System Management Interrupts за время теста (счетчик прерываний считывается из MSR, если в вашем процессоре нет такого счетчика, то для их обнаружения можно использовать утилиту hwlatdetect ). SMI прерывания могут значительно ухудшить показатели системы. В худшем случае, SMI прерывания способны давать задержки на многие миллисекунды. В системе пригодной для реального времени можно снизить число SMI через настройки или перепрошивку BIOS/firmware [2] . Впрочем, полностью отключать эти прерывания не рекомендуется, так как они выполняют полезные функции для здоровья системы (контроль температуры и прочее). Часто полезно прочитать HPC Tuning Guide [3] для вашего железа. Если задержки в системе превышает требуемый максимум, то она не пригодна для использования в реальном времени. Ещё один источник высоких задержек — не оптимизированные для реального времени драйвера оборудования (запрещающие прерывания на долгое время) или само железо (блокирующее процессор на долгое время).
hwlatdetect
Тест (с помощью hwlat tracer’а ядра) обнаруживает задержки создаваемые железом или firmware не зависимые от ядра, такие как SMI. (Не используйте его на продакшен системах или во время работы других тестов так как принцип его работы — нагружать процессоры на продолжительное время при запрещенных прерываниях, постоянно замеряя время (TSC) и определяя таким образом интервалы с пропусками.) Пример на системе с большими задержками:
# hwlatdetect hwlatdetect: test duration 120 seconds detector: tracer parameters: Latency threshold: 10us Sample window: 1000000us Sample width: 500000us Non-sampling period: 500000us Output File: None Starting test test finished Max Latency: 10099000us Samples recorded: 67 Samples exceeding threshold: 67
rtcheck
Небольшая утилита (от IBM) проверяющая, что запущенное ядро имеет свойства -rt ядра. Запускать с опцией -v для просмотра отчета.
jitterdebugger
Более легкий в использовании аналог cyclictest (от Siemens и SUSE) с уклоном в воспроизводимый статистический анализ. (Домашняя страница. Видео презентация. англ.) Пример запуска:
Realtime kernel patchset
This article describes the Linux kernel realtime patch set, and some utilities useful for trouble shooting scheduling latencies.
What is realtime?
Realtime applications have operational deadlines between some triggering event and the application’s response to that event. To meet these operational deadlines, programmers use realtime operating systems (RTOS) on which the maximum response time can be calculated or measured reliably for the given application and environment. A typical RTOS uses priorities. The highest priority task wanting the CPU always gets the CPU within a fixed amount of time after the event waking the task has taken place. On such an RTOS the latency of a task only depends on the tasks running at equal or higher priorities; tasks running at lower priorities may be ignored. On a non-realtime OS (most GNU/Linux distributions running their default kernels), since latencies depend on each process running on the system, it is obviously much harder to ensure deadlines will be met every time, and this difficulty scales nonlinearly with system complexity. Determinism in scheduling becomes yet more difficult to achieve because preemption can be switched off for an arbitrary amount of time. A high priority task wanting to run can thus be delayed indefinitely by lower priority tasks with preemption disabled.
How does the realtime patch work
The RT-Preempt patch converts Linux into a fully preemptible kernel. This is done through:
- Making in-kernel locking-primitives (using spinlocks) preemptible by reimplementation with rtmutexes.
- Critical sections protected by i.e. spinlock_t and rwlock_t are now preemptible. The creation of non-preemptible sections (in kernel) is still possible with raw_spinlock_t (same APIs like spinlock_t).
- Implementing priority inheritance for in-kernel spinlocks and semaphores.
- Converting interrupt handlers into preemptible kernel threads: The RT-Preempt patch treats soft interrupt handlers in kernel thread context, which is represented by a task_struct like a common user space process. However it is also possible to register an IRQ in kernel context.
- Converting the old Linux timer API into separate infrastructures for high resolution kernel timers plus one for timeouts, leading to user space POSIX timers with high resolution.
Installation
There are two realtime patched kernels available: linux-rt and linux-rt-lts , which both have a configuration based on the main linux kernel package. linux-rt follows the development branch of the -rt patch, while linux-rt-lts tracks a stable branch of the rt patchset.
Scheduling latency
In the context of the scheduler, latency is the time that passes from the occurrence of an event until the handling of said event. Often the delay from the firing of an interrupt until the interrupt handler starts running, but could also be from the expiration of a timer, etc.
There can be many varied causes for high scheduling latencies. Some worth mentioning (in no particular order) are: a misconfigured system, bad hardware, badly programmed kernel modules, CPU power management, faulty hardware timers, SMIs and SMT.
When trying to determine a system’s maximum scheduling latency, the system needs to be put under load. A busy system will tend to experience greater latencies than an idle one. To sufficiently characterize latencies of interest, it would be prudent to run tests for a long time and under a variety of nominal and worst-case load conditions. Further, since many subsystems such as disks, network devices, USB and graphics may be used sparsely after a system is brought online, care should be taken to characterize latency with these subsystems active as well.
Latency testing utilities
Understanding latency is non-intuitive. In measuring and interpreting latency, errors are common and very likely to happen even with experienced computer scientists. Popular tools are often incorrect. This talk explains some common pitfalls. There are several tools available to check kernel scheduling latencies, and to track down the causes of latency spikes. One set of tools comes in a package called rt-tests .
cyclictest
One of the programs in rt-tests is called cyclictest, which can be used to verify the maximum scheduling latency, and for tracking down the causes of latency spikes. cyclictest works by measuring the time between the expiration of a timer a thread sets and when the thread starts running again.
Here is the result of a typical test run:
# /dev/cpu_dma_latency set to 0us policy: fifo: loadavg: 239.09 220.49 134.53 142/1304 23799 T: 0 (23124) P:98 I:1000 C: 645663 Min: 2 Act: 4 Avg: 4 Max: 23 T: 1 (23125) P:98 I:1500 C: 430429 Min: 2 Act: 5 Avg: 3 Max: 23 T: 2 (23126) P:98 I:2000 C: 322819 Min: 2 Act: 4 Avg: 3 Max: 15 T: 3 (23127) P:98 I:2500 C: 258247 Min: 2 Act: 5 Avg: 4 Max: 32 ^C
It shows a four CPU core system running one thread (SCHED_FIFO) per core at priority 98, with memory locked, the system is also under a high load due to running hackbench in a separate terminal. What is most interesting is the max schedling latency detected, in this case 32 usecs on core 3.
hackbench
An idle kernel will tend to show much lower scheduling latencies, it is essential to put some load on it to get a realistic result. This can be done with another utility in the rt-tests package called hackbench. It works by creating multiple pairs of threads or processes, that pass data between themselves either over sockets or pipes. To make it run longer add the -l parameter: hackbench -l 1000000 .
hwlatdetect
hwlatdetect can be used to detect SMIs taking an inordinate time, thus introducing latency by blocking normal kernel execution. It consists of a kernel module (present in both linux-rt and linux-rt-lts), and a python script to launch the process and report the results back to the user. To check if the system uses NMIs run the following command:
NMI: 3335 3336 3335 3335 Non-maskable interrupts
The hwlatdetect kernel module works by turning everything running on the CPUs off through the stop_machine() call. It then polls the TSC (Time Stamp Counter) looking for gaps in the generated data stream. Any gaps indicates that it was interrupted by a NMI, as they are the only possible mechanism (apart from a broken TSC implementation). To run the program for 120 secs, with a detection threshold of 15 usecs, execute the following:
# hwlatdetect --duration=120 --threshold=15
hwlatdetect: test duration 120 seconds parameters: Latency threshold: 15us Sample window: 1000000us Sample width: 500000us Non-sampling period: 500000us Output File: None Starting test test finished Max Latency: 21us Samples recorded: 16 Samples exceeding threshold: 16 1408928107.0286324723 18 17 . . 1408928180.0296881126 15 21 . . 1408928212.0300332889 18 18
The result shows 16 NMIs detected that exceeded the 15 usecs threshold specified, the maximum latency detected was 21 usecs.