本文部份內容參考自官方文檔html
自2.6版本開始,linux內核開始使用sysfs文件系統,它的做用是將設備和驅動程序的信息導出到用戶空間,方便了用戶讀取設備信息,同時支持修改和調整。node
與ext系列和fat等文件系統不一樣的是,sysfs是一個系統在啓動時構建在內存中虛擬文件系統,通常被掛載在/sys目錄下,既然是存儲在內存中,天然掉電不保存,不能存儲用戶數據。linux
事實上,在以前也有一樣的虛擬文件系統創建了內核與用戶系統信息的交互,它就是procfs,可是procfs並不是針對設備和驅動程序,而是針對整個內核信息的抽象接口。數組
因此,內核開發人員以爲有必要使用一個獨立的抽象接口來描述設備和驅動信息,畢竟直到目前,驅動代碼在內核代碼中佔比很是大,內容也是很是龐雜。這樣能夠避免procfs的混亂,子系統之間的分層和分離老是能帶來更清晰地框架。數據結構
上文中提到,sysfs通常被掛載在/sys目錄下,咱們能夠經過ls /sys來查看sysfs的內容:框架
block bus class dev devices firmware fs kernel module power
首先須要注意的是,sysfs目錄下的各個子目錄中存放的設備信息並不是獨立的,咱們能夠當作不一樣的目錄是從不一樣的角度來描述某個設備信息。dom
一個設備可能同時有多個屬性,因此對於同一個驅動設備,同時存在於不一樣的子目錄下,例如:在以前的章節中,咱們使用create_dev_node.c編譯出create_dev_node.ko模塊,加載完成以後,咱們能夠在/sys下面看到當前驅動相關的目錄:函數
理解了這個概念,咱們再來簡覽/sys各目錄的功能:.net
若是你手頭上有設備的話,博主強烈建議動手操做一遍看看,這樣才能加深理解和記憶。指針
既然是承載用戶與內核接口的虛擬文件系統,那確定是要能被用戶所使用的,那麼咱們應該怎樣在/sys中添加描述文件呢?
首先,在上文中提到了,sysfs負責向用戶展現驅動在內核中的信息,那麼,確定是要從內核出發,在內核中進行建立。
Linux設備模型的核心是使用Bus、Class、Device、Driver四個核心數據結構,將大量的、不一樣功能的硬件設備(以及驅動該硬件設備的方法),以樹狀結構的形式,進行概括、抽象,從而方便Kernel的統一管理。
而硬件設備的數量、種類是很是多的,這就決定了Kernel中將會有大量的有關設備模型的數據結構。
這些數據結構必定有一些共同的功能,須要抽象出來統一實現,不然就會不可避免的產生冗餘代碼。這就是Kobject誕生的背景。
目前爲止,Kobject主要提供以下功能:
和sysfs虛擬文件系統配合,將每個Kobject及其特性,以文件的形式,開放到用戶空間(有關sysfs,會在其它文章中專門描述,本文不會涉及太多內容)。
注1:在Linux中,Kobject幾乎不會單獨存在。它的主要功能,就是內嵌在一個大型的數據結構中,爲這個數據結構提供一些底層的功能實現。
注2:Linux driver開發者,不多會直接使用Kobject以及它提供的接口,而是使用構建在Kobject之上的設備模型接口。
至於kset,其實能夠當作是kobject的集合,它也能夠當成kobject來使用,下面來看看這兩個結構體的內容:
struct kset { /*鏈表,記錄全部連入這個kset的kobject*/ struct list_head list; /*kset要在文件系統中生成一個目錄,一樣須要包含一個kobj結構體,以插入內核樹中*/ struct kobject kobj; ... } __randomize_layout;
struct kobject { const char *name; /*當前kobj的父節點,在文件系統中的表現就是父目錄*/ struct kobject *parent; /*kobj屬於的kset*/ struct kset *kset; /*kobj的類型描述,最主要的是其中的屬性描述,包含其讀寫方式*/ struct kobj_type *ktype; /*當前kobj的引用,只有當引用爲0時才能被刪除*/ struct kref kref; ... };
雖然linux基於C語言開發,可是其面向對象的思想無處不在,同時咱們能夠將kobject結構體當作是一個基類,提供基礎的功能,而其餘更爲複雜的結構繼承自這個結構體,延伸出不一樣的屬性。
介紹完kobject和kset的概念,固然是給出一個具體的實例來講明kobject和kset的使用:
kobject_create_test.c:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/slab.h> //指定license版本 MODULE_LICENSE("GPL"); static struct kobject *kob; static struct kset *kst; //設置初始化入口函數 static int __init hello_world_init(void) { int ret = 0; kst = kset_create_and_add("test_kset",NULL,kernel_kobj->parent); if(!kst) { printk(KERN_ALERT "Create kset failed\n"); kset_put(kst); } kob = kzalloc(sizeof(*kob),GFP_KERNEL); if(IS_ERR(kob)){ printk(KERN_ALERT "alloc failed!!\n"); return -ENOMEM; } ret = kobject_init_and_add(kob, NULL, NULL, "%s", "test_obj"); if(ret) { kobject_put(kob); kset_unregister(kst); } printk(KERN_DEBUG "kobj test project!!!\n"); return 0; } //設置出口函數 static void __exit hello_world_exit(void) { kobject_put(kob); kset_unregister(kst); printk(KERN_DEBUG "goodbye !!!\n"); } //將上述定義的init()和exit()函數定義爲模塊入口/出口函數 module_init(hello_world_init); module_exit(hello_world_exit);
在上文代碼中咱們建立了一個kset對象和一個kobject對象:
修改Makefile,而後編譯kobject_create_test.c:
make
加載模塊到內核:
sudo insmod kobject_create_test.ko
咱們可使用下面的指令查看:
ls -l /sys/test*
輸出:
/sys/test_kset total 0 /sys/test_obj: total 0
果真,在/sys目錄下生成了相應目錄。
事實上嚴格來講,上面的示例是有問題的:
Dec 23 08:44:28 beaglebone kernel: [21705.791009] kobject (daa8d880): must have a ktype to be initialized properly!
既然須要添加相應操做屬性,那咱們就再來詳細看看kobject結構體的源碼(爲避免陷入一些沒必要要的細節,博主只列出主幹部分,有興趣的朋友能夠自行查看源碼):
struct kobject { ... struct kobj_type *ktype; ... };
先從kobject中找到kobj_type,這是描述kobject屬性的結構體
struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; ... };
在kobj_type結構體中:
咱們再來看看sysfs_ops,這是對應文件的操做函數:
struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *, char *); //當咱們對/sys下目標文件進行讀操做時,調用show函數 ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); //當咱們對/sys下目標文件進行寫操做時,調用store函數 };
default_attrs描述了當前kobject的屬性:
struct attribute { const char *name; //做爲當前kobject目錄下的文件名 umode_t mode; //文件操做權限 }
不知道上面的結構體關係有沒有把你繞暈,咱們按照主幹線再來總結一下:
光說不練假把式,咱們來看看下面的示例kobject_create_with_attrs:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/delay.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/gpio.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Downey"); MODULE_DESCRIPTION("Kobject test!"); MODULE_VERSION("0.1"); static int led_status = 0; #define LED_PIN 26 /*************************kobject***************************/ static struct kobject *kob; static ssize_t led_show(struct kobject* kobjs,struct kobj_attribute *attr,char *buf) { printk(KERN_INFO "Read led\n"); return sprintf(buf,"The led_status status = %d\n",led_status); } static ssize_t led_status_show(struct kobject* kobjs,struct kobj_attribute *attr,char *buf) { printk(KERN_INFO "led status show\n"); return sprintf(buf,"led status : \n%d\n",led_status); } static ssize_t led_status_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) { printk(KERN_INFO "led status store\n"); if(0 == memcmp(buf,"on",2)) { gpio_set_value(LED_PIN,1); led_status = 1; } else if(0 == memcmp(buf,"off",3)) { gpio_set_value(LED_PIN,0); led_status = 0; } else { printk(KERN_INFO "Not support cmd\n"); } return count; } static struct kobj_attribute status_attr = __ATTR_RO(led); static struct kobj_attribute led_attr = __ATTR(led_status,0660,led_status_show,led_status_store); //Doesn't support 0666 in new version. static struct attribute *led_attrs[] = { &status_attr.attr, &led_attr.attr, NULL, }; static struct attribute_group attr_g = { .name = "kobject_test", .attrs = led_attrs, }; int create_kobject(void) { kob = kobject_create_and_add("obj_test",kernel_kobj->parent); return 0; } static void gpio_config(void) { if(!gpio_is_valid(LED_PIN)){ printk(KERN_ALERT "Error wrong gpio number\n"); return ; } gpio_request(LED_PIN,"led_ctr"); gpio_direction_output(LED_PIN,1); gpio_set_value(LED_PIN,1); led_status = 1; } static void gpio_deconfig(void) { gpio_free(LED_PIN); } static int __init sysfs_ctrl_init(void){ printk(KERN_INFO "Kobject test!\n"); gpio_config(); create_kobject(); sysfs_create_group(kob, &attr_g); return 0; } static void __exit sysfs_ctrl_exit(void){ gpio_deconfig(); kobject_put(kob); printk(KERN_INFO "Goodbye!\n"); } module_init(sysfs_ctrl_init); module_exit(sysfs_ctrl_exit);
在上述的示例中,咱們依舊引入了一個指示燈,值得注意的是,在示例中,博主並無將led_attrs傳入給kobject自己,而是使用sysfs_create_group()接口建立了一個目錄,目錄下的文件有led和led_status.
修改Makefile,而後使用make進行編譯。
加載相應內核模塊:
sudo insmod kobject_create_with_attrs.ko
加載完成以後若是你有在gpio26連上指示燈,能夠看到指示燈如今處於亮的狀態,同時咱們能夠用指令查看是否在/sys目錄下生成了相應的目錄:
ls -l /sys/obj_test/kobject_test/
輸出結果:
-r--r--r-- 1 root root 4096 Dec 25 14:45 led -rw-rw---- 1 root root 4096 Dec 25 14:52 led_status
根據程序中的實現,led顯示的內容是led的狀態,同時咱們能夠經過向led_status文件來控制led燈的狀態。
咱們先查看led文件:
cat /sys/obj_test/kobject_test/led
輸出:
The led_status status = 1
如咱們所料,對led的讀調用了led_show()函數,咱們再來試試led_status文件,在這以前,咱們先要賦予文件操做權限:
chmod 666 /sys/obj_test/kobject_test/led_status
而後往led_status文件中寫off來關閉led:
echo "off" > /sys/obj_test/kobject_test/led_status
果真,led被關閉,此時咱們再查看led文件發現led狀態爲0。
相信到這裏,你們對kobject、kset和sysfs有了一個基本的理解,博主在這裏再貼上一些kobject的注意事項:
關於kobject和kset更詳細的部分歡迎你們訪問官方文檔,這裏有更詳細的資料。
同時建議你們多多嘗試,這樣纔能有更深地理解。
kobject描述部分參考大牛的博客 (博主目前看過最好的講解linux內核的系列博客,強烈推薦!)
好了,關於linux驅動程序-sys_fs用戶接口使用就到此爲止啦,若是朋友們對於這個有什麼疑問或者發現有文章中有什麼錯誤,歡迎留言
原創博客,轉載請註明出處!
祝各位早日實現項目叢中過,bug不沾身.