18 Mart 2011 Cuma

Linux Sürücü Yazma-1


Bu yazı daha önce saltokunur.org e-dergisinde yayınlanmıştır. 

Herkese merhabalar. Bu ay Linux'ta donanım sürücülerini inceleyeceğiz ve bir sürücü yazmaya çalışacağız. Konu detaylı ve uzun olduğundan makaleyi bir yazı dizisi şeklinde yayınlamayı düşünüyorum. Yolumuz uzun olduğundan hemen başlayalım. Ne demişler: "Erken kalkan yol alır."
Elimde çok sevdiğim, kullanışlı, güzel bir farem var (A4Tech Power Saver R7). Farenin sevdiğim özelliklerinden birisi de üzerinde fazladan tuşlarının olması ve Windows sürücü yazılımıyla bu tuşlara çeşitli kısayollar atanabilmesi. Bu kısayollar sayesinde fare bilgisayar deneyiminizi çok eğlenceli hale getiriyor. Ancak ne yazık ki bu farenin bir Linux sürücüsü bulunmuyor. Bu yazı dizisinde bahsettiğim fazlalık tuşlara Linux altında işlevlik kazandırmak için sürücü yazmaya çalışacağız.

 
Sürücü yazmaya girişmeden önce bazı önemli Linux Kernel (Çekirdek) özelliklerinden ve terimlerinde bahsedeceğiz. Sonra bunları bir USB Fare sürücüsü yazmada kullanacağız.
Linux'ta sürücüler genelde iki ayrı yöntem izlenerek yazılıyor. Birincisi Linux çekirdeğine inip çekirdek seviyesinde kodlar yazıp, bu kodları çekirdeğe gömmek. İkincisi ise çeşitli 3. parti API'ler kullanarak sürücü yazmak. Aslında bu 3. parti API'ler de yine çekirdek programlamayla yazılmış biraz daha üst seviye C fonksiyonları. Standart C'yi kullanmaya izin verdiği için kodlanması ve baş edilmesi daha kolay. Şimdi aklınıza "Yoksa Kernel kodları Standart C'yi kullanmıyor mu?" diye bir soru gelebilir. O zaman Linux Kernel'a Giriş - 101 'e hoş geldiniz.

Linux Kernel'a Giriş - 101
Kernel bir işletim sisteminin çekirdeğidir (Gerçekten çok açıklayıcı oldu :) Sistemde çalışan programların ve proseslerin önceliğini, çalışma sırasını, bellek erişimini ve belleğin prosesler arasında nasıl paylaşılacağını, sisteme gelen kesmeleri ve bu kesmelere göre işlemcinin nasıl davranacağını, dosya sistemine erişimi ve dosyaların burada nasıl tutulacağını, bilgisayarın donanımla nasıl haberleşeceğini belirlediği giriş-çıkış portlarını yönetir -ki biz bu konuya değineceğiz.
Linux çekirdeği ilk olarak 1991 tarihinde Finlandiyalı bir üniversite öğrencisi olan Linus Torvalds tarafından, o zamanın önemli işletim sistemlerinde Minix'in eksikliklerini tamamlamak için yayınlanmıştır. İlk olarak Intel 80386 mikroişlemcisi için yazılmıştır. Bugün içlerinde AMD, ARM, Compaq Alfha, CRIS, DECVAX, H8/300, Hitachi SuperH, HP/PA-RISC, IBM S/390, Intel IA-64, MISP, Motorola 6800 Serisi, PowerPC, Sun SPARC 'ın da bulunduğu 20 ye yakın mimariyi desteklemektedir. İlk yayınladığında yaklaşık olarak 10000 satırdır ve bunun %80 'i C ile %20 si Assembly ile yazılmıştır. Bu yazı yazıldığında Linux'un 2.6.33 çekirdeği yayınlanmıştı. 2.6.30 versiyonundan alınan istatistiklere göre, Linux çekirdeği 11.5 M satır ve tar.gz sıkıştırma ile yaklaşık 62 MB tutmakta. Bir ilginç istatistik daha: Her gün 5000 geliştirici Linux çekirdeğine yaklaşık 13 K satır eklemektedir.
Linux çekirdeği işletim sistemi seviyesinde çok önemli işler gerçekleştirdiği için geliştirilmesinde kullanılan yöntemler, performansı en üst düzeye çıkarmak amaçlıdır. Bu amaç için geliştiriciler bazı önemli kısıtlamalar doğrultusunda geliştirmeler yapmaktadır.
İşletim sistemlerinde prosesler işlemci düzeyi olarak iki ayrı düzeyde çalıştırılır: Kernel ve User. User düzeydeki prosesler sistemi etkilemeyen, kullanıcının çalıştırdığı programlar gibi çekirdeği kullanan proseslerdir. Kernel prosesler ise sistemi yöneten proseslerdir. Giriş-çıkış portlarına erişim, kullanılan veya boş olan belleğin hesaplanması ve burada dinamik yer tahsisi gibi işlemler kernel modda çalışır. Çekirdek thread’leri, dinamik kütüphane eklenmesini (sistem çalışırken modüller halinde çekirdeğe yazılım parçaları yüklenmesini destekler. Bu konuya sonradan detaylı bir şekilde değineceğiz. Çünkü sürücümüzü modül olarak yazıp çekirdeğe yükleyeceğiz).
Linux çekirdeği GNU C ile yazılmıştır. Bu özellik çekirdek programlamaya önemli kısıtlamalar getirir. Hiçbir standart C Kütüphanesi bir çekirdek koduna ekleyip, hiçbir C çağrısını çekirdek içinde kullanamazsınız. Kernel geliştiricileri çekirdeğin daha performanslı çalışması için, kullanacakları bütün C fonksiyonlarını baştan yazmışlardır. Örneğin printf() metodu kernel kodunda çalışmaz, onun yerine kernel programcıları çok benzeri olan printk() metodunu sıfırdan yazmışlardır. Bu fonksiyonun örnek kullanımı şöyle:

