Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX XENOMAI 2026

Xenomai
Dual-Kernel Hard Gerçek Zamanlı Linux

Linux çekirdeğinin yanında Cobalt RTOS çalıştırarak mikrosaniye altı gecikme garantisi — PREEMPT-RT'nin yetmediği yerde Xenomai.

00 Xenomai neden var

Genel amaçlı Linux çekirdeği, en iyi PREEMPT-RT yamasıyla bile belirli gerçek zamanlı görevler için yeterli gecikme garantisi veremez.

PREEMPT-RT'nin sınırları

PREEMPT-RT yaması Linux'u "soft real-time" düzeye taşır: büyük çoğunlukla 50–200 µs jitter elde edilir, ancak şu nedenlerle deterministik alt sınır garanti edilemez:

IRQ thread latencyDonanım kesmeleri threaded IRQ olarak işlenir; yüksek yük altında Linux zamanlayıcısının öncelik kuyruğundan geçmesi gecikmeye yol açar
spinlock → mutex dönüşümüPREEMPT-RT'nin spinlock'ları preemption noktasına dönüşmesi bazı sürücü yollarında beklenmedik yoksunluk (starvation) üretir
Linux memory subsystemmlockall() ile sayfalar kilitlense bile page fault, TLB shootdown ve zone reclaim gecikmesi kaçınılamaz
SMI (System Management Interrupt)x86 platformlarında firmware kaynaklı SMI, tüm çekirdekleri durdurur; Linux bunun farkında bile değildir

Hard RT gereksinimleri

Bazı endüstriyel ve güvenlik kritik uygulamalar deterministik worst-case execution time (WCET) ister:

UygulamaTipik deadlineKabul edilebilir jitter
EtherCAT master (servo kontrol)1 ms< 10 µs
Dijital osiloskopin örnekleme loop'u100 µs< 1 µs
Motor akım kontrolü (FOC)50 µs< 5 µs
Ses işleme (96 kHz, 4 kanal)~10 µs< 2 µs
Güç elektroniği PWM zamanlaması20 µs< 500 ns

Dual-kernel yaklaşım

Xenomai'nin çözümü, Linux'u küçük bir gerçek zamanlı mikro-çekirdeğin (Cobalt) altında "idle task" olarak çalıştırmaktır. Donanım kesmeleri önce Cobalt'a gider; Cobalt RT görevini işledikten sonra Linux'a iletir.

Donanım IRQ
    │
    ▼
 Cobalt (Xenomai mikro-çekirdek)
    │  RT görevleri burada çalışır — deterministic
    │  Linux'a iletilmemiş IRQ'lar yok sayılır
    ▼
 Linux (co-kernel — en yüksek öncelikli idle task)
    │  Normal süreçler, sürücüler, ağ, dosya sistemi
    ▼
 Donanım (I-pipe / Dovetail interrupt pipeline)
    

PREEMPT-RT vs Xenomai karşılaştırması

KriterPREEMPT-RTXenomai (Cobalt)
Minimum latency~10–30 µs< 1 µs (ideal)
Worst-case jitter100–500 µs yük altında2–10 µs tipik
POSIX uyumuTam POSIXPOSIX skin (kısmi)
Sürücü uyumuTüm Linux sürücüleriRTDM dışı sürücüler "mode switch" üretir
Öğrenme eğrisiDüşük — standart Linux geliştirmeYüksek — dual-kernel kavramları
Kernel yamasıStandart, upstream'e giriyorDovetail yaması gerekir
Kullanım alanıGenel soft-RT, IIoTHard-RT, servo, güç elektroniği

01 Xenomai mimarisi

Xenomai 3'ün mimarisi iki katmandan oluşur: çekirdek tarafında Dovetail interrupt pipeline ve Cobalt mikro-çekirdek; kullanıcı tarafında libcobalt ve çeşitli API skin'ler.

I-pipe'tan Dovetail'e

Xenomai 2, Linux çekirdeğini "I-pipe" (interrupt pipeline) yamasıyla değiştiriyordu. Xenomai 3.x, Linux 5.4+ ile birlikte daha temiz ve upstream dostu olan Dovetail arayüzüne geçti. Dovetail, Linux'un IRQ akışına iki ayrı "stage" (sahne) ekler:

