> "proc" dosya sistemi: Anımsanacağı gibi proc dosya sistemi disk tabanlı bir dosya sistemi değildir. Çekirdek çalışması sırasında dış dünyaya bilgi vermek için bazen de davranışını dış dünyadan gelen verilerle değiştirebilmek için proc dosya sistemini kullanmaktadır. Daha sonra proc gibi sys isimli bir dosya sistemi de Linux'a eklenmiştir. proc dosya sistemi aslında yalnızca çekirdek tarafından değil aygıt sürücüler tarafından da kullanılabilmektedir. Ancak bu dosya sisteminin içerisinde user moddan dosyalar ya da dizinler yaratılamamaktadır. proc dosya sistemindeki tüm girişlerin dosya uzunluları 0 biçiminde rapor edilmektedir. proc dosya sisteminin kullanımına yönelik çekirdek fonksiyonları çekirdeğin versyionları ile zamanla birkaç değiştirilmiştir. Dolayısıyla eski çekirdeklere çalışan kodlar yeni çekirdeklerde derlenmeyecektir. Biz burada en yeni fonksiyonları ele alacağız. User moddan prog dosya sistemindeki bir dosya üzerinde open, read, write, lseek, close işlmeler yapıldığında aslında aygıt sürücülerin belirlediği fonksiyonlar çağrılmaktadır. Yani örneğin biz user moddan proc dosya sistemi içerisindeki bir dosyadan okuma yapmak istediğimizde aslında onu oluşturan aygıt sürücünün içerisindeki bir fonksiyon çalışıtırılır. Bu fonksiyon bize okuma sonucunda elde edilecek bilgileri verir. Benzer biçimde proc dosya sistemindeki bir dosyaya user moddan yazma yapılmak istendiğinde aslında o dosyaya ilişkin aygıt sürücünün bir fonksiyonu çağrılmaktadır. Yani proc dosya sistemi aslında aygıt sürücüden fonksiyon çağıran bir mekanizmaya sahiptir. proc dosya sisteminde bir dosya yaratabilmek için proc_create isimli fonksiyon kullanılmaktadır. Fonksiyonun prototipi şöyledir: #include struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct proc_ops *proc_ops); Fonksiyonun, -> Birinci parametresi yaratılacak dosyanın ismini belirtir. -> İkinci parametresi erişim haklarını belirtmektedir. Bu parametre 0 geçilirse default erişim hakları kullanılır. -> Üçüncü parametre dosyanın hangi dizinde yaratılacağını belirtmektedir. Bu parametre NULL geçilirse dosya ana "/proc" dizini içerisinde yaratılır. proc dosya sistemi içerisinde dizinlerin nasıl yaratıldığını izleyen paragraflarda açıklayacağız. -> Son parametre proc dosya sistemindeki ilgi dosyaya yazma ve okuma yapldığında çalıştırılacak fonksiyonları belirtir. Aslında birkaç sene önceki çekirdeklerde (3.10 çekirdeklerine kadarki çekirdeklerde) bu fonksiyonun son parametresi proc_ops yapısını değil, file_operations yapısını kullanıyordu. Dolayısıyla çekirdeğinizdeki fonksiyonun son parametresinin ne olduğuna dikkat ediniz. Örneğin önceki kursun yapıldığı makinede bu son parametre file_operations yapısına ilişkinken bu kursun yapıldığı makinede proc_ops yapısına ilişkindir. proc_ops yapısı şöyle bildirilmiştir: #include struct proc_ops { unsigned int proc_flags; int (*proc_open)(struct inode *, struct file *); ssize_t (*proc_read)(struct file *, char __user *, size_t, loff_t *); ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *); ssize_t (*proc_write)(struct file *, const char __user *, size_t, loff_t *); /* mandatory unless nonseekable_open() or equivalent is used */ loff_t (*proc_lseek)(struct file *, loff_t, int); int (*proc_release)(struct inode *, struct file *); __poll_t (*proc_poll)(struct file *, struct poll_table_struct *); long (*proc_ioctl)(struct file *, unsigned int, unsigned long); #ifdef CONFIG_COMPAT long (*proc_compat_ioctl)(struct file *, unsigned int, unsigned long); #endif int (*proc_mmap)(struct file *, struct vm_area_struct *); unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); }; proc_ops yapısının elemanlarına ilişkin fonksiyon göstericilerinin türlerinin file_operations yapısındaki elemanlara ilişkin fonksiyon gösterişcilerinin türleri ile aynı olduğuna dikkat ediniz.ç Bu fonksiyonların kullanımı tamamen aygıt sürücü için oluşturduğumuz file_operations yapısı ile aynı biçimdedir. -> Fonksiyon başarı durumunda yaratılan dosyanın bilgilerini içeren proc_dir_entry türünden bir yapı nesnesinin adresiyle, başarısızlık durumunda NULL adresle geri dönmektedir. Bu durumda çağıran fonksiyonun -NOMEM gibi bir hata değeriyle geri döndürülmesi yaygındır. proc dosya sisteminde yaratılan dosya remove_proc_entry fonksiyonuyla silinebilmektedir. Fonksiyonun prototipi şöyledir: #include void remove_proc_entry(const char *name, struct proc_dir_entry *parent); Fonksiyonun, -> Birinci parametresi silinecek dosyanın ismini, -> İkinci parametresi dosyanın içinde bulunduğu dizine ilişkin proc_dir_entry nesnesinin adresini almaktadır. Yine bu parametre NULL adres girilirse dosyanın ana "/proc" dizininde olduğu kabul edilmektedir. proc dosya sistemi genel olarak text tabanlı bir dosya sistemi biçiminde düşünülmüştür. Yani buradaki dosyalar genel olarak text içeriğe sahiptir. Siz de aygıt sürücünüz için proc dosya sisteminde dosya oluşturacaksınız onların içeriğini text olarak oluşturmalısınız. Şimdi de örneklerle ile pekiştirelim: * Örnek 1, Aşağıdaki örnekte proc sisteminde dosya yaratan iskelet bir aygıt sürücü programı verilmiştir. Bu aygıt sürücüde"/proc" dizininde "procfs-driver" isminde bir dosya yaratılmaktadır. Aygıt sürücüyü install ettikten sonra "/proc" dizininde bu dosyanın yaratılıp yaratılmadığını kontrol ediniz. Bu dosyayı "cat" ile komut satırından okumak istediğinizde "cat" programı bu dosyayı açıp, read işlemi uygulayıp kapatacaktır. "cat" işleminden sonra "dmesg" komutu ile aygıt sürücümüzde belirlediğimiz fonksiyonların çağrıldığını doğrulayınız. /* procfs-driver.c */ #include #include #include #include #include #define PIPE_BUFSIZE 4096 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kaan Aslan"); MODULE_DESCRIPTION("procfs driver"); static int generic_open(struct inode *inodep, struct file *filp); static int generic_release(struct inode *inodep, struct file *filp); static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off); static int proc_open(struct inode *inodep, struct file *filp); static int proc_release(struct inode *inodep, struct file *filp); static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t proc_write(struct file *filp, const char *buf, size_t size, loff_t *off); static dev_t g_dev; static struct cdev *g_cdev; static struct file_operations g_fops = { .owner = THIS_MODULE, .open = generic_open, .read = generic_read, .write = generic_write, .release = generic_release }; static struct proc_ops g_proc_ops = { .proc_open = proc_open, .proc_release = proc_release, .proc_read = proc_read, .proc_write = proc_write }; static int __init generic_init(void) { int result; struct proc_dir_entry *pde; printk(KERN_INFO "procfs-driver module initialization...\n"); if ((result = alloc_chrdev_region(&g_dev, 0, 1, "procfs-driver")) < 0) { printk(KERN_INFO "Cannot alloc char driver!...\n"); return result; } if ((g_cdev = cdev_alloc()) == NULL) { printk(KERN_INFO "Cannot allocate cdev!...\n"); return -ENOMEM; } g_cdev->owner = THIS_MODULE; g_cdev->ops = &g_fops; if ((result = cdev_add(g_cdev, g_dev, 1)) < 0) { unregister_chrdev_region(g_dev, 1); printk(KERN_ERR "Cannot add device!...\n"); return result; } if ((pde = proc_create("procfs-driver", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, NULL, &g_proc_ops)) == NULL) { cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); return -ENOMEM; } return 0; } static void __exit generic_exit(void) { remove_proc_entry("procfs-driver", NULL); cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); printk(KERN_INFO "procfs driver module exit...\n"); } static int generic_open(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver opened...\n"); return 0; } static int generic_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver closed...\n"); return 0; } static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver read\n"); return 0; } static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver write...\n"); return 0; } static int proc_open(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver proc file opened...\n"); return 0; } static int proc_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver proc file closed...\n"); return 0; } static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver proc file read...\n"); return 0; } static ssize_t proc_write(struct file *filp, const char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver proc file write...\n"); return 0; } module_init(generic_init); module_exit(generic_exit); # Makefile obj-m += $(file).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 /* load (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$1 mode=666 /sbin/insmod ./$module.ko ${@:2} || exit 1 major=$(awk "\$2 == \"$module\" {print \$1}" /proc/devices) rm -f $module mknod $module c $major 0 chmod $mode $module /* unload (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$1 mode=666 /sbin/rmmod ./$module.ko || exit 1 rm -f $module * Örnek 2, Aşağıdaki örnekte aygıt sürücüsü içerisindeki count isimli global değişken proc dosya sistemindeki "profs-driver" isimli bir dosya ile temsil edilmiştir. Bu dosyadan okuma yapıldığında bu count değişkeninin değeri elde edilmektedir. Dosyaya yazma yapıldığında bu count değişkeninin değeri güncellenmektedir. Yazma işleminde dosya göstericisi dikkate alınmamış ve yazma işlemi her zaman sanki ilgili dosyanın başından itibaren yapılıyormuş gibi bir etki oluşturulmuştur. /* procfs-driver.c */ #include #include #include #include #include #define PIPE_BUFSIZE 4096 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kaan Aslan"); MODULE_DESCRIPTION("procfs driver"); static int generic_open(struct inode *inodep, struct file *filp); static int generic_release(struct inode *inodep, struct file *filp); static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off); static int proc_open(struct inode *inodep, struct file *filp); static int proc_release(struct inode *inodep, struct file *filp); static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t proc_write(struct file *filp, const char *buf, size_t size, loff_t *off); static dev_t g_dev; static struct cdev *g_cdev; static struct file_operations g_fops = { .owner = THIS_MODULE, .open = generic_open, .read = generic_read, .write = generic_write, .release = generic_release }; static struct proc_ops g_proc_ops = { .proc_open = proc_open, .proc_release = proc_release, .proc_read = proc_read, .proc_write = proc_write }; static int g_count = 123; static char g_count_str[32]; static int __init generic_init(void) { int result; struct proc_dir_entry *pde; printk(KERN_INFO "procfs-driver module initialization...\n"); if ((result = alloc_chrdev_region(&g_dev, 0, 1, "procfs-driver")) < 0) { printk(KERN_INFO "Cannot alloc char driver!...\n"); return result; } if ((g_cdev = cdev_alloc()) == NULL) { printk(KERN_INFO "Cannot allocate cdev!...\n"); return -ENOMEM; } g_cdev->owner = THIS_MODULE; g_cdev->ops = &g_fops; if ((result = cdev_add(g_cdev, g_dev, 1)) < 0) { unregister_chrdev_region(g_dev, 1); printk(KERN_ERR "Cannot add device!...\n"); return result; } if ((pde = proc_create("procfs-driver", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, NULL, &g_proc_ops)) == NULL) { cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); return -ENOMEM; } return 0; } static void __exit generic_exit(void) { remove_proc_entry("procfs-driver", NULL); cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); printk(KERN_INFO "procfs driver module exit...\n"); } static int generic_open(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver opened...\n"); return 0; } static int generic_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver closed...\n"); return 0; } static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver read\n"); return 0; } static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver write...\n"); return 0; } static int proc_open(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver proc file opened...\n"); return 0; } static int proc_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver proc file closed...\n"); return 0; } static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off) { size_t esize; size_t left; sprintf(g_count_str, "%d\n", g_count); left = strlen(g_count_str) - *off; esize = left < size ? left : size; if (esize != 0) { if (copy_to_user(buf, g_count_str + *off, esize) != 0) return -EFAULT; *off += esize; } printk(KERN_INFO "procfs-driver proc file read...\n"); return esize; } static ssize_t proc_write(struct file *filp, const char *buf, size_t size, loff_t *off) { size_t esize; char count_str[31]; int count; esize = size > 31 ? 31 : size; if (esize != 0) { if (copy_from_user(count_str, buf, esize) != 0) return -EFAULT; } count_str[esize] = '\0'; if (kstrtoint(count_str, 10, &count) != 0) return -EINVAL; if (count < 0 || count > 1000) return -EINVAL; g_count = count; strcpy(g_count_str, count_str); printk(KERN_INFO "procfs-driver proc file write...\n"); return esize; } module_init(generic_init); module_exit(generic_exit); # Makefile obj-m += $(file).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 /* load (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$1 mode=666 /sbin/insmod ./$module.ko ${@:2} || exit 1 major=$(awk "\$2 == \"$module\" {print \$1}" /proc/devices) rm -f $module mknod $module c $major 0 chmod $mode $module /* unload (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$1 mode=666 /sbin/rmmod ./$module.ko || exit 1 rm -f $module Biz yukarıdaki örneklerde dosyayı proc dosya sisteminin kök diininde yarattık. İstersek proc dizininde bir dizin yaratıp dosyalarımızı o dizinin içerisinde de oluşturabilirdik. proc dosya sisteminde bir dizin yaratmak için proc_mkdir fonksiyonu kullanılmaktadır: #include struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent); Fonksiyonun, -> Birinci parametresi yaratılacak dizin'in ismini, -> İkinci parametresi dizinin hanfi dizin içerisinde yaratılacağını belirtir. Bu parametre NULL geçilirse dizin proc dosya sisteminin kök dizininde yaratılır. -> proc_mkdir fonksiyonu başarısızlık durumunda NULL adrese geri dönmektedir. Çağıran fonksiyonun yine -ENOMEM değeriyle geri döndürülmesi uygundur. Geri değerini proc_create fonksiyonun parent parametresinde kullanırsak ilgili dosyamızı bu dizinde yaratmış oluruz. Tabii benzer biçimde dizin içerisinde dizin de yaratabiliriz. Örneğin: struct proc_dir_entry *pdir; pdir = proc_mkdir("procfs-driver", NULL); proc_create("info", 0, pdir, &g_proc_ops); Dizinlerin silinmesi yine remove_proc_entry fonksiyonuyla yapılmaktadır. Tabii dizin içerisindeki dosyaları silerken remove_proc_entry fonksiyonda dosyanın hangi dizin içerisinde olduğu belirtilmelidir. Bu fonksiyon ile dizin silinirken dizin'in içi boş değilse bile o dizin ve onun içindeki girişlerin hepsi silinmektedir. Ayrıca kök dizindeki girişleri silmek için proc_remove fonksiyonu da bulundurulmuştur. Fonksiyonun prototipi şöyledir: #include void proc_remove(struct proc_dir_entry *de); Bu fonksiyon parametre olarak proc_create ya da proc_mkdir fonksiyonun verdiği geri dönüş değerini alır. proc dosya sisteminin kök dizininde silme yapılmak isteniyorsa aşağıdaki her iki çağrım eşdeğerdir: remove_proc_entry("file_name", NULL); proc_remove("file_name"); Şimdi de örneklerle ile pekiştirelim: * Örnek 1, Aşağıdaki örnekte proc dosya sisteminin kök dizininde "procfs-driver" isimli bir dizin yaratılmış, onun içerisinde de "count" bir dosya yaratılmıştır. Bu örneğin yukarıdaki örnekten tek farkı dosyanın proc dosya sisteminin kökünde değil bir dizinin içerisinde yaratılmış olmasıdır. /* procfs-driver.c */ #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kaan Aslan"); MODULE_DESCRIPTION("procfs driver"); static int generic_open(struct inode *inodep, struct file *filp); static int generic_release(struct inode *inodep, struct file *filp); static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off); static int proc_open(struct inode *inodep, struct file *filp); static int proc_release(struct inode *inodep, struct file *filp); static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t proc_write(struct file *filp, const char *buf, size_t size, loff_t *off); static dev_t g_dev; static struct cdev *g_cdev; static struct file_operations g_fops = { .owner = THIS_MODULE, .open = generic_open, .read = generic_read, .write = generic_write, .release = generic_release }; static struct proc_ops g_proc_ops = { .proc_open = proc_open, .proc_release = proc_release, .proc_read = proc_read, .proc_write = proc_write }; static int g_count = 123; static char g_count_str[32]; static int __init generic_init(void) { int result; struct proc_dir_entry *pde_dir; struct proc_dir_entry *pde; printk(KERN_INFO "procfs-driver module initialization...\n"); if ((result = alloc_chrdev_region(&g_dev, 0, 1, "procfs-driver")) < 0) { printk(KERN_INFO "Cannot alloc char driver!...\n"); return result; } if ((g_cdev = cdev_alloc()) == NULL) { printk(KERN_INFO "Cannot allocate cdev!...\n"); return -ENOMEM; } g_cdev->owner = THIS_MODULE; g_cdev->ops = &g_fops; if ((result = cdev_add(g_cdev, g_dev, 1)) < 0) { unregister_chrdev_region(g_dev, 1); printk(KERN_ERR "Cannot add device!...\n"); return result; } if ((pde_dir = proc_mkdir("procfs-driver", NULL)) == NULL) { cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); return -ENOMEM; } if ((pde = proc_create("count", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, pde_dir, &g_proc_ops)) == NULL) { remove_proc_entry("procfs-driver", NULL); cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); return -ENOMEM; } return 0; } static void __exit generic_exit(void) { remove_proc_entry("procfs-driver", NULL); cdev_del(g_cdev); unregister_chrdev_region(g_dev, 1); printk(KERN_INFO "procfs driver module exit...\n"); } static int generic_open(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver opened...\n"); return 0; } static int generic_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver closed...\n"); return 0; } static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver read\n"); return 0; } static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off) { printk(KERN_INFO "procfs-driver write...\n"); return 0; } static int proc_open(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver proc file opened...\n"); return 0; } static int proc_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "procfs-driver proc file closed...\n"); return 0; } static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off) { size_t esize; size_t left; sprintf(g_count_str, "%d\n", g_count); left = strlen(g_count_str) - *off; esize = left < size ? left : size; if (esize != 0) { if (copy_to_user(buf, g_count_str + *off, esize) != 0) return -EFAULT; *off += esize; } printk(KERN_INFO "procfs-driver proc file read...\n"); return esize; } static ssize_t proc_write(struct file *filp, const char *buf, size_t size, loff_t *off) { size_t esize; char count_str[31]; int count; esize = size > 31 ? 31 : size; if (esize != 0) { if (copy_from_user(count_str, buf, esize) != 0) return -EFAULT; } count_str[esize] = '\0'; if (kstrtoint(count_str, 10, &count) != 0) return -EINVAL; if (count < 0 || count > 1000) return -EINVAL; g_count = count; strcpy(g_count_str, count_str); printk(KERN_INFO "procfs-driver proc file write...\n"); return esize; } module_init(generic_init); module_exit(generic_exit); # Makefile obj-m += $(file).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 /* load (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$1 mode=666 /sbin/insmod ./$module.ko ${@:2} || exit 1 major=$(awk "\$2 == \"$module\" {print \$1}" /proc/devices) rm -f $module mknod $module c $major 0 chmod $mode $module /* unload (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$1 mode=666 /sbin/rmmod ./$module.ko || exit 1 rm -f $module Şimdi de boru aygıt sürücüsüne proc dosya sistemi desteği verelim. Aygıt sürücümüz proc kök dizininde minör numara kadar ayrı dizin oluşturmaktadır. Sonra da bu dizinlerin içerisinde ilgili aygıtların bufsize ve count değerlerini iki dosya ile dış dünyaya vermektedir. Örneğimizde dikkat edilmesi gereken birkaç nokta üzerinde durmak istiyoruz: -> proc dosya sistemi içerisindeki bir dosya user moddan açıldığında aygıt sürücümüz hangi dosyanın açıldığını nereden bilecektir? İşte dosya nesnesi görevinde olan file yapısının f_path elemanı path isimli bir yapı türündendir. Bu yapı şöyle bildirilmiştir: struct path { struct vfsmount *mnt; struct dentry *dentry; } __randomize_layout; Yapının deentry elemanı dosya hakkında bilgiler içermektedir: struct dentry { /* RCU lookup touched fields */ unsigned int d_flags; /* protected by d_lock */ seqcount_spinlock_t d_seq; /* per dentry seqlock */ struct hlist_bl_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct inode *d_inode; /* Where the name belongs to - NULL is negative */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ /* Ref lookup also touches following */ struct lockref d_lockref; /* per-dentry lock and refcount */ const struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ unsigned long d_time; /* used by d_revalidate */ void *d_fsdata; /* fs-specific data */ union { struct list_head d_lru; /* LRU list */ wait_queue_head_t *d_wait; /* in-lookup ones only */ }; struct hlist_node d_sib; /* child of parent list */ struct hlist_head d_children; /* our children */ /* * d_alias and d_rcu can share memory */ union { struct hlist_node d_alias; /* inode alias list */ struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */ struct rcu_head d_rcu; } d_u; }; Burada yapının d_name elemanı qstr isimli bir yapı türündedir: struct qstr { union { struct { HASH_LEN_DECLARE; }; u64 hash_len; }; const unsigned char *name; }; İşte buradaki name elemanı ilgili dosyanın ismini belirtmektedir. Özetle biz file yapısından hareketle dosyanın ismini elde edebilmekteyiz. Böylece biz proc dosya sistemi içerisinde bir dosya açıldığında o dosyanın isminden hareketle hangi dosyanın açılmış olduğunu anlayabiliriz. Ayrıca dentry yapısının d_parent elemanı dosya ya da dizin'in içinde bulunduğu dizine ilişkin dentry nesnesini vermektedir. Yani biz istersek dosyanın içinde bulunduğu dizinin ismini de alabiliriz. Aşağıda örneği bir bütün olarak veriyoruz. * Örnek 1, Aygıt sürücüyü yine aşağıdaki gibi derleyebilirsiniz: $ make file=pipe-driver Yüklemeyi aşağıdaki gibi yapabilirsiniz: $ sudo ./loadmulti 5 pipe-driver ndevices=5 Aygıt sürüyü yükledikten sonra artık proc dosya sisteminde "pipe-driver" isimli bid dizin oluşturulacak ve bu dizin de aşağıdaki gibi 3 dizin yaratılmış olacaktır: $ ls /proc/pipe-driver pipe0 pipe1 pipe2 pipe3 pipe4 Bu dosyaların her birinin içerisinde de "bufsize" ve "count" isimli iki dosya bulunacaktır. Burada teti "prog1.c" ve "prog2.c" programları yardımıyla yapabilirisiniz. Örneğin "prog1" programı ile "pipe-driver3" borusunu açıp içerisine bir şeyler yazarsanız "/proc/pipe-driver/pipe3/count" dosyasının içerisinde yazılan byte sayısını görebilirsiniz. Aygıt sürücümüzü yine "unloadmulti" script'i ile boşaltabilirisiniz: $ sudo ./unloadmulti 5 pipe-drive Aşağıda ise programa ilişkin kodları mevcuttur. /* pipe-driver.h */ #ifndef PIPEDRIVER_H_ #define PIPEDRIVER_H_ #include #include struct PIPE_PEEK { size_t size; void *buf; }; #define PIPE_DRIVER_MAGIC 'p' #define IOC_PIPE_GETCOUNT _IOR(PIPE_DRIVER_MAGIC, 0, size_t) #define IOC_PIPE_GETBUFSIZE _IOR(PIPE_DRIVER_MAGIC, 1, size_t) #define IOC_PIPE_SETBUFSIZE _IOW(PIPE_DRIVER_MAGIC, 2, size_t) #define IOC_PIPE_PEEK _IOWR(PIPE_DRIVER_MAGIC, 3, struct PIPE_PEEK) #endif /* pipe-driver.c */ #include #include #include #include #include #include #include #include #include #include "pipe-driver.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define NDEVICES 10 #define DEF_PIPE_BUFSIZE 10 #define MAX_PIPE_BUFSIZE 131072 /* 128K */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kaan Aslan"); MODULE_DESCRIPTION("Pipe Driver"); static int generic_open(struct inode *inodep, struct file *filp); static int generic_release(struct inode *inodep, struct file *filp); static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off); static long generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); static int proc_open(struct inode *inodep, struct file *filp); static int proc_release(struct inode *inodep, struct file *filp); static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off); static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off); static struct file_operations g_fops = { .owner = THIS_MODULE, .open = generic_open, .read = generic_read, .write = generic_write, .release = generic_release, .unlocked_ioctl = generic_ioctl }; static struct proc_ops g_proc_ops = { .proc_open = proc_open, .proc_release = proc_release, .proc_read = proc_read, }; /* static struct file_operations g_proc_ops = { .open = proc_open, .release = proc_release, .read = proc_read, }; */ struct PIPE_DEVICE { unsigned char *pipebuf; size_t head; size_t tail; size_t count; size_t bufsize; struct semaphore sem; wait_queue_head_t wqread; wait_queue_head_t wqwrite; struct cdev cdev; }; struct PROC_INFO { struct PIPE_DEVICE *pdevice; int filetype; /* 0 = count, 1 = bufsize */ char strbuf[32]; }; static int set_bufsize(struct PIPE_DEVICE *pdevice, unsigned long arg); static int read_peek(struct PIPE_DEVICE *pdevice, unsigned long arg); static dev_t g_dev; static struct PIPE_DEVICE *g_pdevices; static int ndevices = NDEVICES; module_param(ndevices, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); static int __init generic_init(void) { int result; dev_t dev; int i, k; struct proc_dir_entry *pde_root, *pde_pipe; char namebuf[16]; printk(KERN_INFO "pipe-driver module initialization...\n"); if ((result = alloc_chrdev_region(&g_dev, 0, ndevices, "pipe-driver")) < 0) { printk(KERN_INFO "Cannot alloc char driver!...\n"); return result; } if ((g_pdevices = (struct PIPE_DEVICE *)kmalloc(sizeof(struct PIPE_DEVICE) * ndevices, GFP_KERNEL)) == NULL) { unregister_chrdev_region(g_dev, ndevices); return -ENOMEM; } if ((pde_root = proc_mkdir("pipe-driver", NULL)) == NULL) { kfree(g_pdevices); unregister_chrdev_region(g_dev, 1); return -ENOMEM; } for (i = 0; i < ndevices; ++i) { sprintf(namebuf, "pipe%d", i); if ((pde_pipe = proc_mkdir(namebuf, pde_root)) == NULL) { kfree(g_pdevices); unregister_chrdev_region(g_dev, 1); remove_proc_entry("pipe-driver", NULL); return -ENOMEM; } g_pdevices[i].head = g_pdevices[i].tail = g_pdevices[i].count = 0; g_pdevices[i].bufsize = DEF_PIPE_BUFSIZE; sema_init(&g_pdevices[i].sem, 1); init_waitqueue_head(&g_pdevices[i].wqread); init_waitqueue_head(&g_pdevices[i].wqwrite); cdev_init(&g_pdevices[i].cdev, &g_fops); dev = MKDEV(MAJOR(g_dev), i); g_pdevices[i].pipebuf = (char *)kmalloc(DEF_PIPE_BUFSIZE, GFP_KERNEL); result = cdev_add(&g_pdevices[i].cdev, dev, 1); if (g_pdevices[i].pipebuf == NULL || result < 0) { if (g_pdevices[i].pipebuf != NULL) kfree(g_pdevices[i].pipebuf); for (k = 0; k < i; ++k) { cdev_del(&g_pdevices[k].cdev); kfree(g_pdevices[k].pipebuf); } kfree(g_pdevices); unregister_chrdev_region(dev, ndevices); printk(KERN_ERR "Cannot add device!...\n"); return result; } if ((proc_create("count", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, pde_pipe, &g_proc_ops)) == NULL || proc_create("bufsize", S_IRUSR|S_IRGRP|S_IROTH, pde_pipe, &g_proc_ops) == NULL) { remove_proc_entry("pipe-driver", NULL); for (k = 0; k < i; ++k) { cdev_del(&g_pdevices[k].cdev); kfree(g_pdevices[k].pipebuf); } unregister_chrdev_region(g_dev, 1); return -ENOMEM; } } return 0; } static void __exit generic_exit(void) { int i; for (i = 0; i < ndevices; ++i) cdev_del(&g_pdevices[i].cdev); kfree(g_pdevices); remove_proc_entry("pipe-driver", NULL); unregister_chrdev_region(g_dev, ndevices); printk(KERN_INFO "pipe-driver module exit...\n"); } static int generic_open(struct inode *inodep, struct file *filp) { struct PIPE_DEVICE *pdevice; pdevice = container_of(inodep->i_cdev, struct PIPE_DEVICE, cdev); filp->private_data = pdevice; printk(KERN_INFO "pipe-driver opened...\n"); return 0; } static int generic_release(struct inode *inodep, struct file *filp) { printk(KERN_INFO "pipe-driver closed...\n"); return 0; } static ssize_t generic_read(struct file *filp, char *buf, size_t size, loff_t *off) { struct PIPE_DEVICE *pdevice; size_t esize, size1, size2; pdevice = (struct PIPE_DEVICE *)filp->private_data; if (size == 0) return 0; if (down_interruptible(&pdevice->sem)) return -ERESTARTSYS; while (pdevice->count == 0) { up(&pdevice->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(pdevice->wqread, pdevice->count > 0)) return -ERESTARTSYS; if (down_interruptible(&pdevice->sem)) return -ERESTARTSYS; } esize = MIN(pdevice->count, size); if (pdevice->tail <= pdevice->head) size1 = MIN(pdevice->bufsize - pdevice->head, esize); else size1 = esize; size2 = esize - size1; if (copy_to_user(buf, pdevice->pipebuf + pdevice->head, size1) != 0) { up(&pdevice->sem); return -EFAULT; } if (size2 != 0) if (copy_to_user(buf + size1, pdevice->pipebuf, size2) != 0) { up(&pdevice->sem); return -EFAULT; } pdevice->head = (pdevice->head + esize) % pdevice->bufsize; pdevice->count -= esize; up(&pdevice->sem); wake_up_interruptible_all(&pdevice->wqwrite); return esize; } static ssize_t generic_write(struct file *filp, const char *buf, size_t size, loff_t *off) { struct PIPE_DEVICE *pdevice; size_t esize, size1, size2; pdevice = (struct PIPE_DEVICE *)filp->private_data; if (down_interruptible(&pdevice->sem)) return -ERESTARTSYS; if (size > pdevice->bufsize) size = pdevice->bufsize; while (pdevice->bufsize - pdevice->count < size) { up(&pdevice->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(pdevice->wqwrite, pdevice->bufsize - pdevice->count >= size)) return -ERESTARTSYS; if (down_interruptible(&pdevice->sem)) return -ERESTARTSYS; } esize = MIN(pdevice->bufsize - pdevice->count, size); if (pdevice->tail >= pdevice->head) size1 = MIN(pdevice->bufsize - pdevice->tail, esize); else size1 = esize; size2 = esize - size1; if (copy_from_user(pdevice->pipebuf + pdevice->tail, buf, size1) != 0) { up(&pdevice->sem); return -EFAULT; } if (size2 != 0) if (copy_from_user(pdevice->pipebuf, buf + size1, size2) != 0) { up(&pdevice->sem); return -EFAULT; } pdevice->tail = (pdevice->tail + esize) % pdevice->bufsize; pdevice->count += esize; up(&pdevice->sem); wake_up_interruptible_all(&pdevice->wqread); return esize; } static long generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct PIPE_DEVICE *pdevice; printk(KERN_INFO "ioctl"); pdevice = (struct PIPE_DEVICE *)filp->private_data; switch (cmd) { case IOC_PIPE_GETCOUNT: return put_user(pdevice->count, (size_t *)arg); case IOC_PIPE_GETBUFSIZE: return put_user(pdevice->bufsize, (size_t *)arg); case IOC_PIPE_SETBUFSIZE: return set_bufsize(pdevice, arg); case IOC_PIPE_PEEK: return read_peek(pdevice, arg); default: return -ENOTTY; } return 0; } static int set_bufsize(struct PIPE_DEVICE *pdevice, unsigned long arg) { char *new_pipebuf; size_t size; if (arg > MAX_PIPE_BUFSIZE) return -EINVAL; if (arg <= pdevice->count) return -EINVAL; if (down_interruptible(&pdevice->sem)) return -ERESTARTSYS; if ((new_pipebuf = (char *)kmalloc(arg, GFP_KERNEL)) == NULL) { up(&pdevice->sem); return -ENOMEM; } if (pdevice->count != 0) { if (pdevice->tail <= pdevice->head) { size = pdevice->bufsize - pdevice->head; memcpy(new_pipebuf, pdevice->pipebuf + pdevice->head, size); memcpy(new_pipebuf + size, pdevice->pipebuf, pdevice->count - size); } else memcpy(new_pipebuf, pdevice->pipebuf + pdevice->head, pdevice->count); } pdevice->head = 0; pdevice->tail = pdevice->count; kfree(pdevice->pipebuf); pdevice->pipebuf = new_pipebuf; pdevice->bufsize = arg; up(&pdevice->sem); return 0; } static int read_peek(struct PIPE_DEVICE *pdevice, unsigned long arg) { size_t esize, size1, size2; struct PIPE_PEEK *userpp = (struct PIPE_PEEK *)arg; struct PIPE_PEEK pp; int status = 0; if (copy_from_user(&pp, userpp, sizeof(struct PIPE_PEEK)) != 0) return -EFAULT; if (pp.size == 0) return 0; if (down_interruptible(&pdevice->sem)) return -ERESTARTSYS; esize = MIN(pdevice->count, pp.size); if (pdevice->tail <= pdevice->head) size1 = MIN(pdevice->bufsize - pdevice->head, esize); else size1 = esize; size2 = esize - size1; if (copy_to_user(pp.buf, pdevice->pipebuf + pdevice->head, size1) != 0) { status = -EFAULT; goto EXIT; } if (size2 != 0) if (copy_to_user(pp.buf + size1, pdevice->pipebuf, size2) != 0) { status = -EFAULT; goto EXIT; } if (put_user(esize, &userpp->size) != 0) status = -EFAULT; EXIT: up(&pdevice->sem); return status; } static int proc_open(struct inode *inodep, struct file *filp) { const char *file_name = filp->f_path.dentry->d_name.name; const char *parent_file_name = filp->f_path.dentry->d_parent->d_name.name; struct PROC_INFO *pi; printk(KERN_INFO "pipe-driver proc file opened...\n"); if ((pi = (struct PROC_INFO *)kmalloc(sizeof(struct PROC_INFO), GFP_KERNEL)) == NULL) return -ENOMEM; pi->pdevice = &g_pdevices[parent_file_name[4] - '0']; if (!strcmp(file_name, "bufsize")) pi->filetype = 1; else if (!strcmp(file_name, "count")) pi->filetype = 0; filp->private_data = pi; return 0; } static int proc_release(struct inode *inodep, struct file *filp) { struct PROC_INFO *pi; pi = (struct PROC_INFO *)filp->private_data; kfree(pi); return 0; } static ssize_t proc_read(struct file *filp, char *buf, size_t size, loff_t *off) { struct PROC_INFO *pi; size_t esize, left; pi = (struct PROC_INFO *)filp->private_data; switch (pi->filetype) { case 0: sprintf(pi->strbuf, "%lu\n", (unsigned long)pi->pdevice->count); left = strlen(pi->strbuf) - *off; esize = left < size ? left : size; if (esize != 0) { if (copy_to_user(buf, pi->strbuf + *off, esize) != 0) return -EFAULT; *off += esize; } break; case 1: sprintf(pi->strbuf, "%lu\n", (unsigned long)pi->pdevice->bufsize); left = strlen(pi->strbuf) - *off; esize = left < size ? left : size; if (esize != 0) { if (copy_to_user(buf, pi->strbuf + *off, esize) != 0) return -EFAULT; *off += esize; } break; } return esize; } module_init(generic_init); module_exit(generic_exit); # Makefile obj-m += $(file).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 /* loadmulti (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$2 mode=666 /sbin/insmod ./${module}.ko ${@:3} || exit 1 major=$(awk "\$2 == \"$module\" {print \$1}" /proc/devices) for ((i = 0; i < $1; ++i)) do rm -f ${module}$i mknod ${module}$i c $major $i chmod $mode ${module}$i done /* unloadmulti (bu satırı dosyaya kopyalamayınız) */ #!/bin/bash module=$2 /sbin/rmmod ./$module.ko || exit 1 for ((i = 0; i < $1; ++i)) do rm -f ${module}$i done /* prog1.c */ #include #include #include #include #include #include #include "pipe-driver.h" #define PIPE_SIZE 4096 void exit_sys(const char *msg); int main(void) { int fd; char buf[PIPE_SIZE]; char *str; size_t len, bufsize, new_bufsize; if ((fd = open("pipe-driver3", O_WRONLY)) == -1) exit_sys("open"); for (;;) { printf("Enter text:"); fflush(stdout); fgets(buf, PIPE_SIZE, stdin); if ((str = strchr(buf, '\n')) != NULL) *str = '\0'; if (!strcmp(buf, "quit")) break; if (buf[0] == '!') { new_bufsize = atoi(&buf[1]); printf("%zd\n", new_bufsize); if (ioctl(fd, IOC_PIPE_SETBUFSIZE, new_bufsize) == -1) exit_sys("ioctl"); if (ioctl(fd, IOC_PIPE_GETBUFSIZE, &bufsize) == -1) exit_sys("ioctl"); printf("new pipe buffer size is %zu\n", bufsize); } else { len = strlen(buf); if (write(fd, buf, len) == -1) exit_sys("write"); printf("%lu bytes written...\n", (unsigned long)len); } } close(fd); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } /* prog2.c */ #include #include #include #include #include #include #include #include "pipe-driver.h" #define BUFFER_SIZE 4096 void exit_sys(const char *msg); int main(void) { int pdriver; char buf[BUFFER_SIZE + 1]; int count, size; ssize_t result; struct PIPE_PEEK pp; char *peekbuf; if ((pdriver = open("pipe-driver3", O_RDONLY)) == -1) exit_sys("open"); for (;;) { if (ioctl(pdriver, IOC_PIPE_GETCOUNT, &count) == -1) exit_sys("ioctl"); printf("There are (is) %d byte(s) in the pipe\n", count); printf("Size:"); scanf("%d", &size); if (size > BUFFER_SIZE) { printf("size is very long!...\n"); continue; } if (size == 0) break; if (size < 0) { pp.size = -size; if ((pp.buf = malloc(-size)) == NULL) { fprintf(stderr, "cannot allocate memory!...\n"); exit(EXIT_FAILURE); } if (ioctl(pdriver, IOC_PIPE_PEEK, &pp) == -1) exit_sys("ioctl"); peekbuf = (char *)pp.buf; for (size_t i = 0; i < pp.size; ++i) putchar(peekbuf[i]); putchar('\n'); free(pp.buf); } else { if ((result = read(pdriver, buf, size)) == -1) exit_sys("read"); buf[result] = '\0'; printf("%jd bytes read: %s\n", (intmax_t)result, buf); } } close(pdriver); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } > Hatırlatıcı Notlar: >> "epoll" fonksiyonu ile sistem genelinde izlenecek maksimum betimleyici sayısı "/proc/sys/fs/epoll/max_user_watches" dosyasında bulunmaktadır. Bu değer pekala değiştirilebilmektedir.