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