OOB stage (Out-Of-Band)Xenomai/Cobalt'ın IRQ'ları işlediği düşük gecikme kanalı; Linux IRQ alt sistemi bu aşamada devre dışı
In-band stageNormal Linux IRQ işleme yolu; OOB stage tamamlandıktan sonra devreye girer
Donanım IRQ gelir
       │
  ┌────▼────────────────────────┐
  │   Dovetail IRQ pipeline     │
  │  ┌─────────────────────┐    │
  │  │  OOB stage (Cobalt) │◄── RT görevleri, RT timer
  │  └──────────┬──────────┘    │
  │             │ irq_exit_oob()│
  │  ┌──────────▼──────────┐    │
  │  │  In-band (Linux)    │◄── normal sürücüler, syscall
  │  └─────────────────────┘    │
  └─────────────────────────────┘
    

Cobalt çekirdeği

Cobalt, Linux çekirdeği modülü olarak yüklenen küçük bir RTOS'tur. Temel bileşenleri:

xntimerDonanım timer'ını doğrudan programlayan, Linux'un yüksek çözünürlüklü timer alt sisteminden bağımsız RT zamanlayıcı
xnthreadRT görevlerinin çekirdek temsili; Linux task_struct ile eşleşik yaşar (dual-personality thread)
xnschedCobalt'ın FIFO/RR/sporadic zamanlayıcısı; Linux CFS'ten tamamen bağımsız
xnsynchRT mutex, semafor, event bayrakları — priority inheritance dahil
RTDMReal-Time Driver Model — RT görevlerinden çağrılabilen sürücü arayüzü

Kullanıcı alanı: libcobalt ve skin'ler

BileşenAçıklamaBaşlık
libcobaltCobalt syscall wrapper; glibc'nin üstüne binerdahili
POSIX skinpthread, mq_*, sem_* — mevcut kodu port etmek kolaypthread.h (cobalt)
Alchemy APIVxWorks/pSOS'a benzer RT_TASK, RT_MUTEX, RT_SEM API'sialchemy/*.h
VXWORKS skinVxWorks uyumu (taskSpawn, semTake vb.)vxworks/*.h
pSOS skinpSOS+ uyumu (t_start, sm_p vb.)psos/*.h

Mode switch (alan geçişi)

Bir RT görevinin Linux syscall'u çağırması (örn. printf, open, malloc) "mode switch" tetikler: görev OOB stage'den in-band'e geçer ve RT güvencesi kaybolur. Bu durum /proc/xenomai/stat'ta MSW sütununda sayılır.

02 Xenomai 3 kurulumu

Xenomai kurulumu üç aşamadan oluşur: Linux çekirdeğine Dovetail yaması uygulamak, yamayı içeren çekirdeği derlemek ve Xenomai kullanıcı alanı kitaplıklarını derlemek.

Gereksinimler

KernelLinux 5.10 veya 6.1 LTS önerilir; Dovetail yaması bu sürümlere düzenli bakım alır
Xenomai sürümüXenomai 3.2.x (kararlı), kaynak: https://xenomai.org/downloads/xenomai/stable/
Araçlargcc, make, flex, bison, libelf-dev, libssl-dev, autoconf, libtool

Dovetail yamasını uygulama

# 1. Kaynak indirme
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.80.tar.xz
wget https://xenomai.org/downloads/xenomai/stable/xenomai-3.2.3.tar.bz2

tar xf linux-6.1.80.tar.xz
tar xf xenomai-3.2.3.tar.bz2

# 2. Dovetail yamasını uygula
cd xenomai-3.2.3
scripts/prepare-kernel.sh \
    --linux=../linux-6.1.80 \
    --arch=arm64 \
    --dovetail=dovetail-6.1-arm64.patch

# 3. Kernel konfigürasyonu
cd ../linux-6.1.80
make ARCH=arm64 defconfig

# Xenomai için zorunlu seçenekler
# CONFIG_XENOMAI=y
# CONFIG_DOVETAIL=y
# CONFIG_IPIPE_LEGACY=n  (Dovetail kullanıyoruz)
# CONFIG_HZ_1000=y       (daha yüksek timer çözünürlüğü)
# CONFIG_NO_HZ_FULL=n    (RT ile uyumsuz)
# CONFIG_CPU_FREQ=n      (frekans ölçekleme RT gecikme üretir)
# CONFIG_CPU_IDLE=n      (C-state gecikmesi engellenir)

make ARCH=arm64 menuconfig

Çekirdek derleme

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
     -j$(nproc) Image modules dtbs

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
     INSTALL_MOD_PATH=../rootfs modules_install

# Çekirdek imajını kopyala
cp arch/arm64/boot/Image ../rootfs/boot/Image-xenomai
cp arch/arm64/boot/dts/*/my-board.dtb ../rootfs/boot/

