1. 程式人生 > >sysfs檔案系統

sysfs檔案系統

一、sysfs

sysfs是用來向用戶空間匯出核心物件的一種檔案系統,通過它,使用者空間程式可以檢視、甚至修改核心資料結構。該檔案系統是基於核心資料結構kobject建立起來的,同時該檔案系統的目錄結構反映了相關核心資料結構的層次結構。由於kobject是組成裝置模型的基本結構,因此sysfs也包括了系統中裝置的資訊,它提供了系統硬體的拓撲資訊。
由於sysfs提供了訪問、修改核心資料結構的一種手段,因而核心模組也可以通過該檔案系統向用戶空間匯出介面用於訪問、修改模組的引數。
在引入sysfs後,核心向用戶到處介面的方式有/proc檔案系統,sysfs檔案系統,ioctl命令。
雖然sysfs的資訊來自於kobject,但是kobject和sysfs的關聯不是自動建立的,必須通過kobject_add才能把一個kobject新增到sysfs中。
由於核心中使用kobject的主要部件是硬體相關的部分,因而sysfs包含的最主要的內容就是硬體相關的內容,包括匯流排、裝置、驅動程式等。Sysfs掛載點為/sys
rover$ ls /sys
block  bus  class  dev  devices  firmware  fs  hypervisor  kernel  module  power

1.1
sysfs節點

sysfs使用sysfs_dirent來表示一個目錄或檔案節點,其資料結構如下:
struct sysfs_dirent {
        atomic_t                s_count;
        atomic_t                s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map      dep_map;
#endif
        struct sysfs_dirent     *s_parent;
        const char              *s_name;


        struct rb_node          s_rb;


        union {
                struct completion       *completion;
                struct sysfs_dirent     *removed_list;
        } u;


        const void              *s_ns; /* namespace tag */
        unsigned int            s_hash; /* ns + name hash */
        union {
                struct sysfs_elem_dir           s_dir;
                struct sysfs_elem_symlink       s_symlink;
                struct sysfs_elem_attr          s_attr;
                struct sysfs_elem_bin_attr      s_bin_attr;
        };  


        unsigned short          s_flags;
        umode_t                 s_mode;
        unsigned int            s_ino;
        struct sysfs_inode_attrs *s_iattr;
};
其中
  • s_count:該sysfs節點的引用計數。它在建立一個新的sysfs_dirent時被設定為0。
  • s_active:該sysfs節點的活動應用計數。每當核心部件訪問一個sysfs節點的內容sysfs_elem_*時就必須持有該引用計數,在訪問完畢後要相應的更新該引用計數。之所以在引用計數之外再新增該引數是因為,如果沒有該域,則只要使用者打開了一個sysfs檔案,就無法將其刪除了。再增加了該計數後,只要它為負值,就不能再操作該sysfs節點了(這個限制新增在獲取活動引用計數上,如果引用計數為負值,則無法獲取引用計數,因此獲取活動引用計數時,必須檢查返回值), 當該節點的所有使用者都消失時,就可以刪除該sysfs節點了。它在建立新的sysfs_dirent時被設定為1。
  • s_name:該sysfs_dirent的名字。
  • s_parent:指向父節點
  • s_rb:新的程式碼使用紅黑樹來管理同一目錄下的子節點,同一目錄下的兄弟節點被新增到一棵紅黑樹中,該域就是紅黑樹資料結構。紅黑樹的根為s_dir的children域。
  • s_ino:該sysfs節點在sysfs中的id。
  • s_flags:包含了該sysfs_dirent的型別資訊和標誌資訊。型別指示了該sysfs_dirent是那種型別,型別包括SYSFS_DIR、SYSFS_KOBJ_ATTR、SYSFS_KOBJ_BIN_ATTR、SYSFS_KOBJ_LINK,每種型別對應於一個數據項結構,由於一個數據只能表示一種型別,因而這4種類型對應的資料結構被包括在一個列舉型別中。四種類型對應的資料結構分別為:
