1. 程式人生 > >使用 /sys 檔案系統訪問 Linux 核心

使用 /sys 檔案系統訪問 Linux 核心

sysfs 與 /sys

sysfs 檔案系統總是被掛載在 /sys 掛載點上。雖然在較早期的2.6核心系統上並沒有規定 sysfs 的標準掛載位置,可以把 sysfs 掛載在任何位置,但較近的2.6核心修正了這一規則,要求 sysfs 總是掛載在 /sys 目錄上;針對以前的 sysfs 掛載位置不固定或沒有標準被掛載,有些程式從 /proc/mounts 中解析出 sysfs 是否被掛載以及具體的掛載點,這個步驟現在已經不需要了。請參考附錄給出的 sysfs-rules.txt 檔案連結。

sysfs 與 proc

sysfs 與 proc 相比有很多優點,最重要的莫過於設計上的清晰。一個 proc 虛擬檔案可能有內部格式,如 /proc/scsi/scsi

 ,它是可讀可寫的,(其檔案許可權被錯誤地標記為了 0444 !,這是核心的一個BUG),並且讀寫格式不一樣,代表不同的操作,應用程式中讀到了這個檔案的內容一般還需要進行字串解析,而在寫入時需要先用字串格式化按指定的格式寫入字串進行操作;相比而言, sysfs 的設計原則是一個屬性檔案只做一件事情, sysfs 屬性檔案一般只有一個值,直接讀取或寫入。整個 /proc/scsi 目錄在2.6核心中已被標記為過時(LEGACY),它的功能已經被相應的 /sys 屬性檔案所完全取代。新設計的核心機制應該儘量使用 sysfs 機制,而將 proc 保留給純淨的“程序檔案系統”。

初識 /sys

清單 1. 與 /sys 檔案系統的一次互動(視核心版本號和外接裝置的不同,在您的系統上執行這些命令的結果可能與此有所不同)
$ ls -F /sys
block/  bus/  class/  dev/  devices/  firmware/  fs/  kernel/  module/  power/
$ ls -F /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/
broken_parity_status  enable         modalias  resource0     rom               uevent
class                 irq            msi_bus   resource0_wc  [email protected]        vendor
config                local_cpulist  power/    resource1     subsystem_device
device                local_cpus     resource  resource2     subsystem_vendor

這是在 Fedora 10 的 2.6.27.5-117.fc10.i686 的核心上,可以看到在 /sys 目錄下有 block, bus, class, dev, devices, firmware, fs, kernel, module, power 這些子目錄,本文將分別介紹這些目錄存在的含義。

第二個 ls 命令展示了在一個 pci 裝置目錄下的檔案, "ls" 命令的 "-F" 命令為所列出的每個檔案使用字尾來顯示檔案的型別,字尾 "/" 表示列出的是目錄,字尾 "@" 表示列出的是符號連結檔案。可以看到第二個目錄下包含有普通檔案 (regular file) 和符號連結檔案 (symbolic link file) ,本文也將以這個具體的裝置為例說明其中每一個普通檔案的用途。

/sys 檔案系統下的目錄結構

/sys 下的目錄結構是經過精心設計的:在 /sys/devices 下是所有裝置的真實物件,包括如視訊卡和乙太網卡等真實的裝置,也包括 ACPI 等不那麼顯而易見的真實裝置、還有 tty, bonding 等純粹虛擬的裝置;在其它目錄如 class, bus 等中則在分類的目錄中含有大量對 devices 中真實物件引用的符號連結檔案; 清單 1 中在 /sys 根目錄下頂層目錄的意義如下:

