轉載: https://www.cnblogs.com/sctb/...
互聯網上的好東西愈來愈少,且看且珍惜,請尊重版權。
configfs 是一個基於內存的文件系統,它提供了與sysfs相反的功能。sysfs 是一個基於文件系統的內核對象視圖,而configfs 是一個基於文件系統的內核對象管理器(或稱爲config_items)。html
在 sysfs 中,一個對象在內核中被建立(例如,當內核發現一個設備時),並在 sysfs 中註冊,而後它的屬性會出如今 sysfs 中,容許用戶空間經過 readdir(3)/read(2) 讀取,同時,也容許用戶經過 write(2) 修改一些屬性。很重要的一點,對象是在內核中被建立和銷燬的,內核控制着 sysfs 表示內核對象的生命週期,而 sysfs 不能干預。linux
configfs 的 config_item 是經過用戶空間顯式操做 mkdir(2) 建立, rmdir(2) 銷燬的。對象的屬性在 mkdir(2) 時出現,而且能夠經過 read(2) 和 write(2) 進行讀取或修改。和 sysfs 同樣,readdir(3) 能夠查詢 items 和屬性的列表,symlink(2) 能夠用來將 items 分組。與 sysfs 不一樣的是,configfs 表示的生命週期徹底由用戶空間控制,支持這些 items 的內核模塊必須對用戶控制作出響應。git
sysfs 和 configfs 應該同時存在於一個系統中,任何一個都不能取代另外一個。github
configfs 能夠編譯爲一個模塊,也能夠編譯到內核中。你能夠經過如下命令訪問它:shell
sudo mount -t configfs none /sys/kernel/config/
除非客戶端模塊也被加載,不然 configfs 樹是空的。這些模塊將它們的 item 類型做爲子系統註冊到 configfs 中,一旦客戶端子系統被加載,它將做爲一個(或多個)子目錄出如今 /sys/kernel/config/ 下。和 sysfs 同樣,不管是否掛載在 /sys/kernel/config/ 上,configfs樹始終存在。編程
經過 mkdir(2) 建立一個 item。此時,item 的屬性也會出現,readdir(3) 能夠查看有哪些屬性,read(2) 能夠查詢它們的默認值,write(2) 能夠存儲新的值。數組
注意:不要在一個屬性文件中存入多個屬性。安全
configfs 屬性的兩種類型:服務器
configfs 但願 write(2) 能一次性存儲整個緩衝區。在寫入通常的 configfs 屬性時,用戶空間進程應該先讀取整個文件,修改想要修改的部分,而後再把整個緩衝區寫回去。網絡
用戶空間的 write(2) 調用是有緩衝的,屬性的 write_bin_attribute 方法會在其被關閉時調用,所以,用戶空間必須檢查 close(2) 的返回碼,以便驗證操做是否成功完成。
爲了不惡意用戶對內核進行OOMing( "out of memory",溢出攻擊),每一個二進制屬性都有一個最大的緩衝區值。
當一個 item 須要被銷燬時,用 rmdir(2) 刪除它。若是一個 item 與(經過symlink(2))其餘 item 有連接,則不能銷燬該 item。能夠經過 unlink(2) 取消連接。
想象一下,有一個網絡塊設備(NBD)驅動程序,它容許你訪問遠程塊設備,咱們稱它爲FakeNBD。FakeNBD 使用 configfs 進行配置,顯然,須要提供一個很好用的用戶態程序,讓系統管理員可以方便地配置 FakeNBD,爲了使對 FakeNBD 的配置起做用,這個程序必須將配置的信息告訴驅動,這就是 configfs 的做用。
當加載 FakeNBD 驅動時,它會在 configfs 中註冊本身,用戶能使用 readdir(3) 看到它。
ls /sys/kernel/config fakenbd
用戶也可使用 mkdir(2) 建立 fakenbd 鏈接,名字是任意的。不過,示例中的名字可能已經被其餘工具(uuid 或者磁盤名)使用了。
mkdir /sys/kernel/config/fakenbd/disk1 ls /sys/kernel/config/fakenbd/disk1 target device rw
target 屬性包含 FakeNBD 要鏈接的服務器的IP地址,device 屬性是服務器上的設備,可想而知,rw屬性決定了鏈接是隻讀仍是讀寫。
echo 10.0.0.1 > /sys/kernel/config/fakenbd/disk1/target echo /dev/sda1 > /sys/kernel/config/fakenbd/disk1/device echo 1 > /sys/kernel/config/fakenbd/disk1/rw
就是這樣,經過 shell 就已經把設備配置好了。
configfs 中的每一個對象都是一個 config_item,config_item 就是子系統中的一個對象,它的屬性與該對象上的值相匹配。configfs 處理該對象及其屬性的文件系統表示,容許子系統忽略除基本 show/store 以外的其它全部交互。
items 是在 config_group 裏面建立和銷燬的。一個 group 是共享相同屬性和操做的 items 集合。items 由mkdir(2)建立,rmdir(2)刪除,這些 configfs 都會處理, group中 有一組操做來執行這些任務。
子系統是客戶端模塊的頂層。在初始化過程當中,客戶端模塊向 configfs 註冊子系統,子系統做爲一個目錄出如今 configfs 文件系統的最高層(根)。一個子系統也是一個 config_group,能夠作全部 config_group 能作的事情。
struct config_item { char *ci_name; char ci_namebuf[UOBJ_NAME_LEN]; struct kref ci_kref; struct list_head ci_entry; struct config_item *ci_parent; struct config_group *ci_group; struct config_item_type *ci_type; struct dentry *ci_dentry; }; void config_item_init(struct config_item *); void config_item_init_type_name(struct config_item *, const char *name, struct config_item_type *type); struct config_item *config_item_get(struct config_item *); void config_item_put(struct config_item *);
通常來講,config_item 結構體被嵌入到一個 container 結構體中,這個 container 結構體實際上表明瞭子系統正在作的事情,其中的 config_item 部分就是對象與 configfs 的交互方式。
不管是在源文件中靜態定義仍是由父 config_group 建立,建立 config_item 都必須調用一個 _init() 函數,這將初始化引用計數器並設置相應的字段。
config_item 的全部用戶都應該經過 config_item_get() 引用它,並在完成後經過 config_item_put() 函數放棄這個引用。
就其自己而言,config_item 只能在 configfs 中出現。一般,一個子系統但願這個 item 可以顯示和存儲屬性,並完成一些其餘事情,爲此,還須要一個 type 結構體。
換句話說,config_item_type 結構體主要用來完成除了顯示和存儲屬性以外的其餘事情。
struct configfs_item_operations { void (*release)(struct config_item *); int (*allow_link)(struct config_item *src, struct config_item *target); void (*drop_link)(struct config_item *src, struct config_item *target); }; struct config_item_type { struct module *ct_owner; struct configfs_item_operations *ct_item_ops; struct configfs_group_operations *ct_group_ops; struct configfs_attribute **ct_attrs; struct configfs_bin_attribute **ct_bin_attrs; };
config_item_type 最基本的功能是定義能夠對 config_item 進行哪些操做。全部被動態分配的 item 都須要提供 ct_item_ops->release() 方法。當 config_item 的引用計數爲零時,就會調用這個方法釋放它。
struct configfs_attribute { char *ca_name; struct module *ca_owner; umode_t ca_mode; ssize_t (*show)(struct config_item *, char *); ssize_t (*store)(struct config_item *, const char *, size_t); };
當一個 config_item 但願一個屬性以文件的形式出如今項目的 configfs 目錄中時,它必須定義一個 configfs_attribute 來描述它。而後,它將屬性添加到以 NULL 結尾的數組 config_item_type->ct_attrs 中。當 item 出如今 configfs 中時,屬性文件將以configfs_attribute->ca_name 文件名出現,configfs_attribute->ca_mode 指定文件權限。
若是一個屬性是可讀的,而且提供了一個 ->show 方法,那麼每當用戶空間要求對該屬性進行 read(2) 時,該方法( ->show )就會被調用。若是一個屬性是可寫的,而且提供了一個 ->store 方法,那麼每當用戶空間要求對該屬性進行 write(2) 時,該方法( ->store )就會被調用。
struct configfs_bin_attribute { struct configfs_attribute cb_attr; void *cb_private; size_t cb_max_size; };
當須要使用二進制blob來顯示 item 對應 configfs 目錄中文件的內容時,,會使用二進制屬性。
BLOB:binary large object,二進制大對象,是一個能夠存儲二進制文件的容器。
將二進制屬性添加到以 NULL 結尾的數組 config_item_type->ct_bin_attrs 中,item 就會出如今 configfs 中。屬性文件會以 configfs_bin_attribute->cb_attr.ca_name 做爲文件名, configfs_bin_attribute->cb_attr.ca_mode 指定文件權限。
cb_private 成員是提供給驅動程序使用的,cb_max_size 成員則指定了 vmalloc 緩衝區的最大可用空間。
若是二進制屬性是可讀的,而且 config_item 提供了 ct_item_ops->read_bin_attribute() 方法,那麼每當用戶空間要求對屬性進行 read(2) 時,該方法就會被調用。同理,用戶空間的 write(2) 操做會調用 ct_item_ops->write_bin_attribute() 方法。讀/寫會被緩衝,因此只會執行讀/寫的一個,屬性自己不須要關心。
config_item 不能憑空產生,惟一的方法是經過 mkdir(2) 在 config_group 上建立一個,該操做將觸發子 item 的建立。
struct config_group { struct config_item cg_item; struct list_head cg_children; struct configfs_subsystem *cg_subsys; struct list_head default_groups; struct list_head group_entry; }; void config_group_init(struct config_group *group); void config_group_init_type_name(struct config_group *group, const char *name, struct config_item_type *type);
config_group 結構包含一個 config_item,正確地配置該 item 意味着一個 group 能夠單獨做爲一個 item。
此外,group 還能夠完成更多工做:建立 item 或 group,這是經過在 group 中 config_item_type 指定的 group 操做來實現的。
struct configfs_group_operations { struct config_item *(*make_item)(struct config_group *group, const char *name); struct config_group *(*make_group)(struct config_group *group, const char *name); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); };
一個 group 經過提供 ct_group_ops->make_item() 方法來建立子項目。若是提供了這個方法,當在 group 目錄中使用 mkdir(2) 時,該方法被調用。當 ct_group_ops->make_item() 方法被調用,子系統將分配一個新的 config_item( or 更多是它的 container 結構體),初始化並將其返回給 configfs,而後,configfs 將填充文件系統樹以反映新的 item。
若是子系統但願子 item 自己是一個 group,子系統提供 ct_group_ops->make_group(),其餘的操做都是同樣,使用 group 上的 group _init() 函數初始化。
最後,當用戶空間對 item 或 group 調用 rmdir(2) 時,會調用 ct_group_ops->drop_item() 方法。因爲 config_group 也是一個 config_item,因此不須要單獨的 drop_group() 方法。子系統必須調用 config_item_put() 函數釋放 item 分配時初始化的引用,若是除了該操做,子系統不須要要作其它操做,能夠省略 ct_group_ops->drop_item() 方法,configfs 將表明子系統對 item 調用 config_item_put() 方法。
重要:drop_item() 的返回值爲 void,所以不能失敗。當 rmdir(2) 被調用時,configfs 將會從文件系統樹中刪除該 item(假設沒有子 item 正在使用它),子系統負責對此操做作出響應。若是子系統在其餘線程中有對該 item 的引用,那麼內存是安全的,該 item 從子系統中真正消失可能還須要一段時間,但它已經從 configfs 中消失了。
當 drop_item() 被調用時,item 的連接已經被拆掉了,它在父 item 上再也不有引用,在 item 的層次結構中也沒有位置。若是客戶端須要在這個拆分發生以前作一些清理工做,子系統能夠實現 ct_group_ops->disconnect_notify() 方法。該方法在 configfs 從文件系統結構中刪除 item 後,item 從父 group 中刪除前被調用,和drop_item()同樣,disconnect_notify() 的返回值也爲 void,不能失敗。客戶端子系統不該該在這裏刪除任何引用,由於這必須在 drop_item() 中進行。
當一個 config_group 還有子 item 的時候,它是不能被刪除的,這在 configfs 的 rmdir(2) 代碼中有實現。->drop_item() 不會被調用,由於該 item 沒有被刪除,rmdir(2) 也將失敗,由於目錄不是空的。
一個子系統必須註冊本身,一般是在 module_init 的時候,該操做告訴 configfs 讓子系統出如今文件樹中。
struct configfs_subsystem { struct config_group su_group; struct mutex su_mutex; }; int configfs_register_subsystem(struct configfs_subsystem *subsys); void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
一個子系統由一個頂級 config_group 和一個 mutex 組成,這個 group 是建立子 config_items 的地方。對於一個子系統,這個 group 一般是靜態定義的,在調用 configfs_register_subsystem() 以前,子系統必須經過 group _init() 函數來初始化這個 group,而且還必須初始化 mutex。
當調用註冊函數返回後,子系統會一直存在,而且能夠在 configfs 中看到。這時,用戶程序能夠調用 mkdir(2),子系統必須爲此作好準備。
理解這些基本概念的最好例子是 samples/configfs/configfs_sample.c 中的 simple_children subsystem/group 和 simple_child item,它們展現了一個顯示和存儲屬性的簡單對象,以及一個建立和銷燬這些子 item 的簡單 group。
configfs_sample.c : https://github.com/torvalds/l...
configfs 還提供了一些額外的功能。因爲 config_groups 和 config_items 出如今文件系統中,因此它們被安排在一個層次結構中。一個子系統是絕對不會接觸到文件系統部分的,可是子系統可能會對這個層次結構感興趣。出於這個緣由,層次結構是經過 config_group->cg_children 和 config_item->ci_parent 結構體成員表示的。
子系統能夠瀏覽 cg_children 列表和 ci_parent 指針來查看子系統建立的樹。這可能會與 configfs 對層次結構的管理髮生衝突,因此 configfs 使用子系統的 mutex 來保護修改。不管什麼時候子系統要瀏覽層次結構,都必須在子系統 mutex 的保護下進行。
當一個新分配的 item 尚未被連接到這個層次結構中時,子系統將沒法得到 mutex, 一樣,當一個正在被刪除的 item 尚未解除連接時,子系統也沒法獲取mutex。這意味着,當一個 item 在 configfs 中時,項目的 ci_parent 指針永遠不會是 NULL,並且,同一時刻,item 只會存在一個父 item 的 cg_children 列表中,這容許子系統在持有 mutex 時信任 ci_parent 和 cg_children。
configfs 經過 group->item 爲父/子關係提供了一個簡單的 group,可是,一般狀況下,在更大的環境中須要在父/子關係以外進行聚合,這是經過 symlink(2) 實現的。
一個 config_item 能夠提供 ct_item_ops->allow_link() 和 ct_item_ops->drop_link() 方法。若是 ->allow_link() 方法存在,就能夠調用 symlink(2),將 config_item 做爲連接的來源。這些連接只容許在 configfs 的 config_items 之間進行,任何在 configfs 文件系統以外的 symlink(2) 調用都會被拒絕。
當 symlink(2) 被調用時,源 config_item 的 ->allow_link() 方法會被本身和一個目標 item 調用,若是源 item 容許連接到目標 item,則返回0,若是源 item 只想連接到某一類型的對象(例如,在它本身子系統中的對象),它能夠拒絕該連接。
當對符號連接調用 unlink(2) 時,經過 ->drop_link() 方法通知源 item,和 ->drop_item() 方法同樣,這也是一個返回值爲 void 的函數,不能失敗,子系統負責響應因該函數執行致使的變化。
當一個 config_item 連接到任何其它 item 時,它不能被刪除,當一個 item 連接到它時,也不能被刪除。在 configfs 中不容許使用軟連接。
一個新的 config_group 可能但願有兩種類型的子 config_items,雖然這能夠經過在 ->make_item() 中的 magic names 來編寫,但更顯式的方法是讓用戶空間可以看到這種不一樣。
configfs 提供了一種方法,即在建立父 group 時,在其內部自動建立一個或多個子 group,而不是把行爲互不相同的 item 放在同一個 group 中。所以,mkdir("parent") 的結果是 "parent","parent/subgroup1",直到 "parent/subgroupN"。如今,type 1 的 item 能夠在目錄 "parent/subgroup1" 中建立,type N 的 item 能夠在目錄 "parent/subgroupN" 中建立。
這些自動建立的子 group,或者說默認 group,並不影響父 group 的其餘子 group,若是 ct_group_ops->make_group() 存在,其餘子 group 也能夠直接在父 group 上建立。
configfs 子系統經過 configfs_add_default_group() 函數將默認 group 添加到父 config_group 結構體中來指定它們,每一個添加的 group 與父 group 同時被填充到 configfs 樹中。一樣地,它們也會與父 group 同時被刪除,不會另外通知,當一個 ->drop_item() 方法調用通知子系統其父 group 即將消失時,意味着與該父 group 關聯的每一個默認子 group 也即將消失。
所以,不能直接經過 rmdir(2) 來刪除默認 group,當父 group 的 rmdir(2) 檢查子 group 時,也不會考慮它們(默認 group)。
有時,某些驅動程序依賴於特定的 configfs item,例如,掛載 ocfs2 依賴於心跳區域 item,若是使用 rmdir(2) 刪除該區域 item,則 ocfs2 掛載會出錯 或轉爲 readonly 模式。
configfs 提供了兩個額外的 API 調用:configfs_depend_item() 和 configfs_undepend_item(),客戶端驅動程序能夠在一個現有的 item 上調用 configfs_depend_item() 來告訴 configfs 它是被依賴的。若是其餘程序 rmdir(2) 該 item,configfs 將返回 -EBUSY,當這個 item 再也不被依賴時,客戶端驅動會調用 configfs_undepend_item() 取消依賴。
這些 API 不能在任何的 configfs 回調下調用,由於它們會衝突,不過,它們能夠阻塞和重分配。客戶端驅動不能憑本身的直覺調用它們,它應該提供一個外部子系統調用的 API。
這是如何工做的呢?想象一下 ocfs2 的掛載過程。當它掛載時,它會要求一個心跳區域 item,這是經過對心跳代碼的調用來完成的。在心跳代碼中,區域 item 被查找出來,同時,心跳代碼會調用 configfs_depend_item(),若是成功了,那麼心跳代碼就知道這個區域是安全的,能夠交給 ocfs2,若是失敗了,ocfs2 將被卸載,心跳代碼優雅地傳遞出一個錯誤。
注:可提交的 item 目前還沒有使用。
有些 config_item 不能有一個有效的初始狀態,也就是說,不能爲 item 的屬性指定默認值(指定了默認值, item 才能起做用),用戶空間必須配置一個或多個屬性後,子系統才能夠啓動這個 item 所表明的實體。
考慮一下上面的 FakeNBD 設備,若是沒有目標地址和目標設備,子系統就不知道要導入什麼塊設備。這個例子假設子系統只是簡單地等待,直到全部屬性都配置好了,再開始鏈接。每次屬性存儲操做都檢查屬性是否被初始化的方法確實可行,但這會致使在知足條件(屬性都已經初始化)的狀況下,每次屬性存儲操做一定觸發鏈接。
更好的作法是用一個顯式的操做來通知子系統 config_item 已經準備好了。更重要的是,顯式操做容許子系統提供反饋,說明屬性是否以合理的方式被初始化,configfs 以可提交 item (commitable item)的形式提供了這種反饋。
configfs 仍然只使用正常的文件系統操做,經過 rename(2) 提交的一個item,會從一個可修改的目錄移動到一個不能修改的目錄。
任何提供 ct_group_ops->commit_item() 方法的 group 都有可提交 item,當這個 group 出如今 configfs 中時,mkdir(2) 將不會直接在該 group 中工做,相反,該 group 將有兩個子目錄 "live" 和 "pending",live" 目錄不支持 mkdir(2) 或 rmdir(2) ,它只容許 rename(2),"pending" 目錄容許使用 mkdir(2) 和 rmdir(2)。若是在 "pending" 目錄中建立了一個 item,它的屬性能夠隨意修改,用戶空間經過將 item 重命名到 "live" 目錄中來提交,此時,子系統接收到 ->commit_item() 回調。若是全部所需的屬性都被填充,該方法返回0,item 被移到 "live" 目錄下。
因爲 rmdir(2) 在 "live" 目錄中不起做用,因此必須關閉一個 item,或者說使其 "uncommitted",一樣,這也是經過 rename(2) 來完成的,此次是從 "live" 目錄回到 "uncommitted" 目錄,並經過 ct_group_ops->uncommit_object() 方法通知子系統。
參考: https://www.kernel.org/doc/Do...