Bir önceki yazımızla başladığımız kernel serimize yine altyapı niteliğinde olan bu yazımızla devam ediyoruz.Yanlış anlaşılma olmasın, kernele henüz başlamadık, ısındırma turlarında devam ediyoruz. Belirttiğimiz üzere bu yazımız kernel modüller ile alakalı olup, tabii ki meze babında bazı detaylara da değineceğiz diyor ve sizi başlıklarla beraber ormantik bir ortama davet ediyorum.
İstiklal marşımızda vurgulandığı üzere kalabalık ve güçlü olsalar da düşmanlarımızdan korkmuyoruz.Konumuzla alakası nedir dersek, çoğumuz kernele adım atar atmaz, anlamakta zorlandığımız kod deryası karşısında hafiften bir ürkmüşüzdür.Aynı vurguyu ben de tekrarlıyor, düşmanımız olarak nitelendirdiğimiz kernel kaynak kodunu nasıl basitçe anlarız, bu başlığımızda irdeliyoruz.
Kernelle alakalı mesajları düzenleyen kısmın [printk] kaynak kodu sadece ve sadece 2924 satırcık [yorumlar dahil].Peki kernelin tamamı ne kadar dersiniz, [sıkı tututun] 15,803,499 satırcık [Kernel 3.10].Neyse fazla ürkütmemek lazım ceylanları diyelim, devam edelim.
Basit C programlarında ihtiyaç duyulmayan, fakat devasa projelerde şart olan bazı terimleri öğrenerek incelediğimiz kodun aslında ne kadar da basit (!) olduğunu göreceksiniz.İrdelemeye static ve EXPORT_SYMBOL ile başlayalım.Diyelim ki bir değişken (ya da fonksiyon) belirliyoruz, benim_guzel_degiskenim isminde, işlevine göre 2 tür belirleme yapabiliyoruz.Ya yazdığımız modüle özel olabilir ya da diğer modüllerde de kullanımına izin verebiliriz.
int benim_guzel_degiskenim; [Standart tanımlama]
static int benim_guzel_degiskenim; [Birtek bu modülde kullanılsın]
EXPORT_SYMBOL(benim_guzel_degiskenim); [Her yerde kullanılsın]
Örneklendirme kısmını videoya bırakıp devam edelim.Milyonlarca satır kodun mümkün olduğunca hızlı çalışabilmesi için gcc yani GNU C compiler derleyicisine bazı özellikler eklenmiş.Bunlardan en sık rastlayacağımız iki tanesi; __attribute__ ve regparm diyebiliriz.Bu arkadaşlar ne işe yarar, detayları merak edenler bu adresten inceleyebilirler.Herşeyi devletten beklememek lazım diyelim ve diğer bir mezemize geçelim.
Bir önceki yazımızda üstüne basa basa belirttiğimiz bir detayı hatırlayarak başlığımıza giriş yapalım.Linus Reis mürekkep yalarkene hayali neydi,portable bir işletim sistemi istiyordu.Yani hem sörvıra uygun hem vörksiteyşına,hem Intel işlemciye uygun hem ARM işlemciye, hem 32 bite uygun hem 64 bite.Ne kadar çok şey istemiş değil mi, istememiş başarmış arkadaş.Bize de bunu nasıl yapmış onu anlatmak düştü, vira bismillah!
Heykır olcam diyen herkesin yolu C öğrenmekten geçmiştir, hesap makinesi yapmadan olmaz.Mesela toplama çıkarma yaptıracaksak sayı değerlerinin atanacağı değişkenler belirlememiz lazım, hepimizin (!) bildiği üzere,int ile belirlenen bu değişkenler için hafızada 4 byte yer ayrılmaktadır.Buraya kadar herşey normal değil mi?
Benim de katkım olsun dediniz ve birşeyler hazırlamaya karar verdiniz.Değişkenler belirlerken fazla mal göz çıkarmaz dediniz,sıradan int kullanmak yerine long int kullanmaya karar verdiniz ya da mecbur kaldınız.Her neyse şimdi sonuca bakalım:
[Kaynak kodumuz bu]
long int sayi;
printf("Boyut %u byte\n,sizeof(sayi));
[32 bit işletim sistemi]
ka@ka-vm ~ $ gcc test.c && ./a.out
Boyut 4 byte
[64 bit işletim sistemi]
ka@ka-vm ~ $ gcc test.c && ./a.out
Boyut 8 byte
Bu sonuçtan da anlıyoruz ki kaynak kodunuz sağlam temele dayanacak, şartlara göre değişmeyecek.Peki Linus Reis bunu nasıl sağlamış, bazı standartları Linuxe yerleştirivermiş ve katkı sağlamak isteyen kendi kafasına göre değil, bu standartlara göre kod yazacak demiş.Örnekteki sorunu çözmek istersek types.h başlık dosyasında belirlenen u32, s64,.. gibi değişken türlerini kullanıyoruz.
Bir örnek daha verelim; bu sefer USB driver yazmaya niyetlendiniz ve veri aktarımı kısmını kodlarken bazı standartlara uymazsanız Intel işlemcide çatır çatır çalışan driverınız SPARC ya da ARM işlemcide patlayabilir.Peki ama neden?
USB standartlarını incelerseniz Little Endian kullanıldığını görürsünüz.Bu cümlede geçen Little Endian'ın ne olduğunu bilmeyenler ya Assembly serisinin Bela Geliyorum Dedi yazısına bakacak ya da google amcadan öğrenip gelecek.SPARC veya ARM işlemcilerde patlar dememizin sebebi de bu işlemcilerin Little Endian yerine Big Endian kullanmasından kaynaklanıyor.
Linus Reis bu soruna da bir çözüm bulmuş;kernel içerisinde kullanabileceğiniz bazı makrolar hazırlamış.Mesela Little Endian bir veriyi işlemciye uygun hale getiren le32_to_cpu ya da tam tersini yapan cpu_to_le64 gibi.Görüldüğü üzere sorunlar belirlenmiş, çözümler bulunmuş, gel gelelim biz daha bunlardan yeni haberdar oluyoruz.
Daha bir sürüüü detay olmasına rağmen meze babında portable konusuna da el attığımıza göre sıra geldi, asıl konumuz olan mödüllerle kıpraşmaya diyor bir sonraki başlığa ışınlanıyoruz.
Belki de çoğunuzun bildiği bir fıkrayla yazımızın asıl konusu olan modüllere yumuşak bir iniş yapalım.Günlerden bir gün, çok ünlü bir kalp cerrahının arabası bozulur, yolda kalır.Hemen telefon eder ve servis gelir.Klasiktir kaputu kaldırır, inceler ve arabayı servise çekmesi gerektiğini ve ancak orada yapabileceğini söyler.Yolda giderken tamirci ünlü doktorumuzu tanır ve sohbet başlar.
Tamirci sohbetin ilerleyen dakikalarında dayanamaz sorar; "Aslında ikimiz de aynı işi yapıyor sayılırız.Sen insanları ameliyat ediyorsun bense arabaları.Nası oluyor da sen milyonlarca dolar [hikayemiz amerikada geçmiyor, bizim ünlü doktorlar da dolar sever] kazanırken ben zor geçiniyorum." Doktor beklemeden kapaq yapar; "Sen motor çalışmıyoken ameliyat yapıyorsun, bense motor çalışırken."
İşte kernele modül hazırlamak da aynen çalışan motora ameliyat yapma gibisinden olduğundan mütevellit kayış sıyırmasın diye almamız gereken önlemler var.Detaylara girelim, sonrasında ilk modülümüzü hazırlayalım.
Öncelikle bütün modüllerde olması gereken module.h başlığını ekliyoruz.Ondan sonracıma modülümüz yüklendiğinde ve kaldırıldığında yapılacakları belirtmemiz gerekiyor.Bunun için init.h başlığını kullanarak module_init ve module_exit makrolarıyla giriş ve çıkış fonksiyonlarını belirliyoruz.Okurken pek anlaşılmıyorsa da örnekte gayet basit olduğunu göreceksiniz.
Linuxun takdire şayan yanı tabii ki açık kaynak ve beleş olmasıdır.Yazacağınız modüllerin de açık kaynak olduğunu ya da olmadığını belirtmeniz gerekiyor.Bunun için MODULE_LICENSE makrosunu kullanıyoruz.Eğer aGaya değil herkese beleş ise GPL [ya da türevleri] yok bedavamısandın durumu varsa Proprietary diyerek belirtmeniz gerekiyor.Beleş derseniz bütün camia modülünüze destek verir,geliştirir yok demezseniz kimse kılını kıpırdatmaz.Bir de kernel hata verir, sorun çıkarsa direk sizi suçlarlar.Durum bu, hangisi işinize geliyorsa..
Lisans bilgisi haricinde, modülümüzün ne işe yaradığını MODULE_DESCRIPTION makrosyuyla, kimin tarafından hazırlandığını MODULE_AUTHOR makrosuyla belirliyoruz.Bu bilgiler ışığında en basitinden bir modül hazırlayalım.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#define REKLAMLAR "Kenan Abdullahoglu"
#define AMAC "Hic bi cacigaa yaramaz"
static int __init basla(void)
{
printk(KERN_INFO "Hay Kornil\n");
return 0;
}
static void __exit bitir(void)
{
printk(KERN_INFO "Bay Kornil\n");
}
module_init(basla);
module_exit(bitir);
MODULE_AUTHOR(REKLAMLAR);
MODULE_DESCRIPTION(AMAC);
MODULE_LICENSE("GPL");
En basitinden modülümüzü hazırladık, sıra geldi derlemeye fakat sıradan C programlarından farklı olarak derlemeyle alakalı bazı bilgileri Makefile isminde meşhuuur dosyamızda belirtmeye, gelsin:
root@kali:~/m2# cat Makefile
obj-m += kaynak_dosya_ismi.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
root@kali:~/m2# make [diyerek derliyoruz]
En basitinden modülümüzü hazırlayarak konu hakkında temel bilgileri paylaştık, fakat azıcık daha detay isteyenlere yazı için hazırlanan videoyu şiddetle tavsiye ediyorum.Bu yazıyla ısındırma turlarımızı bitiriyor, bir sonraki yazıda process management yani işlem yönetimi konusuna giriş yapacağımızı belirtip sizi videoyla başbaşa bırakıyorum.
Yazı için hazırlanan videoyu YouTube'dan izleyebilirsiniz.