表 1. /sys 下的目錄結構
/sys 下的子目錄 所包含的內容
/sys/devices 這是核心對系統中所有裝置的分層次表達模型,也是 /sys 檔案系統管理裝置的最重要的目錄結構,下文會對它的內部結構作進一步分析;
/sys/dev 這個目錄下維護一個按字元裝置和塊裝置的主次號碼(major:minor)連結到真實的裝置(/sys/devices下)的符號連結檔案,它是在核心 2.6.26 首次引入;
/sys/bus 這是核心裝置按匯流排型別分層放置的目錄結構, devices 中的所有裝置都是連線於某種匯流排之下,在這裡的每一種具體匯流排之下可以找到每一個具體裝置的符號連結,它也是構成 Linux 統一裝置模型的一部分;
/sys/class 這是按照裝置功能分類的裝置模型,如系統所有輸入裝置都會出現在 /sys/class/input 之下,而不論它們是以何種匯流排連線到系統。它也是構成 Linux 統一裝置模型的一部分;
/sys/block 這裡是系統中當前所有的塊裝置所在,按照功能來說放置在 /sys/class 之下會更合適,但只是由於歷史遺留因素而一直存在於 /sys/block, 但從 2.6.22 開始就已標記為過時,只有在打開了 CONFIG_SYSFS_DEPRECATED 配置下編譯才會有這個目錄的存在,並且在 2.6.26 核心中已正式移到 /sys/class/block, 舊的介面 /sys/block 為了向後相容保留存在,但其中的內容已經變為指向它們在 /sys/devices/ 中真實裝置的符號連結檔案;
/sys/firmware 這裡是系統載入韌體機制的對使用者空間的介面,關於韌體有專用於韌體載入的一套API,在附錄 LDD3 一書中有關於核心支援韌體載入機制的更詳細的介紹;
/sys/fs 這裡按照設計是用於描述系統中所有檔案系統,包括檔案系統本身和按檔案系統分類存放的已掛載點,但目前只有 fuse,gfs2 等少數檔案系統支援 sysfs 介面,一些傳統的虛擬檔案系統(VFS)層次控制引數仍然在 sysctl (/proc/sys/fs) 介面中中;
/sys/kernel 這裡是核心所有可調整引數的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等幾項較新的設計在使用它,其它核心可調整引數仍然位於 sysctl (/proc/sys/kernel) 介面中 ;
/sys/module 這裡有系統中所有模組的資訊,不論這些模組是以內聯(inlined)方式編譯到核心映像檔案(vmlinuz)中還是編譯為外部模組(ko檔案),都可能會出現在 /sys/module 中:
  • 編譯為外部模組(ko檔案)在載入後會出現對應的 /sys/module/<module_name>/, 並且在這個目錄下會出現一些屬性檔案和屬性目錄來表示此外部模組的一些資訊,如版本號、載入狀態、所提供的驅動程式等;
  • 編譯為內聯方式的模組則只在當它有非0屬性的模組引數時會出現對應的 /sys/module/<module_name>, 這些模組的可用引數會出現在 /sys/modules/<modname>/parameters/<param_name> 中,
    • 如 /sys/module/printk/parameters/time 這個可讀寫引數控制著內聯模組 printk 在列印核心訊息時是否加上時間字首;
    • 所有內聯模組的引數也可以由 "<module_name>.<param_name>=<value>" 的形式寫在核心啟動引數上,如啟動核心時加上引數 "printk.time=1" 與 向 "/sys/module/printk/parameters/time" 寫入1的效果相同;
  • 沒有非0屬性引數的內聯模組不會出現於此。
/sys/power 這裡是系統中電源選項,這個目錄下有幾個屬性檔案可以用於控制整個機器的電源狀態,如可以向其中寫入控制命令讓機器關機、重啟等。
/sys/slab (對應 2.6.23 核心,在 2.6.24 以後移至 /sys/kernel/slab) 從2.6.23 開始可以選擇 SLAB 記憶體分配器的實現,並且新的 SLUB(Unqueued Slab Allocator)被設定為預設值;如果編譯了此選項,在 /sys 下就會出現 /sys/slab ,裡面有每一個 kmem_cache 結構體的可調整引數。對應於舊的 SLAB 記憶體分配器下的 /proc/slabinfo 動態調整介面,新式的 /sys/kernel/slab/<slab_name> 介面中的各項資訊和可調整項顯得更為清晰。