Xenomai kullanıcı alanı derleme

cd xenomai-3.2.3

./configure \
    --host=aarch64-linux-gnu \
    --with-core=cobalt \
    --enable-smp \
    --disable-lores-clock \
    CFLAGS="-O2 -march=armv8-a" \
    LDFLAGS="-Wl,-rpath,/usr/xenomai/lib"

make -j$(nproc)
make DESTDIR=../rootfs install

# Kurulan dosyalar:
# /usr/xenomai/lib/libcobalt.so  — RT syscall wrapper
# /usr/xenomai/lib/libalchemy.so — Alchemy API
# /usr/xenomai/bin/xeno-latency  — latency test aracı
# /usr/xenomai/bin/xeno-config   — derleme bayrakları

Uygulama derleme

# xeno-config ile bayrakları al
CFLAGS=$(xeno-config --skin=posix --cflags)
LDFLAGS=$(xeno-config --skin=posix --ldflags)

aarch64-linux-gnu-gcc $CFLAGS -o rt_task my_rt_task.c $LDFLAGS

# Alternatif: Alchemy skin
CFLAGS=$(xeno-config --skin=alchemy --cflags)
LDFLAGS=$(xeno-config --skin=alchemy --ldflags)
aarch64-linux-gnu-gcc $CFLAGS -o alchemy_task alchemy_task.c $LDFLAGS

Çalışma zamanı gereksinimleri

# RT görevleri için önerilen sistem ayarları (/etc/sysctl.conf)
kernel.sched_rt_runtime_us = -1   # RT bandwidth sınırı kaldır

# Kullanıcı limitleri (/etc/security/limits.conf)
@realtime  -  rtprio   99
@realtime  -  memlock  unlimited

# Modüller
modprobe xenomai
modprobe xeno_cobalt

03 POSIX skin

Xenomai'nin POSIX skin'i, mevcut POSIX çok iş parçacıklı kodu Cobalt üzerine minimal değişiklikle taşımak için en uygun yoldur.

RT thread oluşturma

#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
#include <xenomai/init.h>

#define TASK_PERIOD_NS  1000000LL   /* 1 ms */
#define STACK_SIZE      65536

static void *rt_loop(void *arg)
{
    struct timespec next;
    int overruns = 0;

    /* Cobalt POSIX: clock_gettime ile zamanı al */
    clock_gettime(CLOCK_MONOTONIC, &next);

    for (;;) {
        /* Periyodik bekleme */
        next.tv_nsec += TASK_PERIOD_NS;
        if (next.tv_nsec >= 1000000000LL) {
            next.tv_nsec -= 1000000000LL;
            next.tv_sec++;
        }

        int ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
        if (ret == EINTR) {
            overruns++;
            continue;
        }

        /* --- Gerçek zamanlı görev gövdesi --- */
        do_control_loop();
    }
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t thread;
    pthread_attr_t attr;
    struct sched_param param;

    /* Xenomai başlatma */
    xenomai_init(&argc, &argv);

    /* Tüm belleği kilitle — sayfa hatasını önle */
    mlockall(MCL_CURRENT | MCL_FUTURE);

    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, STACK_SIZE);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);

    param.sched_priority = 80;
    pthread_attr_setschedparam(&attr, &param);

    pthread_create(&thread, &attr, rt_loop, NULL);
    pthread_join(thread, NULL);
    return 0;
}

Priority Inheritance Protocol (PIP)

Xenomai POSIX skin'de mutex oluşturulurken protokol açıkça belirtilmelidir; aksi hâlde öncelik terslemesi (priority inversion) oluşabilir.

