## 14.2.?低級 sysfs 操作
kobject 是在 sysfs 虛擬文件系統之后的機制. 對每個在 sysfs 中發現的目錄, 有一個 kobject 潛伏在內核某處. 每個感興趣的 kobject 也輸出一個或多個屬性, 它出現在 kobject 的 sysfs 目錄, 作為包含內核產生的信息的文件. 本節檢查 kobject 和 sysfs 如何在低層交互.
使用 sysfs 的代碼應當包含 <linux/sysfs.h>.
使一個 kobject 在 sysfs 出現僅僅是調用 kobject_add 的事情. 我們已經見到這個函數作為添加一個 kobject 到一個 kset 的方式; 在 sysfs 中創建入口也是它的工作的一部分. 有一些事情值得知道, 關于 sysfs 入口如何創建:
-
kobjects 的 sysfs 入口一直為目錄, 因此一個對 kobject_add 的調用導致在sysfs 中創建一個目錄. 常常地, 這個目錄包含一個或多個屬性; 我們稍后見到屬性如何指定.
-
分配給 kobject 的名子( 用 kobject_set_name ) 是給 sysfs 目錄使用的名子. 因此, 出現在 sysfs 層次的相同部分的 kobjects 必須有獨特的名子. 分配給 kobjects 的名子也應當是合理的文件名子: 它們不能包含斜線字符, 并且空白的使用強烈不推薦.
-
sysfs 入口位于對應 kobject 的 parent 指針的目錄中. 如果 parent 是 NULL 當 kobject_add 被調用時, 它被設置為嵌在新 kobject 的 kset 中的 kobject; 因此, sysfs 層級常常匹配使用 kset 創建的內部層次. 如果 parent 和 kset 都是 NULL, sysfs 目錄在頂級被創建, 這幾乎當然不是你所要的.
使用我們至今所描述的, 我們可以使用一個 kobject 來在 sysfs 中創建一個空目錄. 常常地, 你想做比這更有趣的事情, 因此是時間看屬性的實現.
### 14.2.1.?缺省屬性
當被創建時, 每個 kobject 被給定一套缺省屬性. 這些屬性通過 kobj_type 結構來指定. 這個結構, 記住, 看來如此:
~~~
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
~~~
default_attr 成員列舉了對每個這樣類型的 kobject 被創建的屬性, 并且 sysfs_ops 提供方法來實現這些屬性. 我們從 default_attrs 開始, 它指向一個指向屬性結構的指針數組:
~~~
struct attribute {
char *name;
struct module *owner;
mode_t mode;
};
~~~
在這個結構中, name 是屬性的名子( 如同它出現在 kobject 的 sysfs 目錄中), owner 是一個指向模塊的指針(如果有一個), 模塊負責這個屬性的實現, 并且 mode 是應用到這個屬性的保護位. mode 常常是 S_IRUGO 對于只讀屬性; 如果這個屬性是可寫的, 你可以扔出 S_IWUSR 來只給 root 寫權限( modes 的宏定義在 <linux/stat.h> 中). default_attrs 列表中的最后一個入口必須用 0 填充.
default_attr 數組說明這些屬性是什么, 但是沒有告訴 sysfs 如何真正實現這些屬性. 這個任務落到 kobj_type->sysfs_ops 成員, 它指向一個結構, 定義為:
~~~
struct sysfs_ops {
ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);
ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);
};
~~~
無論何時一個屬性從用戶空間讀取, show 方法被用一個指向 kobject 的指針和適當的屬性結構來調用. 這個方法應當將給定屬性值編碼進緩沖, 要確定沒有覆蓋它( 它是 PAGE_SIZE 字節), 并且返回實際的被返回數據的長度. sysfs 的慣例表明每個屬性應當包含一個單個的, 人可讀的值; 如果你有許多消息返回, 你可要考慮將它分為多個屬性.
同樣的 show 方法用在所有的和給定 kobject 關聯的屬性. 傳遞到函數的 attr 指針可用來決定需要哪個屬性. 一些 show 方法包含對屬性名子的一系列測試. 其他的實現將屬性結構嵌入另一個結構, 來包含需要返回屬性值的信息; 在這種情況下, container_of 可能用在 show 方法中來獲得一個指向嵌入結構的指針.
store 方法類似; 它應當將存在緩沖的數據編碼( size 包含數據的長度, 這不能超過 PAGE_SIZE ), 存儲和以任何有意義的的方式響應新數據, 并且返回實際編碼的字節數. store 方法只在屬性的許可允許寫才被調用. 當編寫一個 store 方法時, 不要忘記你在接收來自用戶空間的任意信息; 你應當在采取對應動作之前非常小心地驗證它. 如果到數據不匹配期望, 返回一個負的錯誤值, 而不是可能地做一些不想要的和無法恢復的事情. 如果你的設備輸出一個自銷毀的屬性, 你應當要求一個特定的字符串寫到那里來引發這個功能; 一個偶然的, 隨機寫應當只產生一個錯誤.
### 14.2.2.?非缺省屬性
在許多情況中, kobject 類型的 default_attrs 成員描述所有的 kobject 會擁有的屬性. 但是那不是一個設計中的限制; 屬性隨意可以添加到和刪除自 kojects. 如果你想添加一個新屬性到一個 kobject 的 sysfs 目錄, 簡單地填充一個屬性結構并且傳遞它到:
~~~
int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
~~~
如果所有都進行順利, 文件被使用在屬性結構中給定的名子創建, 并且返回值是 0; 否則, 返回通常的負錯誤碼.
注意, 相同的 show() 和 store() 函數被調用來實現對新屬性的操作. 在你添加一個新的, 非缺省屬性到 kobject, 你應當任何必要的步驟來確保這些函數知道如何實現這個屬性.
為去除一個屬性, 調用:
~~~
int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
~~~
在調用后, 這個屬性不再出現在 kobject 的 sysfs 入口. 要小心, 但是, 一個用戶空間進程可能有一個打開的那個屬性的文件描述符, 并且在這個屬性已經被去除后 show 和 store 調用仍然可能.
### 14.2.3.?二進制屬性
sysfs 慣例調用所有屬性來包含一個單個的人可讀文本格式的值. 就是說, 只是偶然地很少需要來創建能夠處理大量二進制數據的屬性. 這個需要真正地只出現在必須傳遞數據, 不可動地, 在用戶空間和設備. 例如, 上載固件到設備需要這個特性. 當這樣一個設備在系統中遇到, 一個用戶程序可以被啟動( 通過熱插拔機制); 這個程序接著傳遞固件代碼到內核通過一個二進制 sysfs 屬性, 如同在"內核固件接口"一節中所示.
二進制屬性使用一個 bin+attribute 結構來描述:
~~~
struct bin_attribute {
struct attribute attr;
size_t size;
ssize_t (*read)(struct kobject *kobj, char *buffer, loff_t pos, size_t size);
ssize_t (*write)(struct kobject *kobj, char *buffer, loff_t pos, size_t size);
};
~~~
這里, attr 是一個屬性結構, 給出名子, 擁有者, 和這個二進制屬性的權限, 并且 size 是這個二進制屬性的最大大小(或者 0 , 如果沒有最大值). read 和 write 方法類似于正常的字符驅動對應物; 它們一次加載可被多次調用, 每次調用最大一頁數據. 對于 sysfs 沒有辦法來指示最后一個寫操作, 因此實現二進制屬性的代碼必須能夠以其他方式決定數據的結束.
二進制屬性必須明確創建; 它們不能建立為缺省屬性. 為創建一個二進制屬性, 調用:
~~~
int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);
~~~
去除二進制屬性可用:
~~~
int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
~~~
### 14.2.4.?符號連接
sysfs 文件系統有通常的樹結構, 反映它代表的 kobjects 的層次組織. 但是內核中對象間的關系常常比那個更加復雜. 例如, 一個 sysfs 子樹 (/sys/devices )代表所有的系統已知的設備, 而其他的子樹( 在 /sys/bus 之下 )表示設備驅動. 這些樹, 但是, 不代表驅動和它們所管理的設備間的關系. 展示這些附加關系需要額外的指針, 指針在 sysfs 中通過符號連接實現.
創建一個符號連接在 sysfs 是容易的:
~~~
int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
~~~
這個函數創建一個連接(稱為 name)指向目標的 sysfs 入口作為一個 kobj 的屬性. 它是一個相對連接, 因此它不管 sysfs 在任何特殊的系統中安裝在哪里都可用.
這個連接甚至當目標被從系統中移走也持續. 如果你在創建對其他 kobjects 的符號連接, 你應當可能有一個方法知道對這個 kobjects 的改變, 或者某種保證目標 kobjects 不會消失. 結果( 在 sysfs 中的死的符號連接 )不是特別嚴重, 但是它們不代表最好的編程風格并且可能導致在用戶空間的混亂.
去除符號連接可使用:
~~~
void sysfs_remove_link(struct kobject *kobj, char *name);
~~~
- Linux設備驅動第三版
- 第 1 章 設備驅動簡介
- 1.1. 驅動程序的角色
- 1.2. 劃分內核
- 1.3. 設備和模塊的分類
- 1.4. 安全問題
- 1.5. 版本編號
- 1.6. 版權條款
- 1.7. 加入內核開發社團
- 1.8. 本書的內容
- 第 2 章 建立和運行模塊
- 2.1. 設置你的測試系統
- 2.2. Hello World 模塊
- 2.3. 內核模塊相比于應用程序
- 2.4. 編譯和加載
- 2.5. 內核符號表
- 2.6. 預備知識
- 2.7. 初始化和關停
- 2.8. 模塊參數
- 2.9. 在用戶空間做
- 2.10. 快速參考
- 第 3 章 字符驅動
- 3.1. scull 的設計
- 3.2. 主次編號
- 3.3. 一些重要數據結構
- 3.4. 字符設備注冊
- 3.5. open 和 release
- 3.6. scull 的內存使用
- 3.7. 讀和寫
- 3.8. 使用新設備
- 3.9. 快速參考
- 第 4 章 調試技術
- 4.1. 內核中的調試支持
- 4.2. 用打印調試
- 4.3. 用查詢來調試
- 4.4. 使用觀察來調試
- 4.5. 調試系統故障
- 4.6. 調試器和相關工具
- 第 5 章 并發和競爭情況
- 5.1. scull 中的缺陷
- 5.2. 并發和它的管理
- 5.3. 旗標和互斥體
- 5.4. Completions 機制
- 5.5. 自旋鎖
- 5.6. 鎖陷阱
- 5.7. 加鎖的各種選擇
- 5.8. 快速參考
- 第 6 章 高級字符驅動操作
- 6.1. ioctl 接口
- 6.2. 阻塞 I/O
- 6.3. poll 和 select
- 6.4. 異步通知
- 6.5. 移位一個設備
- 6.6. 在一個設備文件上的存取控制
- 6.7. 快速參考
- 第 7 章 時間, 延時, 和延后工作
- 7.1. 測量時間流失
- 7.2. 獲知當前時間
- 7.3. 延后執行
- 7.4. 內核定時器
- 7.5. Tasklets 機制
- 7.6. 工作隊列
- 7.7. 快速參考
- 第 8 章 分配內存
- 8.1. kmalloc 的真實故事
- 8.2. 后備緩存
- 8.3. get_free_page 和其友
- 8.4. 每-CPU 的變量
- 8.5. 獲得大量緩沖
- 8.6. 快速參考
- 第 9 章 與硬件通訊
- 9.1. I/O 端口和 I/O 內存
- 9.2. 使用 I/O 端口
- 9.3. 一個 I/O 端口例子
- 9.4. 使用 I/O 內存
- 9.5. 快速參考
- 第 10 章 中斷處理
- 10.1. 準備并口
- 10.2. 安裝一個中斷處理
- 10.3. 前和后半部
- 10.4. 中斷共享
- 10.5. 中斷驅動 I/O
- 10.6. 快速參考
- 第 11 章 內核中的數據類型
- 11.1. 標準 C 類型的使用
- 11.2. 安排一個明確大小給數據項
- 11.3. 接口特定的類型
- 11.4. 其他移植性問題
- 11.5. 鏈表
- 11.6. 快速參考
- 第 12 章 PCI 驅動
- 12.1. PCI 接口
- 12.2. 回顧: ISA
- 12.3. PC/104 和 PC/104+
- 12.4. 其他的 PC 總線
- 12.5. SBus
- 12.6. NuBus 總線
- 12.7. 外部總線
- 12.8. 快速參考
- 第 13 章 USB 驅動
- 13.1. USB 設備基礎知識
- 13.2. USB 和 sysfs
- 13.3. USB 的 Urbs
- 13.4. 編寫一個 USB 驅動
- 13.5. 無 urb 的 USB 傳送
- 13.6. 快速參考
- 第 14 章 Linux 設備模型
- 14.1. Kobjects, Ksets 和 Subsystems
- 14.2. 低級 sysfs 操作
- 14.3. 熱插拔事件產生
- 14.4. 總線, 設備, 和驅動
- 14.5. 類
- 14.6. 集成起來
- 14.7. 熱插拔
- 14.8. 處理固件
- 14.9. 快速參考
- 第 15 章 內存映射和 DMA
- 15.1. Linux 中的內存管理
- 15.2. mmap 設備操作
- 15.3. 進行直接 I/O
- 15.4. 直接內存存取
- 15.5. 快速參考
- 第 16 章 塊驅動
- 16.1. 注冊
- 16.2. 塊設備操作
- 16.3. 請求處理
- 16.4. 一些其他的細節
- 16.5. 快速參考
- 第 17 章 網絡驅動
- 17.1. snull 是如何設計的
- 17.2. 連接到內核
- 17.3. net_device 結構的詳情
- 17.4. 打開與關閉
- 17.5. 報文傳送
- 17.6. 報文接收
- 17.7. 中斷處理
- 17.8. 接收中斷緩解
- 17.9. 連接狀態的改變
- 17.10. Socket 緩存
- 17.11. MAC 地址解析
- 17.12. 定制 ioctl 命令
- 17.13. 統計信息
- 17.14. 多播
- 17.15. 幾個其他細節
- 17.16. 快速參考
- 第 18 章 TTY 驅動
- 18.1. 一個小 TTY 驅動
- 18.2. tty_driver 函數指針
- 18.3. TTY 線路設置
- 18.4. ioctls 函數
- 18.5. TTY 設備的 proc 和 sysfs 處理
- 18.6. tty_driver 結構的細節
- 18.7. tty_operaions 結構的細節
- 18.8. tty_struct 結構的細節
- 18.9. 快速參考