接下來對 /sys/devices/ 下的目錄結構作進一步探討:

清單 2. 檢視 /sys/devices/ 的目錄結構
$ ls -F /sys/devices/
isa/  LNXSYSTM:00/  pci0000:00/  platform/  pnp0/  pnp1/  system/  virtual/

可以看到,在 /sys/devices/ 目錄下是按照裝置的基本匯流排型別分類的目錄,再進入進去檢視其中的 PCI 型別的裝置:

清單 3. 檢視 /sys/devices/pci0000:00/ 的目錄結構
$ ls -F /sys/devices/pci0000:00/
0000:00:00.0/  0000:00:02.5/  0000:00:03.1/  0000:00:0e.0/   power/
0000:00:01.0/  0000:00:02.7/  0000:00:03.2/  [email protected]  uevent
0000:00:02.0/  0000:00:03.0/  0000:00:03.3/  pci_bus/

在 /sys/devices/pci0000:00/ 目錄下是按照 PCI 匯流排接入的裝置號分類存放的目錄,再檢視其中一個,

清單 4. 檢視 /sys/devices/pci0000:00/ 的目錄結構
$ ls -F /sys/devices/pci0000:00/0000:00:01.0/
0000:01:00.0/         device         local_cpus  power/            subsystem_vendor
broken_parity_status  enable         modalias    resource          uevent
class                 irq            msi_bus     [email protected]        vendor
config                local_cpulist  pci_bus/    subsystem_device

可以看到,其中有一個目錄 0000:01:00.0/, 其它都是屬性檔案和屬性組,而如果對 0000:01:00.0/ 子目錄中進行再列表檢視則會得到 清單 1 的目錄結構。

繼續以上過程可以瞭解整個目錄樹的結構,這裡把它整理成 圖 1. sysfs 目錄層次圖

圖 1. sysfs 目錄層次圖
sysfs目錄層次圖

其中涉及到 ksets, kobjects, attrs 等很多術語,這就不得不提到 Linux 統一裝置模型。

Linux 統一裝置模型

在 Linux 2.5 核心的開發過程中,人們設計了一套新的裝置模型,目的是為了對計算機上的所有裝置進行統一地表示和操作,包括裝置本身和裝置之間的連線關係。這個模型是在分析了 PCI 和 USB 的匯流排驅動過程中得到的,這兩個匯流排型別能代表當前系統中的大多數裝置型別,它們都有完善的熱挺拔機制和電源管理的支援,也都有級連機制的支援,以橋接的 PCI/USB 匯流排控制器的方式可以支援更多的 PCI/USB 裝置。為了給所有裝置新增統一的電源管理的支援,而不是讓每個裝置中去獨立實現電源管理的支援,人們考慮的是如何儘可能地重用程式碼;而且在有層次模型的 PCI/USB 匯流排中,必須以合理形式展示出這個層次關係,這也是電源管理等所要求的必須有層次結構。

如在一個典型的 PC 系統中,中央處理器(CPU)能直接控制的是 PCI 匯流排裝置,而 USB 匯流排裝置是以一個 PCI 裝置(PCI-USB橋)的形式接入在 PCI 匯流排裝置上,外部 USB 裝置再接入在 USB 匯流排裝置上;當計算機執行掛起(suspend)操作時, Linux 核心應該以 “外部USB裝置->USB匯流排裝置->PCI匯流排裝置” 的順序通知每一個裝置將電源掛起;執行恢復(resume)時則以相反的順序通知;反之如果不按此順序則將有裝置得不到正確的電源狀態變遷的通知,將無法正常工作。