printk(KERN_INFO "Merhaba Kernel Programlama");

KERN_INFO 'dan sonra virgül koyulması gerektiğini düşünebilirsiniz. Ama bu gösterimde hiçbir yanlışlık yok. Bu fonksiyonun çıktısı normalde konsola yapılması gerekiyor. Ama bu özellik dağıtımdan dağıtıma değişiyor. Örneğin Pardus'da bu çıktı /var/log altındaki sistem loglarına yazılıyor (syslog, ken.log) Baştaki KERN_INFO bildirisi log'lama için kullanılır.



Ya da şöyle bir fonksiyon tanımı görebilirsiniz. ("__" ile başlayan isimlendirmeler kernel’a özeldir.)

static int __init usb_a4r7mouse_init(void) {}

Burada fonksiyonun dönüş değeri (int) ile adı arasında (usb_a4r7mouse_init) "__init" ifadesi fonksiyonun modülün sadece ilk yükleme "init" sırasında kullanılacağını belirtir. Böylece kernel bu fonksiyondaki değişkenler için ayırdığı belleği ilk yükleme işleminden sonra sisteme geri verebilir.
Her tanımlandığı yerde tekrar derlenen, böylece çalışma performansını artıran inline fonksiyonlar görebilirsiniz.
asm() çağrısı ile doğrudan assembly çağrıları görebilirsiniz.
Kernel’ın bellek koruması yoktur. User-space bir program yasaklı bir bellek bölgesine erişmek istediğinde ya null döndürülür ya da bir hata bildirilir. Böyle bir mekanizma kernel seviyesinde yoktur. Yanlış bir bellek bölgesinde değişiklik yaparsanız... Yani yapmamaya çalışın.
Kernel belleği sayfalanamaz (paging).
Kernel prosesler floating point işlem yapamaz.
Kernel-space bir proses’in stack'i sadece 4KB dır ve dinamik olarak değiştirilemez.
Linux Kernel'ı ücretsiz olarak www.kernel.org 'dan indirilebilir. tar.gz şeklinde indirdiğiniz sıkıştırılmış dosyanın içindeki dizin yapısı şöyledir.



  • arch - Mimariye bağımlı fonksiyonların bulunduğu kaynak kodlar.
  • crypto - Şifreleme API
  • Documantation
  • drivers
  • fs - File System
  • include - C başlık dosyaları
  • init - İlk yükleme kodları
  • ipc - inter proses com.
  • kernel - çekirdek sistem
  • lib
  • mm - hafıza yönetimi (memory management)
  • net - network
  • scripts
  • security
  • sound
  • usr - user space code

