Bir STM32 projesinde çalışma zamanı tamamen sizin elinizdedir: while(1) döngüsü, elle yazılmış bir görev zamanlayıcı ya da FreeRTOS ve USART’ın register haritası. Kart enerji aldığı ilk milisaniyede ne yapıyorsa, onu siz yazmışsınızdır. Gömülü Linux’lu bir SoC kartına geçtiğinizde bu netlik kaybolur: açılışın ilk 500 milisaniyesinde birbirine kontrol devreden beş ayrı yazılım katmanı vardır ve her biri farklı bir kaynaktan, farklı bir derleyiciyle üretilmiştir.
Bu yazının amacı o zinciri zihninizde kurmak: Gömülü Linux’un ne olduğunu (ve ne olmadığını), masaüstü dünyasından hangi varsayımların çöpe gittiğini ve sistemin dört mimari ayağının birbirine nasıl kenetlendiğini netleştirmek.
1. Gömülü Linux Gerçekte Nedir? (Ve Ne Değildir?)
1.1 Dağıtım Değil, Yazılım Yığını
Gömülü Linux, Ubuntu/Fedora/Debian gibi hazır bir dağıtım (distro) değildir. Dağıtımlar binlerce paketi, değişken donanımı ve çalışma zamanında paket kurulumunu yönetecek şekilde tasarlanmış genel amaçlı sistemlerdir. Gömülü tarafta tersini yaparsınız: hedef donanım sabit ve spesifiktir. Kernel yapılandırmasından C kütüphanesine, bootloader’dan init scriptlerine kadar her bileşeni o tek kart için kaynak koddan derlersiniz.
Çıktı, megabaytlarla ölçülen tek parça bir imajdır: sürümü, içeriği, boyutu ve güvenlik yaması stratejisi tamamen sizin kontrolünüzdedir. Bu yığını derleyen fabrika otomasyonları Buildroot veya Yocto, hedef kartın donanımına göre toolchain’i çalıştırır ve çıktı olarak tek bir imaj üretir:
u-boot.bin → bootloader
Image / zImage → Linux çekirdeği
board.dtb → donanım tanım blob'u (Device Tree)
rootfs.ext4 / rootfs.ubifs → kök dosya sistemi
1.2 Neden Tam Olarak Linux?
Kernel seçenekleri bitmiyor: Zephyr, FreeRTOS + TCP/IP yaması, RT-Thread… Bir ürünün Linux’a yönelmesinin somut teknik gerekçeleri şunlar:
- Sürücü ekosistemi: Mainline kernel’de binlerce hazır sürücü var. SoC üreticileri (NXP, TI, ST, Qualcomm, Rockchip) BSP’lerini ana hatta taşımak için ciddi kaynak harcıyor. Yeni bir Ethernet PHY, Wi-Fi çipi veya USB denetleyicisini entegre etmek bare-metal’de aylar süren bir işken, Linux’ta çoğu zaman “kernel config’i açtım, çalışıyor” ile biter.
- Ağ protokolü zenginliği: Olgun TCP/IP yığını, VLAN, netfilter (firewall), IPSec/WireGuard, Zeroconf… Router’lar, endüstriyel gateway’ler ve IoT hub’larının çekirdek gereksinimi budur; yeniden yazılmaz.
- Çoklu işlem + bellek koruması: MMU sayesinde her uygulama kendi izole adres uzayında çalışır. Bir süreç çöktüğünde sistem ayakta kalır. 7/24 çalışan kiosk, HVAC denetleyicisi veya tıbbi cihaz için bu fark kritiktir.
- Lisans ve maliyet: Kernel GPLv2, telif ücreti yok. Kullanıcı uygulamaları (kernel modülü olmadığı sürece) kapalı kaynak olabilir.
- Yetenek havuzu: POSIX API’sini bilen her sistem programcısı üründe görev alabilir; platforma özel API öğrenme maliyeti yoktur.
Bu gerekçeler yüzünden otomotiv (IVI, domain controller), savunma sanayii, telekom ekipmanları, endüstriyel gateway’ler, akıllı sayaçlar ve digital signage ürünlerinde aynı yığın koşar. Donanım tarafında da tablo netleşiyor: NXP i.MX, TI Sitara, STM32MP1, Rockchip ve Allwindger SoC’leri bu ekosistemin standart parçalarıdır.
⚠️ Önemli Not — Android nerede? Android de Linux kernel çalıştırır; ancak Bionic libc, kendi init’i ve framework’üyle bambaşka bir yığındır. “Gömülü Linux” denince anılan derleme-zamanı yığın (Yocto/Buildroot) ile AOSP dünyası ayrı uzmanlık alanlarıdır. Kernel ortak, kullanıcı alanı apayrıdır.
2. Masaüstü Linux vs Gömülü Linux: Mimari Farklar
Aynı kernel isminden bahsediyoruz, ama sistem iki dünyada bambaşka yerlerden besleniyor:
| Kriter | Masaüstü Linux (x86_64) | Gömülü Linux (ARM / RISC-V) |
|---|---|---|
| Donanım keşfi | ACPI tabloları + PCI/USB çalışma zamanı enumerasyonu (Plug & Play) | Device Tree (.dts → .dtb): donanım derleme zamanında statik tanımlanır |
| Başlatma zinciri | Reset vector → UEFI (SPI flash) → GRUB → Kernel | Boot ROM → SPL (SRAM) → Das U-Boot (DDR) → Kernel |
| Paket yönetimi | apt / dnf / pacman: depodan çalışma zamanı kurulumu | Buildroot/Yocto ile tüm imaj tek seferde kaynak koddan derlenir; çoğu cihazda paket yöneticisi yoktur |
| Depolama | SATA/NVMe SSD, yüzlerce GB, blok cihaz | eMMC, Raw NAND (MTD/UBI), SPI-NOR, SD; 8–64 MB yaygın |
| Dosya sistemleri | ext4, XFS, Btrfs (okunur-yazılır) | SquashFS (salt-okunur + sıkıştırılmış rootfs), UBIFS/JFFS2, ext4 (veri bölümü) |
| RAM / boyut | 4–64 GB RAM, TB’larca disk | 32–512 MB RAM; tüm sistem çoğu zaman < 100 MB |
| C kütüphanesi | Glibc | Musl veya uClibc-ng |
| Kullanıcı alanı | systemd + GNU Coreutils | BusyBox (init + ~300 applet, tek binary) + minimal init scriptleri |
| Güncelleme stratejisi | Paket bazlı, sürekli güncelleme | A/B imaj geçişi (RAUC, Mender, SWUpdate), atomik OTA |
2.1 Donanım Keşfi: ACPI + PCI vs Device Tree
Masaüstü x86 dünyasında donanım dinamik keşfedilir: UEFI, ACPI tablolarını (DSDT/SSDT) kernel’e sunar; PCI/PCIe veri yolları açılışta enumerate edilir. Kernel çalışma zamanında “şu adreste şu cihaz varmış” diye öğrenir, milyonlarca donanım kombinasyonunu desteklemenin tek yolu budur.
Gömülü SoC’de bu model geçersizdir. Çevre birimler PCIe kart gibi takılıp sökülmez; çip tasarımı sırasında fiziksel adreslere bağlanmış IP bloklarıdır. UART’ın hangi adreste olduğu, hangi kesme hattına bağlı olduğu ve hangi pinlere muxlandığı üretim aşamasında silikona yazılmıştır. Kernel’in “keşfedeceği” bir veri yolu yoktur, bu sabit bilgi bir veri dosyasına yazılır: “Device Tree.”
Kaynağı .dts olan bu dosya, dtc (Device Tree Compiler) ile .dtb binary’sine derlenir ve açılış sırasında kernel’e fiziksel adres olarak verilir. Kernel her düğümdeki compatible string’ini, derlenmiş sürücü tablolarıyla eşleştirir ve eşleşen sürücüyü probe eder.
Bu ayrımın getirisi büyüktür: tek kernel binary + farklı DTB = aynı çekirdek onlarca kartı destekler. Donanım tanımı ile sürücü kodunun ayrıştırılması, gömülü Linux’un en önemli tasarım fikridir. Board’a özel eklemeler overlay (.dtbo) dosyalarıyla yapılır; U-Boot da devretmeden önce DTB’nin /chosen ve /memory düğümlerine boot argümanları ve RAM boyutu gibi bilgileri işleyebilir.
2.2 Depolama: Blok Cihaz Dünyasından MTD’ye
Masaüstünde her disk bir blok cihazdır (/dev/nvme0n1); üzerine ext4 koyarsınız ve biter. Gömülü tarafta depolama medyası, dosya sistemi seçimini doğrudan belirler:
- eMMC: İçinde NAND flash + bir FTL (flash translation layer) barındıran yönetimli çip. Wear-leveling ve ECC’yi kendi denetleyicisi halleder; sisteme
/dev/mmcblkXblok cihazı olarak görünür. ext4 rahatça kullanılır. - Raw NAND: Yönetim yoktur. Kötü blok (bad block) yönetimi, ECC hesabı ve wear-leveling’i yazılım yapmak zorundadır. Bunun için Linux’ta MTD alt sistemi + üstünde UBI katmanı ve UBIFS dosya sistemi kullanılır.
/dev/mtdXcihazları blok değil, erase-block tabanlı karakter cihazlarıdır. - SPI-NOR: 4–64 MB’lık küçük, güvenilir medya. Bootloader ve DTB gibi kritik küçük imajların evi; MTD üzerinden erişilir.
- SquashFS: Sıkıştırılmış ve salt-okunur. Rootfs’i değiştirilemez kılmak, rastgele yazma bozulmasına ve yarım kalmış güncellemeye karşı doğal bir savunmadır. Üretim desenleri (A/B bölüm + atomik geçiş: RAUC, Mender, SWUpdate) bu yapı üzerine kuruludur.
2.3 Kullanıcı Alanı: Glibc + systemd vs Musl + BusyBox
Masaüstü kullanıcı alanı, GB’larca diskle çalışmayı varsayar: glibc, systemd ve GNU Coreutils birlikte onlarca-yüzlerce MB tutar. Gömülü yığında üç değişim olur:
- C kütüphanesi: Glibc yerine musl (MIT lisanslı, ~1 MB altı, statik derlemeye çok uygun) veya uClibc-ng kullanılır.
- Kullanıcı araçları: Tüm temel Unix araçları:
ls,cp,mount,ifconfig,sh, hattainit.. tek bir binary’de birleşir: BusyBox. ~300 applet, binary’ye symlink’lerle kişilik kazandırılır:/bin/ls→busybox. - init: BusyBox init,
/etc/inittabdosyasını okuyup sistem kurar.
3. Gömülü Linux’un Dört Mimari Ayağı
Sistemin tamamı dört parçadan oluşur. Her birinin görevi, tipik temsilcisi ve hedef donanımdaki izi:
| Ayağ | Görevi | Tipik temsilci | Hedefteki izi |
|---|---|---|---|
| 1. Toolchain | Host’ta target için kod üretir | aarch64-linux-gnu- (GCC/LLVM) + binutils + C kütüphanesi | Doğrudan yok; diğer üçünü o üretir |
| 2. Bootloader | Donanımı uyandırır, kernel’i yükler | Das U-Boot (+SPL) | SPI-NOR/eMMC’nin ilk bölümlerinde (~100 KB SPL + ~500 KB U-Boot) |
| 3. Kernel | Süreç, bellek, sürücü, ağ yönetimi | Mainline Linux + kart DTS | Boot bölümünde Image/zImage + .dtb |
| 4. Rootfs | / dizini + tüm kullanıcı alanı | BusyBox + musl (+ sizin uygulamanız) | eMMC/SD’nin veri bölümünde (2–100 MB) |
3.1 Toolchain: Hedef Makinede Çalışmayan Derleyici
Sorun basit: Host’unuz x86_64, hedefiniz ARM64 — farklı komut setleri. Derleyici, kendi çalıştıramadığı kodu üretmek zorundadır. Buna çapraz derleme (cross-compilation) denir ve tüm yığının temelidir.
Toolchain üç bileşenden oluşur:
- binutils:
as(assembler),ld(linker),objcopy,readelf,strip— düşük seviyeli ikili üretim araçları. - Derleyici: GCC veya Clang front-end’leri.
- C kütüphanesi: Syscall’ları POSIX API’sine saran katman —
printf→write,malloc→mmap/brk. Glibc (tam donanımlı, büyük) veya musl (minimal, statik-dostu) seçilir.
İsimlendirme konvansiyonu (triplet) her şeyi anlatır: aarch64-linux-gnu = aarch64 mimarisi + Linux ABI + glibc. arm-linux-gnueabihf içindeki hf, hard-float ABI’sidir (fonksiyon parametreleri FPU register’larıyla geçilir). Toolchain’in --sysroot‘u, hedefin başlık dosyalarını (headers) ve kütüphanelerini barındırır.
3.2 Bootloader: Das U-Boot
U-Boot’un iş tanımı dört maddedir:
- Donanımı uyandırmak: Saat ağacı, PMIC ve DDR denetleyicisi dahil minimum donanımı çalışır hale getirmek.
- İmajı yüklemek: Kernel (
Image/zImage) ve DTB’yi bir kaynaktan (eMMC, SPI-NOR, SD, USB, TFTP) RAM’e çekmek. - Argümanları işlemek:
bootargs(kernel komut satırı) bilgisini DTB’nin/chosendüğümüne yazmak. - Devretmek: Kontrolü kernel’e bırakmak — bir daha geri dönmemek üzere.
3.3 Linux Kernel
Kernel’in sorumluluk dört ana başlıktır:
- Süreç yönetimi: Zamanlayıcı (scheduler), syscall arayüzü, sinyaller, PID yönetimi.
- Bellek yönetimi: Page table’lar, fiziksel sayfa tahsisi (SLUB), demand paging, OOM politikası. Gömülüde swap yoktur — bellek biterse OOM killer devreye girer; bu yüzden bellek bütçesi tasarımın bir parçasıdır, tesadüf değil.
- Sürücüler + Device Tree: DTB’deki düğümlerle
compatibleeşleşmesi sonucu sürücüler probe edilir; GPIO, I2C, SPI, Ethernet MAC gibi platform cihazları bu sayede hayata geçer. - VFS + ağ yığını:
root=ile gelen dosya sisteminin mount’u, soket arayüzü, netfilter.
İmaj formatları da pratik bilgi: ARM32’de kendi sıkıştırmasını açan zImage; ARM64’te düz Image; üretimde sık sık kernel + DTB + ramdisk’i tek, imzalanmış pakette birleştiren FIT image (secure boot zincirinin parçası).
3.4 Root Filesystem (Rootfs)
Kernel açılışın sonunda root= parametresindeki dosya sistemini / olarak mount eder ve ilk kullanıcı alanı sürecini başlatır: /sbin/init (PID 1). Bu dosya yoksa kernel anında panic verir: “No working init found.” PID 1, tüm proseslerin atasıdır; yetimleri toplar, açılış scriptlerini çalıştırır ve kapanışı yönetir.
Rootfs’i kim derler? İki ana fabrika:
| Buildroot | Yocto / OpenEmbedded | |
|---|---|---|
| Model | make menuconfig → kaynak indirir → toolchain + kernel + rootfs’i tek seferde derler | BitBake + recipe’ler; katman (layer) mimarisi |
| Çıktı | zImage + rootfs.ext4 + u-boot.bin + dtb | İmaj + RPM/IPK paket beslemesi + SDK |
| Öğrenme eğrisi | Düşük, hızlı ilk sonuç | Dik, ciddi yatırımla geri döner |
| Uygun olduğu | Tek ürün, sade cihazlar, hızlı prototip | Çok ürünlü portföy, uzun yaşam döngüsü, lisans/sertifikasyon yükümlülükleri |
3.5 Handoff
Dört ayağın birbirine kenetlendiği üç kritik arayüz vardır — sistem tam olarak bu sözleşmeler üzerinde durur:
- Toolchain → (U-Boot, Kernel, Rootfs): Tek ABI, tek C kütüphanesi. Yığın homojen derlenmezse çalışmaz.
- U-Boot → Kernel: Register sözleşmesi (
x0= DTB fiziksel adresi, ARM64) +bootargs‘ın/chosendüğümüne işlenmesi. - Kernel → Rootfs:
root=parametresiyle mount, ardından/sbin/init‘in PID 1 olarak çalıştırılması.
Bir sonraki bölümde bu devir teslimlerin zamansal sırasını göreceğiz.
4. Donanımın Açılış Akışı: Power-On’dan Kullanıcı Uygulamasına
Güç Uygulaması / Reset
│
▼
┌─────────────────────────────────────────────────────┐
│ 1. BOOT ROM [SoC mask ROM — değiştirilemez]
│ • Boot mod pinleri / eFuse okunur │
│ • Geçerli imaj SD / eMMC / SPI-NOR / USB │
│ kaynaklarından aranır │
│ • İlk stage yazılımı → Internal SRAM'e yüklenir │
└──────────────────────────┬──────────────────────────┘
│ kontrol devri
▼
┌─────────────────────────────────────────────────────┐
│ 2. SPL / TF-A [Internal SRAM, ~128–512 KB]
│ • Saat (clock) ve PMIC yapılandırması │
│ • DDR denetleyicisi init + training │
│ • Ana Bootloader'ı (U-Boot) DDR'ye yükler │
└──────────────────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ 3. U-Boot (proper) [DDR RAM] │
│ • Ortam değişkenleri (env), storage/net sürücü- │
│ leri, komut kabuğu │
│ • Kernel Image + DTB'yi belleğe yükler │
│ • booti/bootz: kontrolü kernel'e devreder │
│ (ARM64: x0 = DTB fiziksel adresi) │
└──────────────────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ 4. LINUX KERNEL [DDR RAM] │
│ • MMU açılır, page table'lar kurulur │
│ • Device Tree ayrıştırılır, sürücüler probe │
│ • root= ile Rootfs mount edilir │
│ • /sbin/init (PID 1) çalıştırılır │
└──────────────────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ 5. ROOTFS / init [eMMC / SD / NFS] │
│ • inittab → init scriptleri / systemd │
│ • Servisler, ağ, mount'lar │
└──────────────────────────┬──────────────────────────┘
▼
Kullanıcı Uygulaması
Neden Üç Aşamalı Bootloader?
Bu merdivenin var olma sebebi, gömülü sistemlerin klasik tavuk-yumurta problemidir:
- Reset anında kullanıma hazır tek bellek, SoC’nin iç SRAM’idir — ancak boyutu 128–512 KB ile sınırlıdır.
- DDR RAM büyüktür, ama denetleyicisi (saat, zamanlama parametreleri, training) yazılım tarafından önce yapılandırılmalıdır. Kod henüz DDR’de yokken, DDR init eden kodun nerede çalışacağı sorusunun cevabı: SRAM’de, yani küçük bir ilk aşama — SPL.
- Boot ROM yalnızca sınırlı boyutta imajı SRAM’e yükleyebilir; bu yüzden zincir kırılma noktalarında küçük adımlarla büyür.