sysfs 是在這個 Linux 統一裝置模型的開發過程中的一項副產品(見 參考資料 中 Greg K. Hartman 寫作的 LinuxJournal 文章)。為了將這些有層次結構的裝置以使用者程式可見的方式表達出來,人們很自然想到了利用檔案系統的目錄樹結構(這是以 UNIX 方式思考問題的基礎,一切都是檔案!)在這個模型中,有幾種基本型別,它們的對應關係見 表 2. Linux 統一裝置模型的基本結構 :

表 2. Linux 統一裝置模型的基本結構
型別 所包含的內容 對應核心資料結構 對應/sys項
裝置(Devices) 裝置是此模型中最基本的型別,以裝置本身的連線按層次組織 struct device /sys/devices/*/*/.../
裝置驅動(Device Drivers) 在一個系統中安裝多個相同裝置,只需要一份驅動程式的支援 struct device_driver /sys/bus/pci/drivers/*/
匯流排型別(Bus Types) 在整個匯流排級別對此總線上連線的所有裝置進行管理 struct bus_type /sys/bus/*/
裝置類別(Device Classes) 這是按照功能進行分類組織的裝置層次樹;如 USB 介面和 PS/2 介面的滑鼠都是輸入裝置,都會出現在 /sys/class/input/ 下 struct class /sys/class/*/

從核心在實現它們時所使用的資料結構來說, Linux 統一裝置模型又是以兩種基本資料結構進行樹型和連結串列型結構組織的:

  • kobject: 在 Linux 裝置模型中最基本的物件,它的功能是提供引用計數和維持父子(parent)結構、平級(sibling)目錄關係,上面的 device, device_driver 等各物件都是以 kobject 基礎功能之上實現的;
    struct kobject {
            const char              *name;
            struct list_head        entry;
            struct kobject          *parent;
            struct kset             *kset;
            struct kobj_type        *ktype;
            struct sysfs_dirent     *sd;
            struct kref             kref;
            unsigned int state_initialized:1;
    	unsigned int state_in_sysfs:1;
            unsigned int state_add_uevent_sent:1;
            unsigned int state_remove_uevent_sent:1;
    };

    其中 struct kref 內含一個 atomic_t 型別用於引用計數, parent 是單個指向父節點的指標, entry 用於父 kset 以連結串列頭結構將 kobject 結構維護成雙向連結串列;
  • kset: 它用來對同類型物件提供一個包裝集合,在核心資料結構上它也是由內嵌一個 kboject 實現,因而它同時也是一個 kobject (面向物件 OOP 概念中的繼承關係) ,具有 kobject 的全部功能;
    struct kset {
            struct list_head list;
            spinlock_t list_lock;
            struct kobject kobj;
            struct kset_uevent_ops *uevent_ops;
    };

    其中的 struct list_head list 用於將集合中的 kobject 按 struct list_head entry 維護成雙向連結串列;

涉及到檔案系統實現來說, sysfs 是一種基於 ramfs 實現的記憶體檔案系統,與其它同樣以 ramfs 實現的記憶體檔案系統(configfs,debugfs,tmpfs,...)類似, sysfs 也是直接以 VFS 中的 struct inode 和 struct dentry 等 VFS 層次的結構體直接實現檔案系統中的各種物件;同時在每個檔案系統的私有資料 (如 dentry->d_fsdata 等位置) 上,使用了稱為 struct sysfs_dirent 的結構用於表示 /sys 中的每一個目錄項。

struct sysfs_dirent {
        atomic_t                s_count;
        atomic_t                s_active;
        struct sysfs_dirent     *s_parent;
        struct sysfs_dirent     *s_sibling;
        const char              *s_name;

        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 int            s_flags;
        ino_t                   s_ino;
        umode_t                 s_mode;
        struct iattr            *s_iattr;
};

在上面的 kobject 物件中可以看到有向 sysfs_dirent 的指標,因此在sysfs中是用同一種 struct sysfs_dirent 來統一裝置模型中的 kset/kobject/attr/attr_group.

具體在資料結構成員上, sysfs_dirent 上有一個 union 共用體包含四種不同的結構,分別是目錄、符號連結檔案、屬性檔案、二進位制屬性檔案;其中目錄型別可以對應 kobject,在相應的 s_dir 中也有對 kobject 的指標,因此在核心資料結構, kobject 與 sysfs_dirent 是互相引用的;

有了這些概念,再來回頭看 圖 1. sysfs 目錄層次圖 所表達的 /sys 目錄結構就是非常清晰明瞭:

  • 在 /sys 根目錄之下的都是 kset,它們組織了 /sys 的頂層目錄檢視;
  • 在部分 kset 下有二級或更深層次的 kset;
  • 每個 kset 目錄下再包含著一個或多個 kobject,這表示一個集合所包含的 kobject 結構體;
  • 在 kobject 下有屬性(attrs)檔案和屬性組(attr_group),屬性組就是組織屬性的一個目錄,它們一起向用戶層提供了表示和操作這個 kobject 的屬性特徵的介面;
  • 在 kobject 下還有一些符號連結檔案,指向其它的 kobject,這些符號連結檔案用於組織上面所說的 device, driver, bus_type, class, module 之間的關係;
  • 不同型別如裝置型別的、裝置驅動型別的 kobject 都有不同的屬性,不同驅動程式支援的 sysfs 介面也有不同的屬性檔案;而相同型別的裝置上有很多相同的屬性檔案;

注意,此表內容是按照最新開發中的 2.6.28 核心的更新組織的,在附錄資源如 LDD3 等位置中有提到 sysfs 中曾有一種管理物件稱為 subsys (子系統物件),在最新的核心中經過重構認為它是不需要的,它的功能完全可以由 kset 代替,也就是說 sysfs 中只需要一種管理結構是 kset,一種代表具體物件的結構是 kobject,在 kobject 下再用屬性檔案表示這個物件所具有的屬性;

常見 sysfs 屬性的功能

使用 sysfs 的關鍵就是掌握這些 sysfs 屬性的用法,下面以一些常見的 sysfs 屬性來展示它的用法;

使用裝置(PCI)的 sysfs 屬性檔案

以一份桌面系統上的視訊卡為例,列舉它對應的 kobject 上的屬性檔案的對應用途;

一般來說,在 Linux 桌面上都有視訊卡以支援 Xorg 軟體包作為 XWindow 伺服器來執行,因此先找到 Xorg 的程序號,檢視這個程序所使用的所有檔案(注意檢視這個程序屬性需要 root 使用者許可權);

# ps xfa |grep Xorg
 2001 tty1     Ss+    2:24      \_ /usr/bin/Xorg :0 -nr -verbose -auth \
/var/run/gdm/auth-for-gdm-NPrkZK/database -nolisten tcp vt1
# lsof -nP -p 2001
Xorg    2001 root  mem    REG        8,3    617732     231033 \
/usr/lib/xorg/modules/drivers/sis_drv.so
[...]
Xorg    2001 root  mem    REG        0,0 134217728       5529 \
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/resource0
Xorg    2001 root  mem    REG        0,0    131072       5531 \
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/resource1
[...]
Xorg    2001 root    7u   REG        0,0       256       5504 \
/sys/devices/pci0000:00/0000:00:00.0/config
Xorg    2001 root    8u  unix 0xdbe66000       0t0       8756 socket
Xorg    2001 root    9u   REG        0,0       256       5528 \
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/config

注意到此 Xorg 伺服器是以記憶體對映 (mem) 的形式打開了 "/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/resource0" 和 "/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/resource1" ,同時以檔案讀寫形式 (7u,9u) 打開了 "/sys/devices/pci0000:00/0000:00:00.0/config" 和 "/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/config"

事實上, PCI 裝置對應的 kobject 目錄下的 config 正是代表PCI裝置的“配置空間”,對於普通 PCI (非PCI-E)裝置而言,其配置空間大小一般是 256位元組,這個空間可以使用十六進位制工具 dump 出來,如下。(有關 PCI 裝置本身的三種地址空間,請參考附錄 LDD3)

# hexdump -C /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/config
00000000  39 10 30 63 03 00 30 02  00 00 00 03 00 00 00 80  |9.0c..0.........|
00000010  08 00 00 d8 00 00 00 e1  01 d0 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 19 10 30 1b  |..............0.|
00000030  00 00 00 00 40 00 00 00  00 00 00 00 00 00 00 00  |[email protected]|
00000040  01 50 02 06 00 00 00 00  00 00 00 00 00 00 00 00  |.P..............|
00000050  02 00 30 00 0b 02 00 ff  00 00 00 00 00 00 00 00  |..0.............|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100

這個空間正好是 256位元組大小,熟悉 PCI 的人們還可以知道,從 PCI 配置空間可以讀到有關此 PCI 裝置的很多有用資訊,如廠商程式碼,裝置程式碼,IRQ 號碼等;前四個位元組 0x39 0x10 0x30 0x63 就是按小端(little endian)存放的2個短整數,因此其 PCI 廠商號碼和 PCI 裝置號碼分別是 0x1039 和 0x6330

# lspci -v -d 1039:6330
01:00.0 VGA compatible controller: Silicon Integrated Systems [SiS] 661/741/760 PCI/AGP \
or 662/761Gx PCIE VGA Display Adapter (prog-if 00 [VGA controller])
	Subsystem: Elitegroup Computer Systems Device 1b30
	Flags: 66MHz, medium devsel
	BIST result: 00
	Memory at d8000000 (32-bit, prefetchable) [size=128M]
	Memory at e1000000 (32-bit, non-prefetchable) [size=128K]
	I/O ports at d000 [size=128]
	Capabilities: [40] Power Management version 2
	Capabilities: [50] AGP version 3.0

在 PCI 裝置上除了有 config 是配置空間對使用者的介面以外,還有 resource{0,1,2,...} 是資源空間,對應著 PCI 裝置的可對映記憶體空間;此外 PCI 裝置還提供了很多介面,全部列表如下:

# ls -lU /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/
總計 0
-rw-r--r-- 1 root root      4096 12-09 00:28 uevent
-r--r--r-- 1 root root      4096 12-09 00:27 resource
-r--r--r-- 1 root root      4096 12-09 00:27 vendor
-r--r--r-- 1 root root      4096 12-09 00:27 device
-r--r--r-- 1 root root      4096 12-09 00:28 subsystem_vendor
-r--r--r-- 1 root root      4096 12-09 00:28 subsystem_device
-r--r--r-- 1 root root      4096 12-09 00:27 class
-r--r--r-- 1 root root      4096 12-09 00:27 irq
-r--r--r-- 1 root root      4096 12-09 00:28 local_cpus
-r--r--r-- 1 root root      4096 12-09 00:28 local_cpulist
-r--r--r-- 1 root root      4096 12-09 00:28 modalias
-rw------- 1 root root      4096 12-09 00:28 enable
-rw-r--r-- 1 root root      4096 12-09 00:28 broken_parity_status
-rw-r--r-- 1 root root      4096 12-09 00:28 msi_bus
lrwxrwxrwx 1 root root         0 12-09 00:28 subsystem -> ../../../../bus/pci
drwxr-xr-x 2 root root         0 12-09 00:28 power
-rw-r--r-- 1 root root       256 12-08 23:03 config
-rw------- 1 root root 134217728 12-08 23:03 resource0
-rw------- 1 root root 134217728 12-09 00:28 resource0_wc
-rw------- 1 root root    131072 12-08 23:03 resource1
-rw------- 1 root root       128 12-09 00:28 resource2
-r-------- 1 root root         0 12-09 00:28 rom

可以看到很多其它屬性檔案,這些屬性檔案的許可權位也都是正確的,有 w 許可權位的才是可以寫入。其中大小為 4096位元組的屬性一般是純文字描述的屬性,可以直接 cat 讀出和用 echo 字串的方法寫入;其它非 4096位元組大小的一般是二進位制屬性,類似於上面的 config 屬性檔案;關於純文字屬性和二進位制屬性,在下文 程式設計實踐:新增sysfs支援 一節會進一步說明。

  • 從 vendor, device, subsystem_vendor, subsystem_device, class, resource 這些只讀屬性上分別可以讀到此 PCI 裝置的廠商號、裝置號、子系統廠商號、子系統裝置號、PCI類別、資源表等,這些都是相應 PCI 裝置的屬性,其實就是直接從 config 二進位制檔案讀出來,按照配置空間的格式讀出這些號碼;
  • 使用 enable 這個可寫屬性可以禁用或啟用這個 PCI 裝置,裝置的過程很直觀,寫入1代表啟用,寫入0則代表禁用;
  • subsystem 和 driver 符號連結檔案分別指向對應的 sysfs 位置;(這裡缺少 driver 符號連結說明這個裝置當前未使用核心級的驅動程式)
  • resource0, resource0_wc, resource1, resource2 等是從"PCI 配置空間"解析出來的資源定義段落分別生成的,它們是 PCI 匯流排驅動在 PCI 裝置初始化階段加上去的,都是二進位制屬性,但沒有實現讀寫介面,只支援 mmap 記憶體對映介面,嘗試進行讀寫會提示 IO 錯誤,其中 _wc 字尾表示 "合併式寫入(write combined)" ,它們用於作應用程式的記憶體對映,就可以訪問對應的 PCI 裝置上相應的記憶體資源段落;

有了 PCI 核心對 sysfs 的完善支援,每個裝置甚至不用單獨的驅動程式,如這裡的 "0000:01:00.0" 不需要一個核心級的驅動程式,有了 PCI 核心對該裝置的配置空間發現機制,可以自動發現它的各個不同段落的資源屬性,在 Xorg 應用程式中可以直接以 "/usr/lib/xorg/modules/drivers/sis_drv.so" 這個使用者空間的驅動程式對其進行對映,就可以直接操作此視訊卡了;

有了這一個 PCI 裝置的示例可以知道,有了一個 PCI 裝置的 /sys/devices/ 裝置物件,去訪問它的各項屬性和設定屬性都非常簡單。

使用 uevent

在 sysfs 下的很多 kobject 下都有 uevent 屬性,它主要用於核心與 udev (自動裝置發現程式)之間的一個通訊介面;從 udev 本身與核心的通訊介面 netlink 協議套接字來說,它並不需要知道裝置的 uevent 屬性檔案,但多了 uevent 這樣一個介面,可用於 udevmonitor 通過核心向 udevd (udev 後臺程式)傳送訊息,也可用於檢查裝置本身所支援的 netlink 訊息上的環境變數,這個特性一般用於開發人員除錯 udev 規則檔案, udevtrigger 這個除錯工具本身就是以寫各裝置的 uevent 屬性檔案實現的。

這些 uevent 屬性檔案一般都是可寫的,其中 /sys/devices/ 樹下的很多 uevent 屬性在較新核心下還支援可讀:

# find /sys/ -type f -name uevent -ls
    11    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/uevent
  1471    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/pcspkr/uevent
  3075    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/vesafb.0/uevent
  3915    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/serial8250/uevent
  3941    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/serial8250/tty/ttyS2/uevent
  3950    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/serial8250/tty/ttyS3/uevent
  5204    0 -rw-r--r--   1 root     root         4096 12月 12 21:10 \
/sys/devices/platform/i8042/uevent
[...]
   912    0 -rw-r--r--   1 root     root         4096 12月 12 21:17 \
/sys/devices/pci0000:00/0000:00:02.5/uevent
[...]

上面擷取的最後一個是 SCSI 硬碟控制器裝置的 uevent 屬性檔案,這些 /devices/ 屬性檔案都支援寫入,當前支援寫入的引數有 "add","remove","change","move","online","offline"。如,寫入 "add",這樣可以向 udevd 傳送一條 netlink 訊息,讓它再重新一遍相關的 udev 規則檔案;這個功能對開發人員除錯 udev 規則檔案很有用。

# echo add > /sys/devices/pci0000:00/0000:00:02.5/uevent

使用驅動(PCI)的 sysfs 屬性檔案, bind, unbind 和 new_id

在裝置驅動 /sys/bus/*/driver/... 下可以看到很多驅動都有 bind, unbind, new_id 這三個屬性,

