Modül Parametreleri
129-11920
Bizler modüllere argüman/parametre geçirebiliriz. Bu işlem, insmod sırasında
modül yüklenirken değişken=değer şeklinde yapılmaktadır.
Mesela:
$ sudo insmod helloworld.ko number=10 msg="\"This is a test\"" values=10,20,30
diyebiliriz. Denemelere göre parametre almayan bir modülü çağırmak hataya sebep
olmamaktadır. Yani Temel Modül Fonksiyonları ve Kavramlar de yazdığımız ve parametre almayan
helloworld modülü böyle yüklense de hata oluşmamaktadır.
Bu parametreleri almak için modül kodunda module_param() ve
module_param_array() makrolarını kullanırız.
module_param()
module_param(name, type, perm);
şeklinde bir kullanım vardır. name değişken ismidir. Bu makro çağırılmadan
önce bir global değişken tanımlamalıyız. İsmi, parametre ismi ile aynı
olmalıdır. type ise parametrenin türünü belirtir ve aşağıdaki değerleri
alabilir [1]:
Standard types are:
byte, hexint, short, ushort, int, uint, long, ulong
charp: a character pointer
bool: a bool, values 0/1, y/n, Y/N.
invbool: the above, only sense-reversed (N = true).
invbool, bool un tersidir.
perm ise /sys/module/<isim> dizininde yaratılacak olan parameters
dizininin erişim haklarını belirtir. Burada aşağıdaki sembolik sabitleri
OR’layarak kullanabiliriz [2]:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
Örneğin modülümüz iki tip parametre alıyorsa kodumuz şöyle olabilir:
int count = 0;
char *msg = "Ok";
module_param(count, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
module_param(msg, charp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
Görüldüğü üzere parametrelerin default değerleri vardır ve insmod ile değer
verilmezse bu değerler kullanılır. Ayrıca charp ile gösterilen pointer’ın
const OLMAMASI gerekir.
helloworld isimli modülümüzü aşağıdaki gibi güncelleyelim.
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
static int count = 123;
static char *msg = "Selam";
module_param(count, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
module_param(msg, charp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
static int __init merhaba(void){
printk(KERN_INFO "Merhaba Dunya...\n");
pr_info("count=%d, msg=%s\n", count, msg);
return 0;
}
static void __exit gulegule(void) {
printk(KERN_INFO "Gule gule Dunya...\n");
pr_info("count=%d, msg=%s\n", count, msg);
}
module_init(merhaba);
module_exit(gulegule);
İki adet parametre eklemiş olduk. İlk olarak hiç değer vermeden yükleyelim
ve log’a bakalım, dmesg ile. sudo insmod helloworld.ko ile yükleyince
[ 870.486406] Merhaba Dunya...
[ 870.486413] count=123, msg=Selam
şeklinde log gördük.
Bu durumda sysfs altında parameters adı altında bir dizin oluştu.
$ ls -lh /sys/module/helloworld/parameters/
total 0
-rw-r--r-- 1 root root 4.0K Dec 21 09:19 count
-rw-r--r-- 1 root root 4.0K Dec 21 09:19 msg
module_param() ile verdiğimiz izinler aslında bu “sanal” dosyaların iznini
belirledi. Modülü yükleyen root olduğu için user ve group root oluyor.
module_param() makrosunun son parametresi ile OR, | yaparak bu izinleri
belirlemiş olduk.
İstersek bu değerleri buradan da değiştirebiliriz. Bizim modül çıkış fonksiyonumuzda da bu parametreler bastırılıyor. Modülü çıkartırken değerleri görebiliriz. Yükleme fonksiyonu sadece modülü yüklerken bir kere çalıştığı için şimdilik böyle test edebiliriz.
$ echo 2025 | sudo tee /sys/module/helloworld/parameters/count
2025
$ sudo rmmod helloworld
Bundan sonra aşağıdaki logu gördük.
[ 1156.062040] Gule gule Dunya...
[ 1156.062044] count=2025, msg=Selam
Yani sysfs üzerinden parametreyi değiştirebildik.
Belki aklımıza neden tee kullandık da > kullanmadık diye bir soru gelebilir.
Bu modüllerle ilgili değil.
$ sudo echo 2025 > /sys/module/helloworld/parameters/count
-bash: /sys/module/helloworld/parameters/count: Permission denied
Biz > ile redirect yaptığımız zaman sağdaki dosya bizim permissionlarımız ile
BASH tarafından açılıyor. Biz de root olarak çalışmıyorsak bu durumda yazma
iznimiz yok. Sol tarafta sudo olması sadece echo yu etkiliyor, dosyanın
açılışını değil. Ama sudo tee dediğimiz zaman dosya tee programı tarafından
root olarak açılmış oluyor. Yani sudoyu doğru yere uygulamış oluyoruz.
Dediğim gibi bu durum bu konuya özgü değil, genel bir durum. Alternatif olarak
sudo bash -c "echo 2025 > ..." da kullanabilirdik, bu durumda BASH’i root
olarak çalıştıracaktık.
Elbette insmod ile doğrudan yükleme sırasında da parametre verebiliriz.
$ sudo insmod helloworld.ko count=1234567
Çıktı:
[ 1482.631191] Merhaba Dunya...
[ 1482.631198] count=1234567, msg=Selam
String olan msgye boşluklu karakter geçmek için BASH sentaksından dolayı
\ ile çeşitli escape uygulamak gerekmektedir.
$ sudo insmod helloworld.ko count=444 msg="\"Zalim dunya\""
En dıştaki "" bunun tek bir komut satırı argümanı olduğunu söylüyor, BASH
bununla ilgileniyor. İçerdeki "" ki biz onları \ ile kaçarak yazıyoruz,
"xxx" şeklinde geçmemizi sağlıyor.
Çıktı:
[ 1642.434638] Merhaba Dunya...
[ 1642.434644] count=444, msg=Zalim dunya
Gerçi hoş, ben kernel 6.12.57 ile çalışıyorum ve "" vermesem de oluyor.
$ sudo insmod helloworld.ko count=444 msg=alper
[ 2330.977443] Merhaba Dunya...
[ 2330.977448] count=444, msg=alper
Şu da olur gerçi ama shell’e ve kernele göre hata olabilir belki (?), bende olmuyor:
$ sudo insmod helloworld.ko count=444 msg="Zalim dunya"
module_param_array()
Bu da module_param() benzeri bir makrodur, fakat array tipi parametreler
için tasarlanmıştır.
module_param_array(name, type, nump, perm)
nump, toplam kaç adet değerin modüle dizi biçiminde aktarıldığını belirten
int bir nesnenin adresini alır.
Örnek:
static int values[5];
static int size;
module_param_array(values, int, &size, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
Dizilerin elemanları arasına , koymamız gerekir. Örnek:
$ sudo insmod helloworld.ko values=1,2,3,4,5
Eğer geçirilen eleman sayısı dizi boyutundan fazla olursa (bu “bir şekilde” anlaşılıyor), modül yükleme başarısız olacaktır. Tabii daha az olabilir.
Önceki programımızı genişletelim bu doğrultuda:
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
static int count = 123;
static char *msg = "Selam";
static int values[5];
static int size;
module_param(count, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
module_param(msg, charp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
module_param_array(values, int, &size, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
static int __init merhaba(void){
printk(KERN_INFO "Merhaba Dunya...\n");
pr_info("count=%d, msg=%s\n", count, msg);
pr_info("Values: ");
for (int i = 0; i < size; i++)
pr_cont("%d ", values[i]);
pr_cont("\n");
return 0;
}
static void __exit gulegule(void) {
printk(KERN_INFO "Gule gule Dunya...\n");
pr_info("count=%d, msg=%s\n", count, msg);
}
module_init(merhaba);
module_exit(gulegule);
Derleyip yüklersek
$ sudo insmod helloworld.ko count=444 msg="this is a test!" values=2,4,6
dmesg çıktısı:
[19829.249249] Merhaba Dunya...
[19829.249255] count=444, msg=this is a test!
[19829.249258] Values: 2 4 6
olmaktadır.
Ayrıca
$ cat /sys/module/helloworld/parameters/values
2,4,6
Dikkat
Elemanlar arasında boşluk yoktur, yani 2, 4, 6 değil, 2,4,6 yazdık.
Burada daha önceden kullanmadığımız pr_cont() isminde bir makro kullandık
for içerisinde yazdırmak için. Normal şartlarda printk()nın kullandığı log
buffer line buffered gibi çalışmaktadır [3], en azından pratikte [4].
Yani \n görene kadar ya da sonraki printk() ya da pr_x() gelene kadar biz
bunları ekranda görmeyebiliriz. Her bir printk() da ayrı bir satıra yazma
yapmaktadır dmesg ile bakarken. pr_cont() ile aynı satıra devam ediyoruz ve
sonrasında tek bir \n yazdırarak flush ediyoruz.
Bknz: pr_cont()
Eğer array’i taşırmaya çalışırsak hata alırız:
$ sudo insmod helloworld.ko count=444 msg="this is a test!" values=2,4,6,8,10,12
insmod: ERROR: could not insert module helloworld.ko: Invalid parameters
dmesg:
[20744.588146] values: can only take 5 arguments
[20744.588184] helloworld: `2' invalid for parameter `values'
130-003930