27 Mart 2016 Pazar

-Kernel Modül Geliştirme 2-

Bir önceki makalemde (link) "kernel modül geliştirme" ile ilgili başlangıç düzeyinde bilgi verilmiştir. Ancak orada işlenen modül mimarisi ile bu makalede işleyeceğimiz yapı birbirinden farklıdır.
Çünkü kernel modül geliştirmek için farklı yapılarda kalıplar kullanılabilir. Dolayısıyla donanım sürücüsü geliştirmek ile zamanlanmış görevleri işlemek için geliştirilecek modül yapısı birbirinden farklı olabilir.
Örneğin ilk konu içeriğinde işlediğimiz modül yapısı standart kalıptaki bir "init"'e sahip olması gerekmez. Çünkü ilgili modül içeriğinde yazdığımız bazı fonksiyonlar,kernel'de bulunan ana driver(örn:ekran)'e çengellenir. Böylece driver işlenirken kernel tarafında lazım olan fonksiyonlar bizim geliştirdiğimiz modül içersinde çağrılır.
Donanım sürücüleri(ekran,sensör vb...) genellikle bu yapıda geliştirilir.

Ancak diğer(bu makalede işlenen) modül yapısı farklıdır. Bu modül bir "init" ve "exit" fonksiyonlarına sahip olması gerekir-zorunludur. Bu iki fonksiyon kernel tarafından ihtiyaç duyulur. Dolayısıyla modülün çalışması için bunlar gereklidir.Aksi taktirde amaca uygun ve/veya hiç çalıştırılamayacak bir modül geliştirmiş oluruz.
Zamanlanmış ve yan görevler,ana kernel ile çalışacak destekleyici parçalar bu yapıda geliştirilir.
Bu kalıbı gösteren örnek içerik aşağıda yer almaktadır:

#include <linux/module.h> /* tüm modüller tarafından ihtiyaç duyulur */
#include <linux/kernel.h> /*KERN_INFO,_ALERT,WARNING vb. mesaj-log sabitleri kullanabilmek için gereklidir */
#include <linux/init.h> /* makrolar için gereklidir */

static int __init hello_init(void)
{
printk(KERN_INFO "Hello, world\n");
return 0;
}

static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, world\n");
}

module_init(hello_init);
module_exit(hello_exit);

Kalıbı incelediğimizde ilk olarak iki adet fonksiyon yer aldığı görülüyor. Bu fonksiyonlar az evvel bahsini yaptığımız "zorunlu" fonksiyonlardır. Bu fonksiyonlar,Kernel'e çengel atıldığında modülün başlatılabilmesi-çalıştırılabilmesi ve modülü kernel'den ayırırken(modülü silerken) çalışması gereken fonksiyonlardır. Dolayısıyla modül kernele çengellendiğinde "hello_init" fonksiyonu,modül kernelden ayrılırken-silinirken ise "hello_exit" fonksiyonu çalışır. Bunun dışında modül içersinde yer alacak fonksiyon tasarımları geliştiricinin insiyatifindedir.

Fonksiyon isimleri standart değildir. Yani "hello_init" ve "hello_exit" olarak tanımlamak zorunlu değildir. Dolayısıyla isim belirlemek de geliştirici insiyatifindedir. Ancak,tasarlanan "init" fonksiyonu "__init" belirteci-tanımı(şimdilik böyle diyeceğiz) ile tanımlanmalıdır.
Aynı şekilde bir "exit" fonksiyonu da "__exit" belirtenic-tanımı ile tanımlanmalıdır.
NOT: "__"(çift alt tire) ile başlayan yapıları kullanırken;"sistem düzeyinde birşeyler kurcalıyorum" bilinciyle hareket ettiğiniz varsayılır. Kullanmanın sorumluluğu geliştiriciye aittir.
Dolayısıyla işletim sisteminize ve/veya donanımlarınıza zarar gelmesinden siz sorumlusunuz.


Son olarak zorunlu fonksiyonlarımızın kernel'e çengellenebilmesi için aşağıdaki satırlar yazılmalıdır-eklenmelidir.

module_init(hello_init);
module_exit(hello_exit);

"module_init" ve "module_exit" kernel standartlarına aittir. Ancak parametreler bizim isimlendirdiğimiz fonksiyon isimleri olmalıdır.
Bu durumda modül başlatılırken,yani kernele çengel atıldığında "hello_init",modül kernelden silinirken-ayrılırken ise "hello_exit" fonksiyonu çalışması belirtilmiştir.

Modül derlemek için belli komutlar kullanmamız gerekir. Bir Makefile dosyası oluşturmalı veya aşağıda verilen komutları komut satırında çalıştırmalısınız.
Proje dizini içersinde bir Makefile dosyası oluşturup aşağıda verilen komutları kopyalayınız.
İçeriği kendi sisteminize-projenize ve isimlendirmeye uygun hale getirip derleme aşamasına geçebilirsiniz.

Makefile:

obj-m += hello-1.o
obj-m += hello-2.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


NOT: Makefile dosyaları ve tasarımı konusuna değinilmedi. Ayrıca bu ve diğer modül,kaynak kod vb. çalıştırma sonucu sisteminizde ve/veya donanımınızda oluşabilecek hasarlardan SİZ SORUMLUSUNUZ.
MAKALE SAHİBİ SORUMLU TUTULAMAZ.

iyi çalışmalar.
------------------------------------------------------------------------------------------------------------------------
-Güncelleme(Ek bilgi):
-Soru:"module_init argüman olarak aldığı fonksiyonun dönüş değeri int iken module_exit’in ki void bunlarkesin olması gereken kalıplar mı yoksa farklı türden geri dönüş değeri olan fonksiyonlar da kullanabilir miyiz ? "

-Cevap:
Burada kullanılan "module_init" makro olarak kullanılmıştır.
Makro tanımlı hali:


#define module_init(x) __initcall(x);


Dolayısıyla "__initcall"’da kullanılabilir. Ancak bu tür kullanımlar için ayrı bir konu başlığında
konşmak gerekir.Çünkü butür durumlarda farklı riskler ortaya çıkacaktır.
Geniş bir konu olduğu için değinmiyorum. Ancak arzu eden olursa bununla ilgili bir yazı hazırlarım.

"__initcall" ve birkaç alt katman daha makro yapıdadır. Diğer kısımlar aşağıda verilmiştir.


1:
#define __initcall(fn) device_initcall(fn)

2:
#define device_initcall(fn)             __define_initcall(fn, 6)

3:


3:
Son olarak bir makro içinde static olarak tanımlanmış bir içerik mevcuttur.
Makro:


#define __define_initcall(fn, id)
///...

----
module_init’e parametre geçilecek fonksiyon "int" döndürmesi gerekiyor.
Yani "module_init" sizden bir "int" türü dönüş değeri bekler. Bu değer kernel modulumuzun başarılı veya başarısız bir init süreci yaşadığını bildirir. Şayet başarılı bir init süreci geçirilirse kernel modülün çalışmasını devam etmesine izin verilir. Aksi taktirde kernel modülümüz çengellden atılır.
Yani ana kernel mekanizması bizim modülü kendisinden ayırır(RİSKLER).

Ancak "module_exit" için bir dönüş değeri geçirmek gerekmez. Çünkü zaten her halükarda modül çengelden ayrılıyor/ayrılmaya zorlanır.


İyi çalışmalar.

Hiç yorum yok:

Yorum Gönder