Linux Kernel Proc File System Üzerinde Dört İşlem

    

    / Proc dosya sistemi aslında bir sistemdeki işlemler(proccess) hakkında bilgi vermek için geliştirilmiş bir dosya sistemidir. Fakat dosya sisteminin kullanışlılığı göz önüne alındığında, kernel çekirdeğinin birçok elemanı hem bilgiyi rapor etmek hem de dinamik çalışma zamanı yapılandırmasını sağlamak için kullanılıyor.

     / Proc dosya sistemi alt klasörleri  ve sanal dosyalar içerir. Sanal bir dosya kernel çekirdeğiden kullanıcıya bilgi sunabilir ve aynı zamanda kullanıcıdan kernel  çekirdeğe bilgi gönderme aracı olarak da hizmet edebilir. 

     /Proc dosya sistemin üzerinde read,write,open,show,mkdir,symlink vb işlemlerin yapılması mümkündür. Bu yazımızda sadece read ve write methodlarını kullanarak dörtişlem yapabilen bir modül yazacağız.

    Kernel modül aracığı ile kişisel işlemlerimiz için /proc alt dosyaları oluşturabiliriz. Bu yazımızda(Tıklayınız) Kernel Modül programlama hakkında detaylı bilgiye ulabilirsiniz.
    
   Modülümüzde aşağıdaki metholar ile /proc alt dosyalama yapısı oluşturabiliriz.


//Proc oluşturma fonksiyonu
# proc_create("string proc name", permissions, id,&File_Operations)
//Proc silme fonksiyonu 
# remove_proc_entry(ProcName, id);
 
    1: Kernel Modülünün Oluşturulması

# cd /home/omansak/Downloads/linux-4.13.4
# cd ExternalModules
# mkdir ProcCalculator
# cd ProcCalculator
# gedit proc.c
     2: C Modulünün Yazılması

     C modulünü yazarken Calculate adın method tanımlayarak bizim için dört işlemi yapacak olan kodları yazacaz. Bu methodu ister /proc alt dosyasının Read methodunu çalışırken aktif edebiliriz ister Write methodu çalışırken aktif edebiliriz.İki yöntem arasında pek fark yoktur. Sadece user space üzerinde çağrılma farklıkları vardır.

// Kütüpnalerin Tanımlanması
#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/proc_fs.h> 
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <linux/init.h>

//Modul Hakkında bilgilerin tanımlanması
MODULE_LICENSE("GPL");
MODULE_AUTHOR("NativeCore");

//Proc Name ve Proc Size tanımlanmaso
#define ProcName   "NativeCore"
#define MaxSize   1024

//Alınacak girdinin max uzunluğunun tanımlanması
#define MaxLenght  10

//Kullanılan Fonksiyonların tanımlanması 
static ssize_t ProcRead(struct file *filp,char *buf,size_t count,loff_t *offp );
static ssize_t ProcWrite(struct file *filp,const char *buf,size_t count,loff_t *offp);
static void Calculate(char* values);
static int Check(char c);

//Proc Struct yapısının tanımlanması ve izin verilen fonksiyonların atanması
static struct proc_dir_entry  *ProcEntry;
static const struct file_operations FileOperations = {
  .owner = THIS_MODULE,
  .read = ProcRead,
  .write = ProcWrite 
};

//Dosya yazma yada okuma esnasında aracı olarak kullanılan buffer tanımlanması
static char ProcBuffer[MaxSize];
static unsigned long BufferSize;

//init(başlangıç) methodunun tanımlanması
static int onStart(void)
{
    //Proc dosyasının oluşturulması
 ProcEntry = proc_create(ProcName, 0, NULL,&FileOperations);
 if (ProcEntry == NULL)
 {
        //Memory yetersizliği yada diğer durumlar için oluşacak hata sonunda proc silinmesi
  remove_proc_entry(ProcName, NULL);
  printk(KERN_ALERT "/proc/%s oluşturulamadı.\n", ProcName);
  return ENOMEM;
 }
    //Proc izinlerinin tanımlanması
 proc_set_user(ProcEntry,KUIDT_INIT(0),KGIDT_INIT(0));
 proc_set_size(ProcEntry,37);
 printk(KERN_ALERT "/proc/%s başarı ile oluşturuldu.\n", ProcName);
 return 0;

}

//exit(kapanış) methodunun tanımlanması
void onExit(void)
{
 remove_proc_entry(ProcName, NULL);
 printk(KERN_ALERT "/proc/%s kaldırıldı\n", ProcName);
}

//Read methodunun tanımlanması
static ssize_t ProcRead(struct file *filp,char *buf,size_t count,loff_t *offp )
{
 printk(KERN_ALERT "ProcRead çalıştırıldı \n");
    //Kernel space'den user space'e verilen aktarılması
 if (copy_to_user(buf, ProcBuffer, BufferSize)) {
  printk(KERN_ALERT "ProcRead : Proc dosyası kopyalanmadı\n");
  return -EFAULT;
 } 
 //Calculate(ProcBuffer);
 return BufferSize;
}

