Linux Kernel Uzayında Yazılım Geliştirmek

Bu yazıyı yazarken belki yüzlerce Linux-Kernel geliştiricisi şu an github’a yüzbinlerce satır kodu “merge” ediyor.  Linux Kernel uzayında yazılım geliştirmenin iki yolu vardır.  Bunlardan ilki yazdığınız kodu kernel kaynak kodları arasına dahil ederek ve kerneli tekrar derleyerek geliştirmektir. İkincisi ise kernel’e enjekte edilebilen modüller şeklinde geliştirmektir.  Sürücüler Linux Kernel’e modül olarak da yazılılabildiği için genellikle sürücü geliştiren yazılımcılar sürücülerini ilk olarak modül şeklinde yazıyorlar.  Fakat tabi ki yazdığınız modülün kullanacağı donanımın  daha önceki yazılarımda anlattığım “Linux Device Tree” de düzgün tanımlanmış olması gerekiyor.  Daha sonra gerekirse kernel kaynak koduna dahil ediyorlar.  Modül nedir diye soracak olursanız da kernel uzayında çalıştırılması için kernele enjekte edilebilen kod parçaları olarak tanımlanıyor genellikle. Kernel uzayında program çalıştırmak bize donanıma direk erişim sağladığı için ön plana çıkıyor.  Yoksa zaten biz kullanıcı uzayında da istediğimz şekilde program geliştirebiliyoruz.

Linux kernel kaynak kodları içerisinde sürücü geliştirmek mi yoksa modül olarak geliştirmek mi daha avantajlıdır?,  diye tartışacak olursak bu duruma göre değişecektir. Öncelikle yazdığınız programı debug etmek kernel kaynak kodlarıyla çalıştığınızda oldukça zor oluyor.  Sürücünüzü modül olarak geliştirmekle kaynak kodlarına dahil ederek geliştirmek arasında yazım şekli ve geliştirme açısından bir fark yok. Fakat eğer yazacağınız programın modül olarak değil de sürekli kernelde yüklü olup servis şeklinde çalışmasını istiyorsanız o zaman kaynak kodlarına dahil etmeniz daha mantıklı olabilir.  Ve ya kernel’in generic olarak her zaman kullanacağı bir kod yazdıysanız kaynak kodlarına dahil etmek daha mantıklıdır.

Kernelde program geliştirmek için bilinmesi gereken önemli şeyler arasında kernel’in kendi fonksiyonları ve yazım şekilleri geliyor. Çünkü biliyorsunuz ki aslında C de yazılım geliştirirken tüm fonksiyonları kendimiz tanımlamıyoruz. Kullandığımız “printf()” fonksiyonu gibi çoğu fonksiyon C Standart kütüphanelerinden geliyor. Fakat kernelde C geliştirirken genellikle standart C kütüphaneleri kullanmıyoruz. Kernel’in kendi tanımlamış olduğu standart fonksiyonları kullanıyoruz. Örneğin libc içerisindeki “printf()” yerine kernelde tanımlanmış “printk()” fonksiyonunu kullanıyoruz. “printk()” fonksiyonu kernel’in kendi log fonksiyonudur.  Bu fonksiyon içerisine “priority” ye göre yazdığınız mesajlar /var/log/messages dosyasında görülür. Örneğin Kernelde “Hello World” programı şu şekildedir;

/*  
 *  hello-1.c - The simplest kernel module.
 */
#include <linux/module.h>	/* Needed by all modules */
#include <linux/kernel.h>	/* Needed for KERN_INFO */

int init_module(void)
{
  printk(KERN_INFO "Hello world 1.\n");

  /* 
   * A non 0 return means init_module failed; module can't be loaded. 
   */
  return 0;
}

void cleanup_module(void)
{
  printk(KERN_INFO "Goodbye world 1.\n");
}

Kernel modülleri en az iki fonksiyon içermek zorundadır. Bunlar modül yüklendiğinde çalışan fonksiyon “init_module” fonksiyonu ve modül sonlandırıldığında çalışan “cleanup_module” fonksiyonu.  Bu fonksiyonların yazım şekilleri kernel versiyonuna göre çeşitlilik gösterebiliyor. Bu yüzden kernel.org sayfasına bakarak ilerlemekte fayda var. Bu fonksiyonlar kernel’in çekirdeğinde bulunan ana fonksiyonlardır ve bunları tekrar yazarak çekirdekte bulunan aynı isimdeki ana fonksiyonları “override” ediyoruz. Bu sayede programın bizim modülümüze dallanmasını sağlıyoruz.  Bu fonksiyonlar gibi kernelde bir yığın operasyona göre “override” edilen fonksiyon vardır. Ve sürücüleri yazarken temelde bu tarzdaki ilgili fonksyionları baz alıyoruz.  Doğal olarak kernel uzayında yazılım geliştirmek için kernel fonksiyonlarını iyi bilmek gerekiyor. Kernel fonksiyonlarını öğrenmek için www.kernel.org sayfasına göz atarak öğrenebilirsiniz.