# find /sys/bus/*/drivers/ -name bind -ls
...

每一個裝置驅動程式在程式內以某種方式註明了可用於哪些硬體,如所有的 PCI 驅動都使用 MODULE_DEVICE_TABLE 聲明瞭所能驅動的 PCI 硬體的 PCI 裝置號。但驅動程式不能預知未來,未來生產的新的硬體有可能相容現有硬體的工作方式,就還可以使用現有硬體驅動程式來工作。在 bind 和 unbind 發明以前,這種情況除了修改 PCI 裝置驅動程式的 DEVICE_TABLE 段落,重新編譯驅動程式,以外別無他法,在 2.6 核心上添加了 bind 和 unbind 之後可以在不重新編譯的情況下對裝置和驅動之間進行手工方式地繫結。

而且對於有些硬體裝置可以有多份驅動可用,但任何具體時刻只能有一個驅動程式來驅動這個硬體,這時可以使用 bind/unbind 來強制使用和不使用哪一個驅動程式;(注意關於多種驅動程式的選擇,更好的管理方法是使用 modprobe.conf 配置檔案,需要重啟才生效,而 bind/unbind 提供的是一種臨時的無需重啟立即生效的途徑;)

使用它們可以強制繫結某個裝置使用或強制不使用某個驅動程式,操作方法就是通過 bind 和 unbind 介面。