#include <pthread.h>

pthread_mutex_t rt_mutex;
pthread_mutexattr_t mattr;

void setup_rt_mutex(void)
{
    pthread_mutexattr_init(&mattr);

    /* Priority Inheritance protokolü */
    pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);

    /* Robust mutex — sahip thread ölürse kurtarılabilir */
    pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);

    /* Priority Protect (ceiling) alternatif:
     * pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_PROTECT);
     * pthread_mutexattr_setprioceiling(&mattr, 90);
     */

    pthread_mutex_init(&rt_mutex, &mattr);
    pthread_mutexattr_destroy(&mattr);
}

RT mesaj kuyruğu

#include <mqueue.h>

mqd_t create_rt_mqueue(const char *name)
{
    struct mq_attr attr = {
        .mq_flags   = 0,
        .mq_maxmsg  = 16,
        .mq_msgsize = 64,
        .mq_curmsgs = 0,
    };

    /* Cobalt POSIX mq: /cobalt/mqueue ad alanında */
    mqd_t mq = mq_open(name, O_CREAT | O_RDWR, 0666, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        return (mqd_t)-1;
    }
    return mq;
}

Mode switch kaçınma kuralları

printf / fprintfLinux stdio kullanır — mode switch. Bunun yerine Xenomai'nin rt_printf() veya serbest çalışmada log buffer kullanın
malloc / freeglibc heap — mode switch. RT thread başlamadan önce tüm belleği tahsis et, mlockall() ile kilitle
open / read / writeLinux VFS — mode switch. RT'de dosya I/O gerekiyorsa RTDM sürücüsü veya paylaşımlı bellek kullan
sem_openCobalt sem_open POSIX named semafor sağlar — mode switch yok. Linux semaphore.h ile karıştırma

04 Alchemy API

Alchemy API, VxWorks ve pSOS'tan alışkın geliştiriciler için daha sezgisel bir RT programlama modeli sunar; RT_TASK, RT_MUTEX, RT_SEM, RT_QUEUE gibi nesne türleri içerir.

RT_TASK oluşturma ve periyodik çalıştırma

#include <alchemy/task.h>
#include <alchemy/timer.h>

RT_TASK control_task;

void control_loop(void *arg)
{
    RTIME period = 1000000;  /* 1 ms, nanosaniye cinsinden */

    /* Periyodik göreve dönüştür */
    rt_task_set_periodic(NULL, TM_NOW, period);

    for (;;) {
        rt_task_wait_period(NULL);

        /* --- Kontrol algoritması --- */
        read_encoder();
        compute_pid();
        write_pwm();
    }
}

int main(void)
{
    /* RT_TASK oluştur: isim, yığın boyutu, öncelik, mod */
    rt_task_create(&control_task, "control", 0, 80, T_JOINABLE);

    /* Görevi başlat */
    rt_task_start(&control_task, control_loop, NULL);

    /* Ana thread Cobalt değil; rt_task_join ile bekle */
    rt_task_join(&control_task);
    return 0;
}

RT_MUTEX

#include <alchemy/mutex.h>

RT_MUTEX shared_mutex;

void init_resources(void)
{
    /* Mutex oluştur — priority inheritance otomatik */
    rt_mutex_create(&shared_mutex, "shared_mtx");
}

void producer_task(void *arg)
{
    for (;;) {
        rt_mutex_acquire(&shared_mutex, TM_INFINITE);

        update_shared_buffer();

        rt_mutex_release(&shared_mutex);

        rt_task_sleep(500000);  /* 500 µs */
    }
}

void cleanup(void)
{
    rt_mutex_delete(&shared_mutex);
}

RT_SEM ve RT_QUEUE

#include <alchemy/sem.h>
#include <alchemy/queue.h>

RT_SEM irq_sem;
RT_QUEUE data_queue;

/* Interrupt handler tarafından sinyal gönderilir */
void irq_handler_rt(void)
{
    rt_sem_v(&irq_sem);  /* semafor arttır */
}