struct sysfs_elem_dir {
	struct kobject		*kobj;
	unsigned long		subdirs;
	/* children rbtree starts here and goes through sd->s_rb */
	struct rb_root		children;
};
struct sysfs_elem_symlink {
	struct sysfs_dirent	*target_sd;
};
struct sysfs_elem_attr {
	struct attribute	*attr;
	struct sysfs_open_dirent *open;
};
struct sysfs_elem_bin_attr {
	struct bin_attribute	*bin_attr;
	struct hlist_head	buffers;
};
struct sysfs_inode_attrs {
	struct iattr	ia_iattr;
	void		*ia_secdata;
	u32		ia_secdata_len;
};
sysfs使用dentry的d_fsdata來儲存對應的sysfs_dirent,因此通過dentry就能找到對應的sysfs_dirent。
在sysfs節點的四種類型中:
  • 目錄結構包含了該目錄的紅黑樹的根節點資訊以及該目錄節點的kobject資訊。
  • 連線結構包含了指向目標sysfs節點的指標資訊
  • 而另外兩種則包含了屬性資訊,即該節點對應的屬性資訊,屬性資訊對使用者來說是最關鍵的。

1.2 sysfs節點屬性

1.2.1屬性資訊定義如下:

struct attribute {
	const char		*name;
	umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};
  • name為屬性的名字,並且會作為檔名字出現在sysfs檔案系統中
  • mode即該屬性的訪問屬性,最終反映為sysfs檔案系統中該屬性檔案的訪問屬性

1.2.2 二進位制屬性資訊

其定義如下
struct bin_attribute {
	struct attribute	attr;
	size_t			size;
	void			*private;
	ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
			char *, loff_t, size_t);
	ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *,
			 char *, loff_t, size_t);
	int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
		    struct vm_area_struct *vma);
};

該屬性各個域的含義很明顯。

二進位制屬性本身就包含了該屬性的讀寫操作指標資訊,它和常規的屬性是不一樣的,因而二進位制屬性檔案必須顯式建立,它不能作為kobject的預設屬性。相關的API如下:

int sysfs_create_bin_file(struct kobject *kobj,  const struct bin_attribute *attr);
void sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr);

1.2.3 建立連結檔案

sysfs中常見的一個操作是建立連結檔案,其相關API如下:

int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);
void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, const char *name);
這裡只討論了連結檔案,二進位制數形檔案的建立,根據kobject的內容屬性好像少了目錄和常規屬性的建立,常規屬性的建立在kobject中有描述,而目錄的建立則由kobject框架自動完成,不需要呼叫者關心。

1.3 自定義屬性

如果一個核心部件需要定義自己的屬性,實現方案也很簡單,只需要定義如下的結構:
struct self_attribute{
    struct attributeattr;
    …
};
然後再sysfs需要屬性時,將slef_attriute的attr提供給sysfs即可。這樣sysfs仍可以使用自己所通用的struct attribute結構,而模組自己可以很容易的通過contanier_of巨集得到自己所定義的自定義屬性。

1.4 sysfs檔案系統初始化

使用sysfs檔案系統之前必須將其初始化並且掛載到系統中,該過程和proc檔案系統完全類似。sysfs檔案系統的的初始化主要完成:
  •  呼叫kmem_cache_create建立sysfs檔案系統所使用的專用緩衝區
  •  呼叫register_filesystem註冊sysfs檔案系統,這裡會提供proc檔案系統自己的file_system_type,其中包括了用於mount的函式指標。在執行mount的時候會用到這些資訊,並最終找到mount函式進行掛載操作。
在sysfs的mount函式中會呼叫sysfs_fill_super,它會
  • 給出sysfs檔案系統超級塊所需要的資訊(比如檔案系統的超級塊操作函式指標,超級塊大小等)。
  • 初始化根目錄的inode,在這個過程中會指定與之對應的inode_operations和file_operations,有了這些資訊後,VFS就可以在該檔案系統上進行各種操作了(建立、刪除、查詢檔案)。
  • 建立sysfs檔案系統的根目錄, 並且將dentry的d_fsdata指向根目錄的sysfs_dirent結構sysfs_root。sysfs的每一個節點都對應一個dentry。
從上述描述可以看出,其和proc檔案系統的初始化、以及mount是類似的。

設定sysfs的inode_operations和file_operations的工作是由sysfs_get_inode函式完成的,它最終呼叫函式sysfs_init_inode來完成這個工作。

1.5 sysfs檔案節點刪除

在刪除一個sysfs節點時,sysfs會首先保證核心沒有任何對sysfs內容的引用,這通過s_active來保證,然後在對該kobject的常規引用也全部消失後,就會真正刪除該kobject。其流程大致為:

  1. 首先將一個特殊值SD_DEACTIVATED_BIAS加到s_active上,然後檢查結果是否仍然為該值,如果結果仍然是該值,則和其它檔案的刪除類似,根據引用計數是否為0進行相應的處理,否則
  2. 在該kobject的u.completion上等待