# find /sys/ -type f \( -name bind -or -name unbind -or -name new_id \) -ls
    69    0 -rw-r--r--   1 root     root         4096 12月 12 22:12 \
/sys/devices/virtual/vtconsole/vtcon0/bind
  3072    0 --w-------   1 root     root         4096 12月 12 22:15 \
/sys/bus/platform/drivers/vesafb/unbind
[...]
  6489    0 --w-------   1 root     root         4096 12月 12 22:09 \
/sys/bus/pci/drivers/8139too/unbind
  6490    0 --w-------   1 root     root         4096 12月 12 22:09 \
/sys/bus/pci/drivers/8139too/bind
  6491    0 --w-------   1 root     root         4096 12月 12 22:15 \
/sys/bus/pci/drivers/8139too/new_id

這個結果中特別提到了 8139too 這份驅動程式的這三個屬性檔案,

# find /sys/bus/pci/drivers/8139too/ -ls
  6435    0 drwxr-xr-x   2 root     root            0 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/
  6436    0 lrwxrwxrwx   1 root     root            0 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/0000:00:0e.0 -> ../../../../devices/pci0000:00/0000:00:0e.0
  6485    0 lrwxrwxrwx   1 root     root            0 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/module -> ../../../../module/8139too
  6488    0 --w-------   1 root     root         4096 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/uevent
  6489    0 --w-------   1 root     root         4096 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/unbind
  6490    0 --w-------   1 root     root         4096 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/bind
  6491    0 --w-------   1 root     root         4096 12月 12 22:08 \
/sys/bus/pci/drivers/8139too/new_id
# echo 0000:00:0e.0 > /sys/bus/pci/drivers/8139too/unbind
-bash: echo: write error: 沒有那個裝