static void Calculate(char* values)
{
    //Dört işlem için kullanılacak değişkenlerin tanımlanması
 static char stringOne[10];
 static char stringTwo[10];
 static long numberOne;
 static long numberTwo;
 static char proccess;
 static int i = 0;
 static int j = 0;

    //+-*/ işaretlerinden birini görene kadar olan tüm karakterleri birinci sayı dizisine aktarılması
 while (Check(values[i]) == 0)
 {
  stringOne[j] = values[i];
  j++;
  i++;
 }

    //+-*/ kulllanılacak işlemin atanaması
 proccess = values[i];
 j = 0;
 i++;

    //+-*/ den sonraki tüm karakterleri ikinci sayı dizisine atanması
 while (i < BufferSize)
 {
  stringTwo[j] = values[i];
  j++;
  i++;
 }

    //Karakter dizilerinin integer dönüştürülmesi
 if (kstrtol(stringOne, 10, &numberOne) != 0)
 {
  printk(KERN_ALERT "1.Sayı : Geçersiz Karakter\n");
  return;
 }
 if (kstrtol(stringTwo, 10, &numberTwo) != 0)
 {
  printk(KERN_ALERT "2.Sayı : Geçersiz Karakter\n");
  return;
 }

    //Yapılacak işleme göre methodun yapılması ve ekrane basılması
 static int result;
 switch (proccess)
 {
 case '+':
  result = numberOne + numberTwo;
  break;
 case '-':
  result = numberOne - numberTwo;
  break;
 case '*':
  result = numberOne * numberTwo;
  break;
 case '/':
  result = numberOne / numberTwo;
  break;
 default:
  printk(KERN_ALERT "Geçersiz Karakter Girildi\n");
  break;
 }
 printk(KERN_ALERT "%d %c %d = %d\n", numberOne, proccess, numberTwo, result);
}

//+-*/ yardımcı karakter ayırt etme methodu
static int Check(char c)
{
 if (c == '+' || c == '-' || c == '*' || c == '/')
  return 1;
 else
  return 0;

}

//Write methodunun tanımlanması
static ssize_t ProcWrite(struct file *filp,const char *buf,size_t count,loff_t *offp)
{

    // Girilen verinin boyutu izin verilen boyutun üstende ise izin verilen boyuta çekilmesi 
 printk(KERN_ALERT "ProcWrite çalıştırıldı\n");
 BufferSize = count;
 if (BufferSize > MaxSize) {
  BufferSize = MaxSize;
 }
 if (copy_from_user(ProcBuffer, buf, BufferSize)) {
  printk(KERN_ALERT "ProcWrite : Proc dosyası kopyalanmadı\n");
  return -EFAULT;
 }
 Calculate(ProcBuffer);
 return BufferSize;
}
module_init(onStart);
module_exit(onExit);

     3: Makefile Yazılması


obj-m = proc.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
     4: Modülün derlenmesi ve aktif olması

# make
# insmod proc.ko (aktif etme)
# rmmod proc (inaktif etme)
    

     5: Proc dosya sisteminin çağrılması

   Öncellikle proc alt dizinine girilmesi gerekmektedir.

# cd ../proc
   Modülümüzü aktif olmasından sonra /proc klasörünün içerisine verdiğimiz isme göre dosya oluşacaktır (/proc/NativeCore)

 

   Görüldüğü üzere dosyamız oluştu ve artık bu dosyaya veri yazabilir yada okuyabiliriz.
     5: Proc dosya veri yazılması ya da okunması

   Modül üzerinde ki Write methodu ile proc dosyasına veri yazmak için "echo" komutu kullanılır

#### Proc Dosyasına veri yazmak için ####
# echo "string veri" > ProcName

# echo "15+65" > NativeCore
# dmesg
  Echo komutu kullandıktan sonra veriler proc dosyamıza yazılmış olacaktır. 

Örnek Toplama İşlemi


Örnek Çarpma İşlemi

Bu örnekte Write Methodundan sonra hesaplama işlemlerini yaptık. Read Methodundan sonra hesaplama işlemlerini yapmak için

#### Proc Dosyasından veri okumak için ####
# cat ProcName
# echo "15+65" > NativeCore
# cat NativeCore

     6: Modulden Çıkılması

#rmmod proc
Olası Hatalar ve Çözümleri

1: "Makefile:3: *** missing separator. Stop".
Makefile dosyanızda boşluk(Space) karakterleri olabilir,bu karakterler yerine TAB karakteri koymalısınız.
2: "make not found"
Bu hata Kernel Kaynak kodlarının bozulması sonucunda meydana gelir, güncel Kerneli indirip tekrar derlenip kurulması gerekir.
3"Bad Adrress" or "Is not proccess"
Bu hata genellikle Write Read methodları içinde veri okurken copy_to_user yada copy_from_user fonksiyonlarının karıştırılması sonucu oluşur 
4:"Could not save file"
Bu hata Kernel Dosyalarını değiştirmek için yönetici izninizin  olmadığını belirtir.

Yönetici şifrenizi oluşturmadıysanız 
# sudo passwd root
Yönetici olarak terminalde işlem yapmak için 
# su
C dosyasının derlerken alınan hatalar için komut satırı incelenmeli ve hataların ayıklanması gerekmektedir. En çok karşılaşan hatalar /proc dosyası için tanımlanan write/read methodlarının geri dönüş tipi yada parametlerin hatalı olmasından kaynaklamakdatır.

Linux Terminal Komutları
1: "cd" istenilen klasöre girilmesi
2: "ls" klasör içeriğinin görülmesi
3: "mkdir" klasör oluşturulması
4: "gedit" dosya açılması yada düzenlenmesi
5: "su" yönetici olarak girilmesi
6: "dmesg" kernel/debug mesajlarının görülmesi
7: "make" kernele ait komut olup derlenme işlemini yapar
8: "insmod" kernel modülünün aktif olması
9: "rmmod" kernel modülünün inaktif olması
10: "echo" veri yazılması
11: "cat" veri okunması
12: "lsmod" modüllerin listelenmesi


Grup Üyeleri
OSMAN ŞAKİR KAPAR (Takım Lideri)


Yorumlar

Bu blogdaki popüler yayınlar

Web Servis ve Kimlik Doğrulama (Authentication) Yöntemleri

Linux Kernel ile Raspberry Pi 3'den TCP Protolü Ile Sensör Verileri Alınması

Yazılım Kalite Metrikleri