能夠結束該等待的就是對活動引用計數的put動作,在活動引用計數的put操作中,會進行檢查,如果減1後的值為SD_DEACTIVATED_BIAS則就呼叫complete(sd->u.completion)結束該等待。

1.6 sysfs檔案系統的操作

在檔案操作中,inode檔案操作相對比較簡單,如果是目錄,則提供有一個查詢函式用於查詢一個dentry,如果是普通檔案,則提供有設定、檢視檔案屬性的函式。相對比較負責的是實際的檔案操作。
為了在核心和使用者空間互動資料,sysfs還提供了一個數據結構來緩衝這種資料:
struct sysfs_buffer {
	size_t			count;
	loff_t			pos;
	char			* page;
	const struct sysfs_ops	* ops;
	struct mutex		mutex;
	int			needs_read_fill;
	int			event;
	struct list_head	list;
};
  • count:緩衝區中資料長度
  • pos:當前位置
  • page:指向儲存資料的緩衝區
  • ops:實際的檔案操作指標
  • needs_read_fill:緩衝區資料是否需要填充,在第一次讀的時候肯定需要,如果在讀之後沒有寫發生,則可以直接從緩衝區讀而不需要重新填充。
  • list:關聯到同一個sysfs的緩衝區列表
緩衝區列表與開啟該sysfs的檔案以及該sysfs的sysfs_dirent的關係如下所示:
  Sysfs_ops結構的內容如下,需要進行讀寫的節點需要提供相應的函式。
struct sysfs_ops {
	ssize_t	(*show)(struct kobject *, struct attribute *,char *);
	ssize_t	(*store)(struct kobject *,struct attribute *,const char *, size_t);
	const void *(*namespace)(struct kobject *, const struct attribute *);
};

1.5.1開啟檔案

如果是普通屬性檔案,則在sysfs_get_inode中會將檔案操作指標設定為sysfs_file_operations,其中包含了開啟檔案的函式sysfs_open_file,它會完成開啟檔案的操作。
在最新的程式碼中,要求所有的屬性必須指定一個ktype,ktype中包含了一個struct sysfs_ops指標,用於儲存相關聯的檔案操作指標。因此相關的程式碼邏輯比較簡單:
  •  找到對應的ktype
  •  將sysfs_dirent的活動引用加1
  •  獲得檔案操作指標
  •  分配一個buffer,並對其進行初始化,同時將將檔案操作賦給buffers
  •  如果是第一次開啟該sysfs節點,則初始化一個用於poll的等待佇列,否則僅將新的buffer新增到buffer的列表中
  •  將sysfs_dirent的活動引用減1

1.5.2讀檔案

如果是普通屬性檔案,則在sysfs_get_inode中會將檔案操作指標設定為sysfs_file_operations,其中包含了開啟檔案的函式sysfs_read_file。該函式比較簡單,它會
  • 找到開啟檔案對應的buffer
  • 檢視是否需要填充
  • 如果需要填充,則
    • 將sysfs_dirent的活動引用加1
    • 呼叫buffer中的sysfs_ops中的show方法來將資訊讀取到buffer中
    • 將sysfs_dirent的活動引用減1
  •  如果不需要填充,則直接從buffer中讀取

1.5.3寫檔案

如果是普通屬性檔案,則在sysfs_get_inode中會將檔案操作指標設定為sysfs_file_operations,其中包含了開啟檔案的函式sysfs_write_file,該函式也很簡單,它。
  • 找到開啟檔案對應的buffer
  • 首先將內容寫到buffer中
  • 將sysfs_dirent的活動引用加1
  • 呼叫buffer的sysfs_ops中的store方法來完成最後的寫
  • 將sysfs_dirent的活動引用減1
從上述描述可以看出,一個sysfs的實現者只需要將自己的真正的讀寫函式放到kobject的kobj_type的sysfs_ops中即可被呼叫到。
由於sysfs檔案系統是基於kobject構建的,而kobject內嵌在其它各種核心資料結構中,相應的核心框架都已經包括了註冊到sysfs中的程式碼,因而只需要遵循相應模組所屬框架的規則即可完成向sysfs新增新的內容。