Linux Çekirdek Modülü Programlama Rehberi
(sysprog21.github.io)- Linux Kernel Module Programming Guide, Linux v5.10 ve üzeri için yüklenebilir çekirdek modülleri oluşturmaya yönelik ücretsiz bir rehberdir; geliştirme ortamından derleme, yükleme, hata ayıklama ve başlıca çekirdek arayüzlerine kadar süreci uçtan uca ele alır
- İlk örnekler
hello-*.cüzerindenmodule_init(),module_exit(),kbuild,insmod,rmmod,dmesgkullanımını öğretir; QEMU tabanlı devtools ile ana sistemin zarar görme riskini azaltır - Çekirdek modülleri çekirdek adres alanında çalıştığından hatalı işaretçiler, unload sırası, eşzamanlılık ve kullanıcı belleği kopyalama hataları çekirdek belleği bozulmasına veya sistem kararsızlığına yol açabilir
- Karakter aygıtları,
/proc,seq_file, threaded IRQ, input, PCI, USB, blok, ağ, Device Model, Device Tree ve static key’e kadar genişler; kayıt–kaldırma sırası ve lifetime yönetimini tekrar tekrar işler - Çekirdek içi API’ler sürüme göre değiştiği için
LINUX_VERSION_CODE,KERNEL_VERSION,CONFIG_MODVERSIONS, SecureBoot imzası ve version magic gibi koşulları kontrol eder; örnekler de koşullu derleme içerir
Rehberin yapısı ve temel akış
- Bu rehber, GitHub deposu ve PDF belgesi sunan bir çekirdek modülü öğrenme kaynağıdır; Open Software License 3.0 koşullarıyla kopyalanabilir, değiştirilebilir ve dağıtılabilir
- Mevcut rehber Linux v5.10’u asgari destek ölçütü olarak alır ve uzun süreli destek çekirdekleri genelinde örneklerin ve yönergelerin uyumluluğunu korumayı hedefler
- Öğrenenlerin C dili ve genel proseslere yönelik program yazma deneyimine sahip olması gerekir; çekirdek modülleri dinamik olarak yüklenip kaldırılarak yeniden başlatma gerektirmeden çekirdek işlevlerini genişletir
- Temel geliştirme akışı; çekirdek header’larının kurulması,
makeile.koderlenmesi,modinfokontrolü,insmodile yükleme,dmesgveyajournalctl -kile günlüklerin incelenmesi vermmodile unload adımlarından oluşur devtools/, çekirdek kaynaklarını ve BusyBox kök dosya sistemini derleyip QEMU’da boot eder;examples/dizinini 9p virtfs ile paylaşarak modüllerin konuk sistemde test edilmesini sağlar- Modül başlatma ve temizleme için
module_init()·module_exit()kullanımı tercih edilir; eskiinit_module()·cleanup_module()yöntemi, x86 IBT’nin etkin olduğu 6.15 sonrası çekirdeklerde bazı koşullarda derleme hatasına neden olabilir - Çekirdek modülleri
printf()veya libc kullanmaz, yalnızca çekirdeğin export ettiği sembolleri kullanabilir; çıktı terminale değil çekirdek log ring buffer’ına gider - Kullanıcı alanı ile çekirdek alanı arasında veri taşımak için
put_user,get_user,copy_to_user,copy_from_usergibi özel fonksiyonlar gerekir - Karakter aygıtı örneği
register_chrdev,file_operations, dinamik major number,/devnode’u oluşturma, exclusive open,put_usertabanlı read ve desteklenmeyen write işlemesini gösterir /procörneğiproc_create,proc_ops, read/write callback’leri veseq_fileAPI’sini ele alır; Linux v5.6’dan sonra/prochandler’larındafile_operationsyerineproc_opsgetirilen değişikliği yansıtır- threaded IRQ,
request_threaded_irq()ile top-half ve bottom-half’i ayırır; top-half interrupt context’te yalnızca asgari işi yapar veIRQ_WAKE_THREADile thread tabanlı bottom-half’i uyandırır - Sonraki bölümler input, PCI, USB, blok, ağ, Device Model ve Device Tree gibi gerçek sürücü alanlarına genişler; her subsystem’in kayıt yöntemi ve userspace ABI seçimi odağında ilerler
- Optimizasyon ve güvenlik bölümü
likely·unlikely, static key, küçük çekirdek stack’i, FPU kullanım yasağı, başlatılmamış padding sızıntıları ve double-underscore iç API kullanımına dikkat edilmesini işler
Derleme ve yüklemede ilk karşılaşılan kısıtlar
- Bir çekirdek için derlenen modül başka bir çekirdekte yüklenmeyebilir; version magic ve
CONFIG_MODVERSIONSeşleşmezseInvalid module formatveya sembol sürümü uyuşmazlığı oluşur - Çoğu genel Linux dağıtımı çekirdeğinde modversioning etkin olabilir; örnekler doğrudan çalışmazsa modversioning kapalı bir çekirdek veya QEMU ortamı düşünülmelidir
- SecureBoot açık sistemlerde imzasız modül yükleme kısıtlanabilir;
Lockdown: insmod: unsigned module loading is restrictedgörülürse SecureBoot’u devre dışı bırakmak veya modül imzalama süreci gerekir
QEMU tabanlı pratik ortamı
devtools/setup.sh, çekirdek tarball’ını ve BusyBox’ı indirip derler ve initramfs’i paketlerdevtools/build-modules.sh, QEMU çekirdeği hedefli modülleri derler;devtools/boot.shkonuk shell’i sağlar;devtools/test-modules.shise modül bazındainsmod·rmmodotomatik testleri yürütür- GDB ile hata ayıklama,
LKMPG_NO_PREBUILT=1 devtools/setup.shilevmlinuxderlendikten sonradevtools/boot.sh --gdbve uzak GDB bağlantısıyla yapılır
Çekirdek kodu yazma kuralları
- init fonksiyonunda kayıt ve ayırma işlemleri başarısız olabileceğinden, edinilen kaynaklar
gototabanlı hata yollarında ters sırayla serbest bırakılmalıdır - Çekirdeğe bir callback yapısı kaydedildiğinde kullanıcı alanı init dönüşünden önce bile callback’i çağırabileceği için, iç başlatmayı bitirdikten sonra en sonda kayıt yapmak ve kaldırmayı önce yapmak anlamındaki register last, unregister first kuralı önemlidir
- process context, softirq/tasklet context ve hardirq context; sleep, kullanıcı belleğine erişim,
GFP_KERNELve mutex kullanımının mümkün olup olmaması açısından farklıdır; bu ayrımı yanlış anlamak yaygın çekirdek hatalarına yol açar
Aygıtlar ve subsystem’lere göre dikkat noktaları
- Karakter aygıtları sürücüyü major number ile, sürücü içindeki birden çok aygıtı ise minor number ile ayırt eder; modern yöntemde
register_chrdev()yerinecdevarayüzü önerilir - PCI sürücüleri sabit adres varsaymaz; PCI core tarafından enumerate edilen BAR resource’larını map eder. Linux 5.10 sonrası kodlarda
pcim_enable_device()ve device-managed resource API, teardown hatalarını azaltmada kullanışlıdır - USB sürücüleri hotplug ve disconnect’i normal olaylar olarak ele almalıdır; URB completion ile disconnect, timeout, suspend ve userspace shutdown’ın yarışabileceği varsayımıyla tasarlanmalıdır
- Blok sürücüleri
blk-mq,request,gendisk, queue limit ve flush/FUA semantics etrafında çalışır; basit read/write callback’i değil asenkron request completion modeline katılır - Ağ sürücüleri
struct net_device,net_device_ops,sk_buff, NAPI, offload feature flag’leri ve link-state raporlamasıyla birleşir; hatalı offload beyanı trafik bozulmasına yol açabilir
Çekirdek sürümü değişikliklerine uyum
- Örnekler; Linux 6.4’teki
class_create()imza değişikliğini, Linux v5.6’dakiproc_ops’u, Linux 6.11’dekiremove()dönüş tipi değişikliğini ve Linux 5.15–6.9 arasındakiblk-mqhelper değişikliklerini koşullu derlemeyle ele alır - Çekirdek içi arayüzler sistem çağrılarından daha sık değiştiği için, birden çok çekirdeği destekleyen modüllerde
LINUX_VERSION_CODEveKERNEL_VERSIONkarşılaştırmalarından kaçınmak zordur
Güvenlik kontrol noktaları
- Çekirdek stack’i kullanıcı alanı stack’inden çok daha küçüktür ve birçok sistemde 8 KiB veya 16 KiB düzeyinde olabilir; büyük diziler için
kmalloc()·kzalloc()kullanılmalıdır copy_to_user()ile kullanıcı alanına veri gönderirken padding dahil tüm byte’ların başlatılmış olması gerekir; aksi halde çekirdek belleği bilgi sızıntısı oluşabilir__kmalloc(),__list_add()gibi double underscore ile başlayan API’ler iç önkoşullar varsayabilir; dokümantasyon gerektirmedikçe önce public wrapper kullanılmalıdır
Kapsam dışında bırakılanlar
- Girdi işleme sırasında bazı özgün metin parçalarının uzunluk ve maliyet sınırları nedeniyle atlandığı belirtilmiştir; bu nedenle bu özet rehberin tamamındaki tüm bölümleri, örnekleri ve kod yollarını eksiksiz kapsamaz
1 yorum
Hacker News yorumları
QEMU, kernel hacklemeyi deneyimlemek için iyi bir yöntem
Birilerinin LDD(Linux Device Drivers) ve Linux kernel kitaplarını güncellemesi güzel olurdu; bu tür teknik kitaplardan gelir elde etmek zor olduğu için Linux Foundation’ın sponsor olması da iyi olabilir
Bu hafta da v6.8’de arm64 kernel komut satırı parametresi 146 karakterden uzunsa kernelin anında ve sessizce durması sorununu QEMU + GDB ile yeniden ürettim; Debian 12 Bookworm amd64 host üzerinde arm64 kernel derlemesini emüle edip sorunlu kodu satır satır izleyerek nedeni buldum
Akış, derleme bağımlılıkları ve çapraz derleme araçlarının hazır olduğu bir ortamda arm64 kernel imajını ve GDB için scriptleri derlemek; host’a
gdb, gerekirsegdb-multiarchveqemu-system-armkurmak; ardındanqemu-system-aarch64ı-S -gdb tcp::1234ile durdurulmuş durumda başlatıp başka bir terminaldengdb-multiarch ./vmlinuxile bağlanmak şeklindeSonrasında GDB’de
target remote :1234,break __parse_cmdline,continueçalıştırılırsa bellek, değişken ve stack inceleme ile tek adım yürütme gibi normal GDB özellikleri kullanılabiliyorKernel GDB hata ayıklama ve
lx-*scriptleri için bkz. https://www.kernel.org/doc/html/latest/dev-tools/gdb-kernel-...GDB’nin
lx-*Python scriptlerini kullanabilmesi için geneldeecho "add-auto-load-safe-path ${SRC_DIR}/scripts/gdb/vmlinux-gdb.py" > ~/.gdbinitgibi path’e izin vermek de gerekiyorİlgili HN başlıkları: https://news.ycombinator.com/item?id=35782630, https://news.ycombinator.com/item?id=28283030
The Linux Memory Manager da bakmaya değer: https://linuxmemory.org/chapters
Yazarın temmuz başında gönderdiği son güncellemeye göre ilk taslağı bitirmiş ve şu anda yayıneviyle birlikte editörlük aşamasına geçmiş
Bazı örnekleri doğrudan çalıştırmak zor görünüyor
Örneğin “Detecting button presses”, RPi için modül derlenebildiğini varsayıyor; bu da başlı başına çapraz derleme gibi işler gerektirdiğinden basit olmayabilir
Ayrıntılı, pratik odaklı ve doğrudan kernel modülü derlemeyi deneten bir öğretici olduğu için harika
Birlikte bakılabilecek kaynak: https://0xax.gitbooks.io/linux-insides/content/index.html
Dosya sistemi veya bellek yönetimi gibi genel Linux kernel programlama konuları için nereye bakmanın iyi olacağını merak ediyorum
Eskiden Robert Love’ın “Linux Kernel Development” kitabı vardı, ama artık güncellenmiyor gibi
Bunu ilk okuduğumda yaklaşık 22 yıl önceydi :)