IDE Ayarları
Kernel geliştirmesini bir IDE üzerende yapacaksınız, buradaki klasörlerden include içindeki başlık dosyalarını ve arch altında kendi platformunuza uygun başlık dosyalarını (yine include altında) projenize eklemelisiniz. (Biz Netbeans kullandık, Netbans'te projeye sağ tıklayıp Code Assintance kısmından ya da Properties kısmından C Complier altında Include Directories kısmından bu klasörlerin yollarını gösterin.) Her ne kadar kaynaklarda "Kernel kodu geliştirmek için gerekli herşey buradadır" diye yazsa da, Linux dağıtımları temel kernel kodunu aşırı biçimde yamalanmış halde kullanmaktadır. Kullandığınız dağıtıma özel kodlar ve başlık (.h) dosyaları bulunabilir. Bunları internetten bulup kullandığınız derleyici ve IDE ye tanıtmanız gerekebilir. Örneğin Ubuntu için autoconf.h dosyası özeldir. Kernel koduyla birlikte gelmez. Bu dosyayı elde etmenin en kolay yolu Synaptic Paket Yöneticisinde linux-source ve linux-headers paketlerini kurmaktır. Pardus’ta ise PİSİ paket yöneticisinden adında kernel-header ve kernel-source geçen bütün paketleri kurmalısınız. Kurulan başlık dosyaları /usr/src/linux-headers-2.6.31-19-generic/include altına yerleştirilir. Bu dizini de projenin başlık dosyaları kısmına eklemeniz gerekebilir.



IDE ayarlanması ile ilgili son bir şey daha: Bütün kernel kodları bir #ifdef-#endif içerisinde "__KERNEL__" önişlemci bildirimini yapmak zorundadır. Netbeans'te bu başlık dosyalarını eklediğimiz kısımda "Preprosesor Definitions" kısmında "__KERNEL__" önişlemci bildiriminin yapılması gerekir. Aksi halde Netbeans bazı başlık dosyalarında bazı tanımlamaları bulamayacağını söyleyip, projede hatalar veriyor. Siz de bu ayarları kendi IDE’nize göre özelleştirmelisiniz.



Sistem Çağrıları
Linux çekirdeği user-space’te çalışan proseslere sistemle etkileşim, kaynaklara ve donanım'a erişim, için bir arayüz sunar. Bu sayede user -space programlar sistemin kritik noktalarını bozmadan kritik kaynaklara erişimi çekirdeğe bırakır ve isteklerini çekirdeğe yaparlar, çekirdek de bu isteklere cevap verir. Bellek tahsisi buna örnek verilebilir. Çünkü bir user-space program belleğin hangi gözünün boş hangisinin kullanımda olduğunu bilemez ama çekirdek bilir.
Bugün Linux'ta x86 platformu için 250 ye yakın sistem çağrısı vardır.
Programcılık açısından bakıldığında sistem çağrıları çekirdek tarafından user-space programlara kullandırılan C fonksiyonlarıdır. Hepsi sys_xxx yapısındadır. Dönüş değeri olarak başarı ya da hata kodu belirten long tipinde bir veri döner.



En çok kullanılan sistem çağrılarına bir örnek write() dır. Bir user-space program C dilinde printf() çağrısı ile bir dosyaya yazmak istediyse, önce bunun C kütüphanesinde karşılığı olan write(), sonra da sys_write() sistem çağrısı çağrılır ve bu fonksiyonun içinde hard diske yazma işlemi yapılır.



Sistem çağrıları mimariye bağımlıdır. x86 mimarisi için sistem çağrıları tablosu /arch/x86/kernel/syscall_table_32.S altındadır. Dosyanın uzantısının .S olduğuna dikkat edin. Bu uzantı assembly dosyalarının uzantısıdır.

Gelecek Ay Yazının Devamıyla Görüşmek Üzere.

Kaynaklar
Linux Device Drivers, Robert Corbert, O’Reilly, 2005
Linux Kernel Development, Robert Love, Novell Press, 2005

9 yorum:

  1. Yazınızı çok beğendim. Çok güzel bilgiler var. Teşekkürler :)

    YanıtlaSil
  2. konu üzerinde türkçe kaynak sıkıntısı var. elimden geldiğince bu eklikliği gidermeye çalıştım. işinize yaradıysa ne mutlu :)

    YanıtlaSil
  3. Emek verip bu yazıyı yazdıgınız için ellerine saglik. Bir de bu kadar duzgun yazdıgınız icin bir daha ellerine saglık.

    YanıtlaSil
  4. 2012 yilindayiz yeni dokuman nezaman gelicek :)

    YanıtlaSil
    Yanıtlar
    1. Eğer yazı dizisinin devamından bahsediyorsanız sitede 2 ve 3 nolu kısım var. Başka yazılar gelir mi diye soruyorsanız: "Yazmayı çok isterim ama, şu an günde en az 12 saat çalıştığım bir projeye dahilim. Kendime bile zaman ayıramıyorum ki, bloguma yazayım. İş hayatı zor demişlerdi. Gerçekten öyleymiş."
      Yazılarımla ilgilendiğiniz için çok teşekkürler. Faydalı olduklarını öğrenmek güzel...

      Sil
  5. keşke biraz okuduktan sonra yazıyı aşşa çekmeseydim, şevkim kaçtı. bu c nası bir dildir yahu öğren öğren bitmiyor. Herşeyin bi C'si var bizi mi kekliyonuz :D kernel ayağına

    YanıtlaSil
  6. Bilgilerinizi paylaştığınız için teşekkür ederim. Bir çok insanın bilmediği, ya da aradığı mevzular bunlar...

    YanıtlaSil
  7. fareye ne yaptın !!!!!!!:)

    YanıtlaSil