void processing_task(void *arg)
{
    struct data_packet pkt;
    ssize_t sz;

    for (;;) {
        /* IRQ gelene kadar bekle */
        rt_sem_p(&irq_sem, TM_INFINITE);

        /* Kuyruğa veri gönder (non-blocking) */
        sz = rt_queue_write(&data_queue, &pkt, sizeof(pkt), Q_NORMAL);
        if (sz < 0)
            rt_printf("Queue full: %d\n", (int)sz);
    }
}

void init_ipc(void)
{
    rt_sem_create(&irq_sem, "irq_sem", 0, S_PRIO);
    /* 16 mesaj, her biri 128 byte */
    rt_queue_create(&data_queue, "data_q", 16 * 128, 16, Q_PRIO);
}

RT_EVENT — olay bayrakları

#include <alchemy/event.h>

RT_EVENT sys_events;

#define EVT_ENCODER_READY  0x01
#define EVT_ADC_READY      0x02
#define EVT_FAULT          0x80

void sensor_task(void *arg)
{
    for (;;) {
        read_encoder_hw();
        rt_event_signal(&sys_events, EVT_ENCODER_READY);
        rt_task_sleep(1000000);
    }
}

void fusion_task(void *arg)
{
    unsigned int flags;

    for (;;) {
        /* Her iki olay da gelene kadar bekle */
        rt_event_wait(&sys_events,
                      EVT_ENCODER_READY | EVT_ADC_READY,
                      &flags,
                      EV_ALL,       /* AND koşulu */
                      TM_INFINITE);

        compute_sensor_fusion();
    }
}

05 Latency testi

Xenomai kurulumunun doğrulanması için yerleşik xeno-latency aracı kullanılır; SMP sistemlerde her CPU çekirdeği ayrı ayrı test edilmelidir.

xeno-latency kullanımı

# Temel latency testi — 1000 µs periyot, 10000 örnek
xeno-latency -p 1000 -l 10000

# SMP: tüm CPU'ları test et (-s: SMP modu)
xeno-latency -s -p 1000 -l 50000

# Histogram çıktısı (her 1 µs'lik aralık için sayı)
xeno-latency -H 100 -p 1000 -l 100000 > latency_hist.txt

# Yük altında test: başka terminalde stress çalıştır
stress-ng --cpu 0 --io 4 --vm 2 --vm-bytes 256M &
xeno-latency -p 500 -l 100000

Örnek çıktı ve yorum

== Sampling period: 1000 us
== Test mode: periodic user-mode task
== All results in microseconds

