PREEMPT_RT, 6.12 sürümüyle birlikte ana akım Linux çekirdeğine girdi; artık ayrı bir yama seti uygulamadan CONFIG_PREEMPT_RT seçeneğini açmak yeterli. Ne var ki RT çekirdeği derlemek, deterministik davranışın yalnızca ön koşulu. Yanlış yapılandırılmış bir sistemde en yüksek öncelikli görev bile yüzlerce mikrosaniyelik gecikme sıçramaları görebilir. Bu yazıda endüstriyel bir kontrol döngüsü için tipik yapılandırma zincirini ele alıyoruz: çekirdek izolasyonu, IRQ affinity, SCHED_FIFO öncelik hiyerarşisi ve cyclictest ile doğrulama.
PREEMPT_RT Ne Sağlar, Ne Sağlamaz
RT çekirdeği kritik bölümleri preemptible hale getirir: spinlock'ların büyük kısmı rt_mutex tabanlı uyuyan kilitlere dönüşür, IRQ handler'ları kernel thread'i olarak koşar (threaded IRQ) ve priority inheritance mekanizması öncelik ters dönmesini engeller. Genelleme olarak sonuç, worst-case zamanlama gecikmesinin milisaniye mertebesinden onlarca mikrosaniye mertebesine inmesidir; kesin değer donanıma, firmware davranışına ve yapılandırmaya bağlıdır.
Sağlamadıkları da aynı derecede net: SMI kaynaklı kesintileri, CPU frekans geçişlerini, paylaşılan cache ve bellek denetleyicisi çekişmesini RT çekirdeği tek başına çözmez. Bu etkiler ancak izolasyon, doğru donanım seçimi ve ölçümle kontrol altına alınır; konuya bütünsel yaklaşımımızı hard real-time yetenekleri sayfasında özetliyoruz.
Çekirdek İzolasyonu: isolcpus, nohz_full, rcu_nocbs
Amaç, RT görevine ayrılmış çekirdeklerde kernel'in arka plan gürültüsünü sıfıra yaklaştırmak. Dört Cortex-A53 çekirdekli bir i.MX8M Plus gibi bir platformda CPU2-3'ü RT'ye ayırıp CPU0-1'i housekeeping (genel amaçlı) olarak bırakmak yaygın bir düzendir:
| Parametre | Görevi | Dikkat edilecekler |
|---|---|---|
isolcpus=domain,managed_irq,2-3 |
Scheduler load balancer'ını bu çekirdeklerden uzak tutar | Görevler buraya yalnızca açık CPU affinity ile gelir |
nohz_full=2-3 |
Çekirdekte tek runnable görev varken periyodik tick'i durdurur | Timekeeping CPU'su (genelde CPU0) listeye girmemeli |
rcu_nocbs=2-3 |
RCU callback'lerini housekeeping çekirdeklerine taşır | rcu_nocb_poll ile birlikte kullanın |
irqaffinity=0-1 |
Varsayılan IRQ maskesini housekeeping tarafına sabitler | Tekil IRQ'lar sonradan elle taşınabilir |
# U-Boot bootargs veya GRUB_CMDLINE_LINUX_DEFAULT içine
isolcpus=domain,managed_irq,2-3 nohz_full=2-3 rcu_nocbs=2-3 rcu_nocb_poll irqaffinity=0-1 skew_tick=1 nosoftlockup
# Doğrulama
cat /sys/devices/system/cpu/isolated
cat /proc/cmdline
skew_tick=1, tick'lerin tüm çekirdeklerde aynı anda tetiklenmesini önleyerek kilit çekişmesini azaltır. Buna ek olarak cpufreq governor'ını performance moduna sabitleyin ve RT çekirdeklerinde derin idle state'leri kapatın; tek bir C-state çıkışı bile döngünüze onlarca mikrosaniye ekleyebilir. Çalışma zamanında esneklik gerekiyorsa cpuset cgroup'ları (örneğin cset shield veya systemd tarafında AllowedCPUs=) benzer izolasyonu dinamik olarak kurar; ancak boot parametresi kadar kesin sınır çizmedikleri için hard real-time hedeflerinde önce isolcpus ile başlamak daha güvenlidir.
IRQ Affinity: Kesmeleri Bilinçli Yönlendirmek
İlk adım irqbalance servisini kapatmak; aksi halde elle yaptığınız atamaları arka planda sessizce ezer. Sonrasında kural basit: RT göreviyle ilgisi olmayan her kesme housekeeping çekirdeklerinde kalır, RT görevinin beklediği kesme (örneğin EtherCAT trafiğini taşıyan NIC) ise bilinçli olarak RT çekirdeğine taşınır.
systemctl disable --now irqbalance
# eth0 IRQ numarasını bul (örnek: 55)
grep eth0 /proc/interrupts
# IRQ 55'i CPU2'ye sabitle (bitmask 0x4)
echo 4 > /proc/irq/55/smp_affinity
# İlgili threaded IRQ'nun önceliğini yükselt
chrt -f -p 85 $(pgrep -f 'irq/55-eth0')
Threaded IRQ'lar varsayılan olarak SCHED_FIFO 50 önceliğiyle koşar. Kontrol döngünüz kesmenin işlenmesine bağımlıysa IRQ thread'ini görevin üzerinde tutmak genellikle doğru tercihtir; tersini yapıyorsanız nedenini tasarım dokümanına yazın.
SCHED_FIFO ve Öncelik Hiyerarşisi
RT görevleri SCHED_FIFO ile koşmalı; SCHED_RR aynı öncelik seviyesindeki görevler arasına time-slice eklediği için worst-case analizini karmaşıklaştırır. Pratikte işleyen kaba bir hiyerarşi şöyle:
- 99:
migration/Nvewatchdog/Nthread'lerine dokunmayın - 90-98: yalnızca en kritik IRQ thread'leri
- 80-89: birincil kontrol döngüsü ve ona doğrudan hizmet eden IRQ thread'leri
- 50: threaded IRQ varsayılanı
- 50'nin altı: deadline'ı olmayan yardımcı RT işleri
Uygulama tarafında iki klasik tuzak var. Birincisi sayfa hataları: mlockall(MCL_CURRENT | MCL_FUTURE) çağrısı ve stack prefault yapılmadan alınan ilk page fault, döngünüze onlarca mikrosaniye ekler. İkincisi RT throttling: sched_rt_runtime_us varsayılanı RT görevlere CPU zamanının yüzde 95'ini verir. İzole çekirdekte yüzde 100 CPU tüketen bir busy-poll döngüsü çalıştırıyorsanız bu sınırı -1 ile kapatın, ancak aynı çekirdekteki kernel thread'lerinin aç kalmayacağını analizle gösterin.
cyclictest ile Doğrulama Metodolojisi
Yapılandırma ancak ölçümle tamamlanmış sayılır. cyclictest'i RT çekirdeğine sabitleyip gerçekçi yük altında koşturun; boşta duran bir sistemin histogramı hiçbir şey kanıtlamaz.
# Yük: scheduler + bellek + ağ baskısı (housekeeping tarafında)
stress-ng --cpu 2 --vm 1 --vm-bytes 256M &
iperf3 -c 192.168.1.10 -t 86400 &
# Ölçüm: CPU2 üzerinde, 200µs periyot, histogramlı, 24 saat
cyclictest -m --policy=fifo -p90 -a2 -t1 -i200 -h400 -D24h
Değerlendirmede ortalamaya değil maksimuma bakın: hard real-time bağlamında tek bir deadline kaçırma başarısızlıktır. Histogramın kuyruğu asıl hikâyeyi anlatır; 20 dakikalık pürüzsüz bir koşu, saatte bir tetiklenen bir firmware olayını yakalayamaz. Bu yüzden en az 24 saat, tercihen gerçek üretim iş yüküyle günlerce ölçün ve sonuçları her çekirdek için ayrı raporlayın. x86 platformlarda hwlatdetect ile SMI kaynaklı gecikmeleri ayrıca tarayın; ARM SoC'lerde ise TrustZone/secure world tarafında geçen süreyi gözden kaçırmayın. Kuyrukta açıklayamadığınız bir outlier görürseniz körlemesine parametre değiştirmeyin: osnoise ve timerlat tracer'ları gecikmenin hangi kernel yolunda, hangi kesmede biriktiğini doğrudan gösterir; 6.x çekirdeklerde rtla aracı bu analizi tek komuta indirger.
Bu zincirin sahadaki karşılığı somut: i.MX8M Plus üzerinde PREEMPT_RT ve IgH EtherCAT master ile yürüttüğümüz bir çalışmada döngü jitter'ını 8µs'den 3.8µs'ye, Distributed Clocks senkronizasyonunu ±5µs'den 100ns'nin altına indirdik; ayrıntılar EtherCAT motion control vaka analizinde. Kendi sisteminizin gecikme bütçesini ve izolasyon stratejisini birlikte gözden geçirmek isterseniz, gerçek zamanlı sistem mimari denetimi bunun için doğru başlangıç noktasıdır.