Kernel modülünü derlemek normal bir C programını derlemekten farklı olarak uymanız gereken  bazı prosedürlere tabidir. Eski kernel sürümleri genellikle “makefile” de belirtilen bu presedürlere dikkat edilmesine çok önem veriyor.  Fakat artık bu işleri yapmanın daha basit bir yolu var.  kbuild servisiyle artık modüller daha kolay derlenebiliyor.  “kbuild”  için  /linux/Documentation/kbuild/module.txt dosyasına göz atabilirsiniz. 

hello.c modülünü derlemek için şöyle basit bir makefile gerekiyor;

obj-m += hello.o

all:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

bu makefile ile “make” komutunu çalıştırarak hello.c kodunuzu modül olarak derleyebilirsiniz.  derlediğiniz modül /lib/modules/<kernel-sürümü>/build klasöründe .ko uzantısıyla görülecektir.  Ve modülü yüklerken de “insmod” komutunu kullanıyoruz.

  Linux’de “lsmod” komutunu kullanarak kernel’e yüklenmiş ve çalıştırılan modülleri görebilirsiniz.  Ayrıca bu modül isimleri /proc/modules dosyasında da yazılmaktadır.  Linux Kernel, kernelde bulunmayan bir özelliğe ya da bir sürücüye ihtiyaç duyduğunda, “kmod” ismindeki servis, modprobe ismindeki servisi çağırır ve modprobe modüllerin generic isimlerine göre /lib/modules klasöründeki ilgili modülü kernel’e yükler. Bu modüller “insmod <modül ismi>” komutuyla elle de kernel’e yüklenebilir.

Eğer yazdığınız modülü “insmod komutuyla yüklerken “versioning” tarzı hatalar alıyorsanız bilinki kernel konfigrasyonlarındaki “CONFIG_MODVERSIONS” işaretli değildir. Bu kanfigürasyonu ancak kernel’i derlerken konfigürasyon scripti ile yapabilirsiniz.

Kernel 2.4 ve sonraki sürümlerini kullanıyorsanız,  şöyle bir hata alabilirsiniz modülü yüklerken;

# insmod xxxxxx.o Warning: loading xxxxxx.ko will taint the kernel: no license See http://www.tux.org/lkml/#export-tainted for information about tainted modules Module xxxxxx loaded, with warnings

Bunun için modülünüze şu fonksiyonları eklemeniz gerekiyor;

* Get rid of taint message by declaring code as GPL. 
 */
MODULE_LICENSE("GPL");

/*
 * Or with defines, like this:
 */
MODULE_AUTHOR(DRIVER_AUTHOR);	/* Who wrote this module? */
MODULE_DESCRIPTION(DRIVER_DESC);	/* What does this module do */

/*  
 *  This module uses /dev/testdevice.  The MODULE_SUPPORTED_DEVICE macro might
 *  be used in the future to help automatic configuration of modules, but is 
 *  currently unused other than for documentation purposes.
 */
MODULE_SUPPORTED_DEVICE("testdevice");

Kernel 2.4 ve sonrasında, GPL kapsamında lisanslanan kodu tanımlayan bir mekanizma geliştirilmiştir, böylece insanlar kodun açık kaynak olmadığı konusunda uyarılabilir.  Bu, bir sonraki kod parçasında gösterilen MODULE_LICENSE () makrosu tarafından gerçekleştirilir. Lisansı GPL’ye ayarlayarak, uyarının yazdırılmasını önleyebilirsiniz. Bu lisans mekanizması linux / module.h dosyasında tanımlanmış ve belgelenmiştir

Kernelde Modül ya da sürücü yazmaya böyle bir giriş yaptıktan sonra bir sonraki yazım bunun ikinci bölümü niteliğinde daha gelişmiş modüller yazmak, yazım kuralları ve modül yazarken dikkat edilmesi gereken önemli konular hakkında olacak. Şimdilik sağlıcakla alın.

Yusuf

Yusuf

Bir Mühendis.

Önerilen makaleler

2 Yorum

  1. Avatar

    Dostum selam, ben de bir muhendis olarak yazdigin yaziyi okudum ve begendim ama bir elestide bulunmak istiyorum. Bu yazdigin yazi baska bir yerde kitapta bulunan kodlardan olusuyor. Orijinal olarak kendi kodunu yazmani tavsiye ederdim ama alinti yaptigin icinde seni elestirmiyorum, sadece elestirmek istedigim konu, madem bir yerden konuyu alip yaziyorsun aldigin kismida burada paylasman.

    https://sysprog21.github.io/lkmpg/#before-we-begin

    1. Yusuf

      Merhaba eleştri için teşekkür ederim. Bundan sonra bu konuda daha dikkatli olurum. Aslında bu kodu o zamanlar githubda bir sayfada bulmuştum ve çok yaygın bir kod örneği olduğu için de alıntı yapma gereği pek duymamıştım. Fakat dediğiniz gibi bu konuda hassas olmakda fayda var. Linki eklediğiniz için de ayrıca teşekkür ederim.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Translate »