warming up...
RTT|  00:00:01  (periodic user-mode task, 1000 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|      0.499|      0.612|      1.847|       0|     0|      0.499|      1.847
RTD|      0.501|      0.618|      2.103|       0|     0|      0.499|      2.103
RTD|      0.498|      0.609|      1.956|       0|     0|      0.498|      2.103
...
---|-----------|-----------|-----------|--------|------|-----------|----------
RTS|      0.498|      0.614|      3.201|       0|     0|      0.498|      3.201
lat min/avg/maxMinimum, ortalama ve maksimum gecikme (µs) — max değeri WCET için kritik
overrunPeriyot aşımı sayısı — sıfır olmalı; artıyorsa CPU yükü veya konfigürasyon hatası var
mswMode switch sayısı — sıfır olmalı; artıyorsa test programı Linux syscall çağırıyor

PREEMPT-RT ile karşılaştırmalı ölçüm

Ortamlat minlat avglat max (yük altında)
Vanilla Linux 6.15 µs25 µs800 µs+
PREEMPT-RT Linux 6.13 µs12 µs120 µs
Xenomai 3.2 (Cobalt)0.5 µs0.8 µs5 µs
Xenomai 3.2 + CPU izolasyon0.3 µs0.5 µs2 µs

CPU izolasyonu ile latency iyileştirme

# Kernel komut satırına ekle (grub veya device tree bootargs)
isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3

# RT görevi için izole CPU'yu belirle
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(2, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);

# İzole CPU'da IRQ affinity ayarı
echo 3 > /proc/irq/<irq_num>/smp_affinity_list

06 RTDM sürücü modeli

RTDM (Real-Time Driver Model), RT görevlerinden çağrılabilen çekirdek sürücüsü yazmanın standart Xenomai yoludur; mode switch olmadan donanım erişimi sağlar.

RTDM sürücü çatısı

#include <rtdm/driver.h>

struct my_rtdm_priv {
    rtdm_irq_t     irq_handle;
    rtdm_lock_t    lock;
    unsigned int   tx_count;
    unsigned int   rx_count;
};

/* Dosya işlemleri */
static int my_open(struct rtdm_fd *fd, int oflags)
{
    struct my_rtdm_priv *ctx = rtdm_fd_to_private(fd);
    rtdm_lock_init(&ctx->lock);
    ctx->tx_count = 0;
    ctx->rx_count = 0;
    return 0;
}

static void my_close(struct rtdm_fd *fd)
{
    /* kaynakları serbest bırak */
}

static ssize_t my_read_rt(struct rtdm_fd *fd,
                           void __user *buf, size_t size)
{
    struct my_rtdm_priv *ctx = rtdm_fd_to_private(fd);
    rtdm_lockctx_t lock_ctx;
    char kbuf[64];
    ssize_t ret;

    rtdm_lock_get_irqsave(&ctx->lock, lock_ctx);
    ret = read_hw_register(kbuf, size);
    ctx->rx_count += ret;
    rtdm_lock_put_irqrestore(&ctx->lock, lock_ctx);

    return rtdm_safe_copy_to_user(fd, buf, kbuf, ret);
}

static ssize_t my_write_rt(struct rtdm_fd *fd,
                            const void __user *buf, size_t size)
{
    struct my_rtdm_priv *ctx = rtdm_fd_to_private(fd);
    char kbuf[64];

    if (rtdm_safe_copy_from_user(fd, kbuf, buf, size))
        return -EFAULT;

    write_hw_register(kbuf, size);
    ctx->tx_count += size;
    return size;
}

/* IOCTL — RT bağlamından çağrılabilir */
static int my_ioctl_rt(struct rtdm_fd *fd,
                        unsigned int request, void __user *arg)
{
    switch (request) {
    case MY_IOC_RESET:
        reset_hw();
        return 0;
    default:
        return -ENOTTY;
    }
}

static struct rtdm_fd_ops my_fd_ops = {
    .open      = my_open,
    .close     = my_close,
    .read_rt   = my_read_rt,
    .write_rt  = my_write_rt,
    .ioctl_rt  = my_ioctl_rt,
};

static struct rtdm_driver my_driver = {
    .profile_info = RTDM_PROFILE_INFO(my_drv,
                        RTDM_CLASS_SERIAL, 0, 1),
    .device_flags  = RTDM_NAMED_DEVICE,
    .device_count  = 1,
    .context_size  = sizeof(struct my_rtdm_priv),
    .ops           = &my_fd_ops,
};

static struct rtdm_device my_device = {
    .driver = &my_driver,
    .label  = "my_rtdm%d",
};

static int __init my_rtdm_init(void)
{
    return rtdm_dev_register(&my_device);
}

static void __exit my_rtdm_exit(void)
{
    rtdm_dev_unregister(&my_device);
}

module_init(my_rtdm_init);
module_exit(my_rtdm_exit);
MODULE_LICENSE("GPL");

RTDM IRQ yönetimi

/* RT IRQ handler — OOB stage'de çalışır */
static int my_irq_handler(rtdm_irq_t *irq)
{
    struct my_rtdm_priv *ctx = rtdm_irq_get_arg(irq, struct my_rtdm_priv);

    /* Donanım kesme bayrağını temizle */
    clear_hw_irq();

    /* RT event sinyal gönder */
    rtdm_event_signal(&ctx->data_ready);

    return RTDM_IRQ_HANDLED;
}

/* IRQ'yu kaydet */
ret = rtdm_irq_request(&ctx->irq_handle,
                        platform_get_irq(pdev, 0),
                        my_irq_handler,
                        RTDM_IRQTYPE_EDGE,
                        "my_rtdm",
                        ctx);

07 Xenomai + CAN, SPI, UART

Xenomai, endüstriyel haberleşme protokolleri için özel RTDM sürücüler içerir; RTnet (Ethernet), XDDP (RT↔Linux köprü soket) ve platforma özgü CAN/SPI/UART sürücüleri bunların başında gelir.

XDDP: RT görev ↔ Linux süreci köprüsü

/* ---- Xenomai (RT) tarafı ---- */
#include <rtdm/ipc.h>

int rt_to_linux_bridge(void)
{
    struct sockaddr_ipc saddr;
    int sock, ret;
    char msg[] = "sensor_data";

    sock = socket(AF_RTIPC, SOCK_DGRAM, IPCPROTO_XDDP);

    /* RT tarafı: port 0'a bağlan */
    saddr.sipc_family = AF_RTIPC;
    saddr.sipc_port   = 0;
    bind(sock, (struct sockaddr *)&saddr, sizeof(saddr));

    /* Linux tarafına gönder (/dev/rtp0 üzerinden okunabilir) */
    sendto(sock, msg, sizeof(msg), 0, NULL, 0);

    close(sock);
    return 0;
}

/* ---- Linux (non-RT) tarafı ---- */
int linux_reader(void)
{
    int fd;
    char buf[64];

    fd = open("/dev/rtp0", O_RDONLY);
    read(fd, buf, sizeof(buf));   /* RT'den gelen veri */
    printf("Received: %s\n", buf);
    close(fd);
    return 0;
}

RT CAN sürücüsü (RTnet)

/* RTnet CAN socket — RT bağlamından kullanılabilir */
#include <rtdm/can.h>

int rt_can_init(int ifindex)
{
    struct sockaddr_can addr;
    int sock;

    sock = rt_dev_socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (sock < 0) return sock;

    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifindex;

    rt_dev_bind(sock, (struct sockaddr *)&addr, sizeof(addr));

    /* CAN frame gönder */
    struct can_frame frame = {
        .can_id  = 0x123,
        .can_dlc = 4,
        .data    = {0xDE, 0xAD, 0xBE, 0xEF},
    };
    rt_dev_send(sock, &frame, sizeof(frame), 0);

    rt_dev_close(sock);
    return 0;
}

RT SPI transferi

/* Xenomai SPI RTDM sürücüsü ile RT transferi */
#include <rtdm/spi.h>

int rt_spi_transfer(int fd)
{
    struct rtdm_spi_config cfg = {
        .speed_hz      = 10000000,   /* 10 MHz */
        .bits_per_word = 8,
        .mode          = SPI_MODE_0,
    };

    rtdm_ioctl(fd, SPI_RTIOC_SET_CONFIG, &cfg);

    uint8_t tx[] = {0x01, 0x02, 0x03, 0x04};
    uint8_t rx[4] = {0};

    struct rtdm_spi_iobufs bufs = {
        .io_len    = sizeof(tx),
        .i_offset  = 0,
        .o_offset  = sizeof(tx),
        .map_len   = sizeof(tx) * 2,
    };

    /* DMA eşlemeli çift tamponlu transfer */
    rtdm_ioctl(fd, SPI_RTIOC_SET_IOBUFS, &bufs);
    memcpy(bufs.tx_buf, tx, sizeof(tx));

    rtdm_write(fd, NULL, 0);  /* transferi başlat */
    memcpy(rx, bufs.rx_buf, sizeof(rx));

    return 0;
}

Endüstriyel kontrol döngüsü örneği

/* EtherCAT + Xenomai motor kontrol döngüsü (1 ms periyot) */
void ethercat_control_loop(void *arg)
{
    rt_task_set_periodic(NULL, TM_NOW, 1000000); /* 1 ms */

    for (;;) {
        rt_task_wait_period(NULL);

        /* 1. EtherCAT frame al (RTnet RTDM socket) */
        receive_ethercat_frame();

        /* 2. Encoder konumunu oku */
        int32_t pos = read_encoder_rtdm();

        /* 3. PID hesapla */
        int32_t torque = pid_compute(target_pos, pos);

        /* 4. Akım referansı yaz (SPI RTDM) */
        write_current_ref_spi(torque);

        /* 5. EtherCAT yanıt gönder */
        send_ethercat_response(pos, torque);
    }
}

08 Hata ayıklama

Xenomai, RT görevlerinin durumunu ve performansını izlemek için /proc dosya sistemi, xeno-config aracı ve GDB entegrasyonu sunar.

/proc/xenomai arayüzü

# Cobalt çekirdek versiyonu ve yapılandırma
cat /proc/xenomai/version
# Çıktı: Xenomai/cobalt v3.2.3

# Çalışan RT görevleri
cat /proc/xenomai/sched/threads
# PID  CPU  PRI  PERIOD  TIMEOUT  STAT  NAME
# 142    0   80  1000us       --   RUN  control
# 143    1   70  2000us       --  WAIT  sensor

# Gecikme istatistikleri (her görev için min/avg/max)
cat /proc/xenomai/sched/stat

# Mode switch sayaçları — sıfır olmalı
cat /proc/xenomai/stat | grep MSW

# Timer istatistikleri
cat /proc/xenomai/timer

xeno-config ile derleme bilgisi

# Kurulu Xenomai yapılandırması
xeno-config --version
xeno-config --prefix
xeno-config --skin=alchemy --cflags
xeno-config --skin=alchemy --ldflags

# SMP desteği var mı?
xeno-config --smp

# Desteklenen skin'ler
xeno-config --skins

Mode switch uyarıları

/* Geliştirme ortamında MSW yakalamak için sinyal işleyici */
#include <signal.h>
#include <xenomai/signal.h>

void msw_handler(int sig, siginfo_t *si, void *ctx)
{
    /* Bu handler Linux bağlamında çalışır */
    fprintf(stderr, "MODE SWITCH in PID %d! Check for Linux syscalls.\n",
            getpid());
    /* Stack trace için */
    // backtrace_symbols_fd(...)
}

void setup_msw_warn(void)
{
    struct sigaction sa = {
        .sa_sigaction = msw_handler,
        .sa_flags     = SA_SIGINFO,
    };
    sigaction(SIGDEBUG, &sa, NULL);

    /* MSW uyarısını etkinleştir */
    pthread_setmode_np(0, PTHREAD_WARNSW, NULL);
}

GDB ile Xenomai RT görevi hata ayıklama

# 1. Sembolle derle
aarch64-linux-gnu-gcc -g -O0 $CFLAGS -o rt_app_debug rt_app.c $LDFLAGS

# 2. Hedef üzerinde gdbserver başlat
gdbserver :1234 ./rt_app_debug

# 3. Çapraz GDB ile bağlan
aarch64-linux-gnu-gdb ./rt_app_debug
(gdb) target remote 192.168.1.100:1234
(gdb) set sysroot /path/to/rootfs
(gdb) break control_loop
(gdb) continue

# Not: GDB'nin breakpoint'i RT görevini durduracağı için
# zaman kısıtlamaları geçersiz olur — yalnızca mantık
# hatası ayıklamak için kullan, zamanlama analizi için değil

Yaygın sorunlar ve çözümleri

BelirtiOlası nedenÇözüm
MSW > 0RT threadden Linux syscallprintf, malloc, open kaldır; XDDP kullan
overrun artıyorCPU yükü, yanlış öncelikisolcpus, öncelik artır, periyot uzat
lat max spikeSMI, CPU frekans geçişiCPU_FREQ=n, SMI sayacını izle (rdmsr)
SIGXCPU alındıRT görev CPU quotasını aştısched_rt_runtime_us = -1 yap
cobalt modülü yüklenmiyorDovetail yaması eksik/uyumsuzkernel config'i kontrol et, yama sürümünü eşleştir

Performans izleme betiği

#!/bin/sh
# Xenomai RT sistem sağlık kontrolü

echo "=== Xenomai Versiyon ==="
cat /proc/xenomai/version

echo "=== Aktif RT Görevler ==="
cat /proc/xenomai/sched/threads

echo "=== Mode Switch Sayaçları ==="
cat /proc/xenomai/stat | awk 'NR==1 || $6 > 0'

echo "=== Timer Çözünürlüğü ==="
cat /proc/xenomai/timer | grep resolution

echo "=== CPU Izolasyon ==="
cat /sys/devices/system/cpu/isolated

echo "=== IRQ Affinities ==="
for irq in $(ls /proc/irq/); do
    aff=$(cat /proc/irq/$irq/smp_affinity_list 2>/dev/null)
    echo "IRQ $irq: $aff"
done