Linux Sysfs文件系统详解:内核与用户空间的设备模型桥梁

文章目录

  • [Linux Sysfs文件系统详解:内核与用户空间的设备模型桥梁](#Linux Sysfs文件系统详解:内核与用户空间的设备模型桥梁)
    • 引言:从proc到sysfs的演进之路
    • 第一部分:sysfs的设计哲学与架构
      • [1.1 sysfs的诞生背景](#1.1 sysfs的诞生背景)
      • [1.2 sysfs与kobject的关系](#1.2 sysfs与kobject的关系)
      • [1.3 sysfs的挂载与布局](#1.3 sysfs的挂载与布局)
    • 第二部分:sysfs核心目录结构详解
      • [2.1 /sys/devices - 设备的物理层次](#2.1 /sys/devices - 设备的物理层次)
        • [2.1.1 系统总线设备示例](#2.1.1 系统总线设备示例)
        • [2.1.2 CPU设备信息](#2.1.2 CPU设备信息)
      • [2.2 /sys/bus - 按总线类型组织的设备](#2.2 /sys/bus - 按总线类型组织的设备)
        • [2.2.1 PCI总线示例](#2.2.1 PCI总线示例)
        • [2.2.2 USB总线示例](#2.2.2 USB总线示例)
      • [2.3 /sys/class - 按设备类组织的视图](#2.3 /sys/class - 按设备类组织的视图)
        • [2.3.1 网络设备示例](#2.3.1 网络设备示例)
        • [2.3.2 块设备示例](#2.3.2 块设备示例)
        • [2.3.3 输入设备示例](#2.3.3 输入设备示例)
      • [2.4 /sys/block - 块设备的传统视图](#2.4 /sys/block - 块设备的传统视图)
      • [2.5 /sys/kernel - 内核运行时信息](#2.5 /sys/kernel - 内核运行时信息)
        • [2.5.1 安全相关配置](#2.5.1 安全相关配置)
        • [2.5.2 调试接口](#2.5.2 调试接口)
      • [2.6 /sys/module - 已加载内核模块信息](#2.6 /sys/module - 已加载内核模块信息)
        • [2.6.1 模块参数查看](#2.6.1 模块参数查看)
        • [2.6.2 模块引用计数](#2.6.2 模块引用计数)
      • [2.7 /sys/power - 电源管理配置](#2.7 /sys/power - 电源管理配置)
        • [2.7.1 电源状态控制](#2.7.1 电源状态控制)
        • [2.7.2 唤醒源配置](#2.7.2 唤醒源配置)
      • [2.8 /sys/firmware - 固件相关信息](#2.8 /sys/firmware - 固件相关信息)
        • [2.8.1 ACPI表信息](#2.8.1 ACPI表信息)
        • [2.8.2 DMI信息(SMBIOS)](#2.8.2 DMI信息(SMBIOS))
      • [2.9 /sys/fs - 文件系统相关信息](#2.9 /sys/fs - 文件系统相关信息)
        • [2.9.1 cgroup控制组](#2.9.1 cgroup控制组)
      • [2.10 /sys/dev - 字符和块设备的主次设备号视图](#2.10 /sys/dev - 字符和块设备的主次设备号视图)
    • 第三部分:sysfs属性文件详解
      • [3.1 属性文件的类型](#3.1 属性文件的类型)
      • [3.2 常用属性文件解析](#3.2 常用属性文件解析)
        • [3.2.1 uevent文件](#3.2.1 uevent文件)
        • [3.2.2 modalias文件](#3.2.2 modalias文件)
        • [3.2.3 driver符号链接](#3.2.3 driver符号链接)
        • [3.2.4 subsystem符号链接](#3.2.4 subsystem符号链接)
      • [3.3 设备特定属性](#3.3 设备特定属性)
        • [3.3.1 网络设备属性](#3.3.1 网络设备属性)
        • [3.3.2 块设备属性](#3.3.2 块设备属性)
        • [3.3.3 CPU相关属性](#3.3.3 CPU相关属性)
        • [3.3.4 温度传感器属性](#3.3.4 温度传感器属性)
        • [3.3.5 背光控制属性](#3.3.5 背光控制属性)
        • [3.3.6 LED控制属性](#3.3.6 LED控制属性)
    • 第四部分:sysfs与设备管理实践
      • [4.1 设备发现与枚举](#4.1 设备发现与枚举)
        • [4.1.1 使用sysfs发现USB设备](#4.1.1 使用sysfs发现USB设备)
        • [4.1.2 PCI设备扫描](#4.1.2 PCI设备扫描)
      • [4.2 设备绑定与解绑](#4.2 设备绑定与解绑)
        • [4.2.1 手动绑定驱动](#4.2.1 手动绑定驱动)
        • [4.2.2 驱动黑名单管理](#4.2.2 驱动黑名单管理)
      • [4.3 电源管理控制](#4.3 电源管理控制)
        • [4.3.1 CPU频率调控](#4.3.1 CPU频率调控)
        • [4.3.2 电源状态监控](#4.3.2 电源状态监控)
    • 第五部分:sysfs编程接口与实践
      • [5.1 C语言操作sysfs](#5.1 C语言操作sysfs)
        • [5.1.1 基础sysfs操作函数](#5.1.1 基础sysfs操作函数)
        • [5.1.2 内核模块创建sysfs接口](#5.1.2 内核模块创建sysfs接口)
      • [5.2 Python操作sysfs](#5.2 Python操作sysfs)
        • [5.2.1 Python sysfs工具库](#5.2.1 Python sysfs工具库)
        • [5.2.2 sysfs监控工具](#5.2.2 sysfs监控工具)
      • [5.3 Bash脚本操作sysfs](#5.3 Bash脚本操作sysfs)
        • [5.3.1 sysfs信息收集脚本](#5.3.1 sysfs信息收集脚本)
    • 第六部分:sysfs高级主题与最佳实践
      • [6.1 sysfs与udev的协同工作](#6.1 sysfs与udev的协同工作)
        • [6.1.1 udev规则中的sysfs属性匹配](#6.1.1 udev规则中的sysfs属性匹配)
        • [6.1.2 动态设备权限管理](#6.1.2 动态设备权限管理)
      • [6.2 sysfs安全考虑](#6.2 sysfs安全考虑)
        • [6.2.1 权限控制](#6.2.1 权限控制)
        • [6.2.2 敏感信息保护](#6.2.2 敏感信息保护)
      • [6.3 sysfs性能调优](#6.3 sysfs性能调优)
        • [6.3.1 减少sysfs访问频率](#6.3.1 减少sysfs访问频率)
        • [6.3.2 异步sysfs监控](#6.3.2 异步sysfs监控)
        • [6.3.3 使用mmap优化大文件访问](#6.3.3 使用mmap优化大文件访问)
        • [6.3.4 异步I/O与epoll](#6.3.4 异步I/O与epoll)
    • 第七部分:sysfs故障排查与调试
      • [7.1 常见问题排查](#7.1 常见问题排查)
        • [7.1.1 sysfs文件不存在](#7.1.1 sysfs文件不存在)
        • [7.1.2 sysfs权限问题](#7.1.2 sysfs权限问题)
        • [7.1.3 sysfs文件内容异常](#7.1.3 sysfs文件内容异常)
      • [7.2 sysfs调试技巧](#7.2 sysfs调试技巧)
        • [7.2.1 使用strace跟踪sysfs访问](#7.2.1 使用strace跟踪sysfs访问)
        • [7.2.2 使用systemtap进行内核跟踪](#7.2.2 使用systemtap进行内核跟踪)
        • [7.2.3 使用bpftrace进行动态跟踪](#7.2.3 使用bpftrace进行动态跟踪)
    • 第八部分:sysfs的未来发展与替代方案
      • [8.1 sysfs的局限性](#8.1 sysfs的局限性)
      • [8.2 sysfs的演进方向](#8.2 sysfs的演进方向)
        • [8.2.1 configfs:配置专用文件系统](#8.2.1 configfs:配置专用文件系统)
        • [8.2.2 debugfs:调试专用文件系统](#8.2.2 debugfs:调试专用文件系统)
        • [8.2.3 BPF和tracefs:现代跟踪方案](#8.2.3 BPF和tracefs:现代跟踪方案)
      • [8.3 sysfs与io_uring的集成](#8.3 sysfs与io_uring的集成)
      • [8.4 sysfs在云原生环境中的角色](#8.4 sysfs在云原生环境中的角色)
        • [8.4.1 设备插件框架](#8.4.1 设备插件框架)
        • [8.4.2 虚拟化环境中的sysfs](#8.4.2 虚拟化环境中的sysfs)
    • 第九部分:sysfs最佳实践总结
      • [9.1 开发实践](#9.1 开发实践)
      • [9.2 部署实践](#9.2 部署实践)
      • [9.3 运维实践](#9.3 运维实践)
    • 结论

Linux Sysfs文件系统详解:内核与用户空间的设备模型桥梁

引言:从proc到sysfs的演进之路

在Linux系统中,proc文件系统长期以来一直是访问内核信息的主要接口。然而,随着Linux设备模型的发展,proc逐渐暴露出其局限性:结构不够清晰、缺乏统一的组织方式、难以反映设备之间的层次关系。为了解决这些问题,Linux 2.6内核引入了sysfs------一个全新的虚拟文件系统,专门用于展示设备、驱动和内核模块的层次结构。

sysfs不仅仅是一个信息展示窗口,它更是Linux统一设备模型(Unified Device Model)的用户空间接口。通过sysfs,用户空间可以直观地查看和管理系统中所有设备的拓扑结构、属性和状态。从简单的USB设备到复杂的PCIe设备树,从CPU调频到电源管理,sysfs都提供了一个统一、结构化的访问方式。

本文将深入探索sysfs的方方面面,从理论基础到实际应用,从简单的命令行操作到复杂的编程接口,全面揭示这个强大文件系统的奥秘。

第一部分:sysfs的设计哲学与架构

1.1 sysfs的诞生背景

在sysfs出现之前,Linux设备管理存在几个主要问题:

  1. 信息分散:设备信息分散在/proc、/dev和各个专有目录中
  2. 结构混乱:没有统一的设备层次结构表示
  3. 交互困难:设备控制接口不一致
  4. 扩展性差:难以适应日益复杂的硬件环境

sysfs的设计目标正是为了解决这些问题:

  • 提供统一的设备模型视图
  • 反映设备间的父子关系和依赖关系
  • 提供标准化的属性访问接口
  • 支持动态设备发现和管理

1.2 sysfs与kobject的关系

sysfs的核心是Linux内核的kobject(内核对象)机制。每个在内核中注册的设备、驱动、总线或类都会创建一个kobject,而sysfs就是这些kobject的用户空间投影。

c 复制代码
// 简化的kobject结构(基于Linux 5.10)
struct kobject {
    const char      *name;          // 对象名称(在sysfs中作为目录名)
    struct list_head entry;         // 链表节点
    struct kobject  *parent;        // 父对象(在sysfs中表现为父目录)
    struct kset     *kset;          // 所属的kset
    struct kobj_type *ktype;        // 对象类型,定义属性和操作
    struct kernfs_node *sd;         // sysfs目录项
    struct kref     kref;           // 引用计数
    unsigned int state_initialized:1; // 初始化状态
    // ...
};

// kobj_type定义对象的属性和行为
struct kobj_type {
    void (*release)(struct kobject *kobj);  // 释放函数
    const struct sysfs_ops *sysfs_ops;      // sysfs操作
    const struct attribute_group **default_groups;  // 默认属性组
    // ...
};

1.3 sysfs的挂载与布局

sysfs通常挂载在/sys目录下:

bash 复制代码
# 查看sysfs挂载信息
$ mount | grep sysfs
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)

# sysfs的标准挂载点
$ ls -l /sys
total 0
drwxr-xr-x  2 root root 0 May 15 10:45 block
drwxr-xr-x 42 root root 0 May 15 10:45 bus
drwxr-xr-x 66 root root 0 May 15 10:45 class
drwxr-xr-x  4 root root 0 May 15 10:45 dev
drwxr-xr-x 17 root root 0 May 15 10:45 devices
drwxr-xr-x  5 root root 0 May 15 10:45 firmware
drwxr-xr-x  8 root root 0 May 15 10:45 fs
drwxr-xr-x  2 root root 0 May 15 10:45 hypervisor
drwxr-xr-x 14 root root 0 May 15 10:45 kernel
drwxr-xr-x 16 root root 0 May 15 10:45 module
drwxr-xr-x  2 root root 0 May 15 10:45 power

第二部分:sysfs核心目录结构详解

2.1 /sys/devices - 设备的物理层次

/sys/devices展示了系统中所有设备的物理连接关系,这是sysfs中最底层的设备视图:

bash 复制代码
$ ls /sys/devices/
breakpoint     isa            LNXSYSTM:00    msr            pci0000:00     platform        pnp0           pnp2           system         tracepoint     virtual
cpu            kprobe         LNXSYSTM:01    pci0000:00     pci0000:00     platform        pnp1           pnp3           tracepoint     uprobe         workqueue

2.1.1 系统总线设备示例
bash 复制代码
# 查看PCI总线设备
$ find /sys/devices/pci0000:00 -type l -name "*" | head -20
/sys/devices/pci0000:00/0000:00:00.0
/sys/devices/pci0000:00/0000:00:01.0
/sys/devices/pci0000:00/0000:00:02.0
/sys/devices/pci0000:00/0000:00:14.0
/sys/devices/pci0000:00/0000:00:14.2
/sys/devices/pci0000:00/0000:00:16.0
/sys/devices/pci0000:00/0000:00:17.0

# 查看特定PCI设备的详细信息
$ ls /sys/devices/pci0000:00/0000:00:02.0/
boot_vga  class     config     device    enable    irq       link       modalias  msi_bus  power     remove    resource0  resource2  resource4  rom       subsystem  uevent
broken_parity_status  consistent_dma_mask_bits  d3cold_allowed  devspec   firmware_node  local_cpulist  local_cpus  msi_irqs  numa_node  power_resume_D3cold  rescan    resource1  resource3  resource5  resource0_wc  subsystem_device  vendor

2.1.2 CPU设备信息
bash 复制代码
# 查看CPU设备
$ ls /sys/devices/system/cpu/
cpu0  cpu1  cpu2  cpu3  cpuidle  isolated  kernel_max  microcode  modalias  offline  online  possible  power  present  probe  uevent

# 查看CPU0的详细信息
$ ls /sys/devices/system/cpu/cpu0/
cache       cpuidle     node0       online      power       subsystem   topology    uevent
cpufreq     hotplug     of_node     physical_package_id  smt         thread_siblings  thermal_throttle

2.2 /sys/bus - 按总线类型组织的设备

/sys/bus按照总线类型组织设备,每种总线类型都有独立的目录:

bash 复制代码
$ ls /sys/bus/
acpi        container   event_source  i2c         mdio_bus    nvmem       platform    sdio        usb
amba        cpu         hid           ieee80211   media       pci         pnp         serio       vme
clockevents  edac        iio           iommu       memory      pci_express  rapidio     spi         workqueue
clocksource  gpio        infiniband    ipack       mmc         pcmcia      scsi        thunderbolt  xen

2.2.1 PCI总线示例
bash 复制代码
$ ls /sys/bus/pci/
devices  drivers  drivers_autoprobe  drivers_probe  slots  uevent

# 查看PCI设备
$ ls /sys/bus/pci/devices/ | head -10
0000:00:00.0
0000:00:01.0
0000:00:02.0
0000:00:14.0
0000:00:14.2
0000:00:16.0
0000:00:17.0
0000:00:1c.0
0000:00:1c.4
0000:00:1f.0

# 查看PCI驱动
$ ls /sys/bus/pci/drivers/
ahci         i801_smbus   i915         intel-lpss-pci  mei_me       nvme         pcieport      soundwire_intel  vfio-pci
i2c_designware-pci  i8042       iwlwifi      ixgbe          nvme-acpi    pci-stub     serial        thunderbolt     xhci_hcd

2.2.2 USB总线示例
bash 复制代码
$ ls /sys/bus/usb/
devices  drivers  drivers_autoprobe  drivers_probe  uevent

# 查看USB设备树
$ find /sys/bus/usb/devices/ -name "*" -type l | sort
/sys/bus/usb/devices/usb1
/sys/bus/usb/devices/usb1/1-0:1.0
/sys/bus/usb/devices/usb1/1-1
/sys/bus/usb/devices/usb1/1-1/1-1:1.0
/sys/bus/usb/devices/usb1/1-1/1-1:1.1
/sys/bus/usb/devices/usb1/1-1/1-1:1.2
/sys/bus/usb/devices/usb1/1-1/1-1:1.3
/sys/bus/usb/devices/usb1/1-1/1-1:1.4
/sys/bus/usb/devices/usb2

2.3 /sys/class - 按设备类组织的视图

/sys/class按照设备的功能类别组织设备,这是用户最常接触的视图:

bash 复制代码
$ ls /sys/class/
ata_device    drm          hwmon       leds           mem          nd           pci_ep        power_supply  spi_master   tpmrm
ata_link      dvb          i2c-adapter  mdio_bus       misc         net          phy           pps           thermal      tty
ata_port      extcon       i2c-dev      mei            mmc_host     pci_bus      pinctrl       ptp           thunderbolt  usbmon
bdi           firmware     ieee80211   memstick       mtd          pci_epc      platform      rc            tpm          vc
bluetooth     gpio         input       message        nvme         pci_epf      powercap      regulator     tpm0         vtconsole
bsg           graphics     iommu       misc           nvme-subsystem  pci_epf      power         rpmb          tpm1         wakeup
dma           hidraw       lcd         mux            nvmem        pci_epf      power         scsi_device   tpm2         watchdog

2.3.1 网络设备示例
bash 复制代码
$ ls /sys/class/net/
docker0  enp0s31f6  lo  wlp2s0

# 查看网络接口的详细信息
$ ls /sys/class/net/eth0/
addr_assign_type  carrier_changes     dev_id      flags       ifalias      lower_up      mtu              proto_down  subsystem     tx_queue_len
address           carrier_down_count  dormant     gro_flush_timeout  ifindex      macaddress     name_assign_type  queues       statistics    type
broadcast         carrier_up_count    duplex      iflink      iflink       master        netdev_group      speed       threaded      uevent
carrier           device              feature      iflink      link_mode    mem_start     operstate        speed       tx_maxrate    wireless

2.3.2 块设备示例
bash 复制代码
$ ls /sys/class/block/
loop0  loop2  loop4  loop6  nvme0n1  nvme0n1p1  nvme0n1p2  nvme0n1p3  sr0
loop1  loop3  loop5  loop7  nvme0n1  nvme0n1p1  nvme0n1p2  nvme0n1p3

# 查看磁盘信息
$ ls /sys/class/block/sda/
alignment_offset  discard_alignment  holders   power        ro    size    stat       subsystem
bdi               events             inflight  queue        sda1  slaves  subsystem  trace
capability        events_async       integrity  removable    sda2  sda3    uevent
dev               events_poll_msecs  partition  rotational  sda1  sda2    sda3

2.3.3 输入设备示例
bash 复制代码
$ ls /sys/class/input/
input0  input1  input10  input11  input12  input13  input14  input2  input3  input4  input5  input6  input7  input8  input9

# 查看输入设备的事件节点
$ ls /sys/class/input/event0/
capabilities  device  id  name  power  subsystem  uevent  uniq

2.4 /sys/block - 块设备的传统视图

/sys/block是块设备的传统视图,现在建议使用/sys/class/block

bash 复制代码
$ ls /sys/block/
loop0  loop2  loop4  loop6  nvme0n1  sr0
loop1  loop3  loop5  loop7  ram0

2.5 /sys/kernel - 内核运行时信息

/sys/kernel包含了内核的各种配置和状态信息:

bash 复制代码
$ ls /sys/kernel/
boot_params  debug        iommu_groups  kexec_crash_loaded  mm         rcu_expedited  security     tracing     uevent_seqnum
config       firmware     irq           kexec_loaded        notes      rcu_normal     slab         uevent_helper  usermodehelper
cgroups      ftrace       kexec_crash_size  livepatch      profiling  rtnl_link      softlockup_all_cpu_backtrace  uevent

2.5.1 安全相关配置
bash 复制代码
$ ls /sys/kernel/security/
apparmor  lockdown  lsm

2.5.2 调试接口
bash 复制代码
$ ls /sys/kernel/debug/
acpi           block          cleancache     clk            device_component  extfrag        gpio           iommu          memblock       mmc0           pinctrl        regmap         sched          sync           tracing        vfio           wakeup_sources
asoc           bluetooth      cleancache     clk            device_component  fault_around_bytes  hid            iwlwifi        memcg          mtd            pwm            regulator      sched_debug    tegra_combined_sdmmc4  ubi            videobuf2      x86
bdi            boot_params    cleancache     clk            device_component  gpio           hrtimer        kprobes        mmc0           nvme           qat            rpm            schedstat      thp            ucsi           virtio-ports   zswap

2.6 /sys/module - 已加载内核模块信息

/sys/module展示了系统中所有已加载的内核模块:

bash 复制代码
$ ls /sys/module/ | head -20
8250               efivarfs           i2c_i801           nf_conntrack        pcie_aspm          scsi_mod           usbcore
acpi               ehci_hcd           i2c_smbus          nf_conntrack_ftp    pciehp             sd_mod             usbhid
acpi_cpufreq       ehci_pci           i8042              nf_conntrack_irc    pcspkr             serio              usb_storage
aesni_intel        e1000e             ib_core            nf_conntrack_netbios_ns  perf_pmu           sg                 vboxguest
af_packet          ext4               igb                nf_conntrack_sip    pinctrl_amd        shpchp             vboxvideo
ahci               fb                 i915               nf_defrag_ipv4      pinctrl_intel      sis900             video

2.6.1 模块参数查看
bash 复制代码
# 查看ext4模块的参数
$ ls /sys/module/ext4/parameters/
commit  debug_guardpage_minorder  default_mb_history_len  mb_cache_scan_limit  mb_max_linear_groups  mb_max_to_scan  mb_min_to_scan  mb_order2_req  mb_stats  nojournal_checksum  nodelalloc  use_dax

# 查看当前参数值
$ cat /sys/module/ext4/parameters/debug_guardpage_minorder
-1

2.6.2 模块引用计数
bash 复制代码
# 查看模块的引用计数
$ cat /sys/module/ext4/refcnt
6

2.7 /sys/power - 电源管理配置

/sys/power提供了电源管理相关的配置接口:

bash 复制代码
$ ls /sys/power/
async        disk         mem_sleep    pm_debug_messages  pm_print_times  pm_test        resume       wakeup_count
autosleep    energy_performance_preference  mem_sleep_current  pm_freeze_timeout  pm_async     pm_trace     state        wakeup_lock

2.7.1 电源状态控制
bash 复制代码
# 查看支持的电源状态
$ cat /sys/power/state
freeze mem disk

# 挂起到内存(休眠)
$ echo mem > /sys/power/state

# 查看磁盘休眠模式
$ cat /sys/power/disk
[platform] shutdown reboot suspend test_resume

2.7.2 唤醒源配置
bash 复制代码
# 查看唤醒事件计数
$ cat /sys/power/wakeup_count
0

# 查看唤醒锁
$ cat /sys/power/wakeup_lock

2.8 /sys/firmware - 固件相关信息

/sys/firmware提供了对系统固件(如ACPI、DTB、EFI)的访问:

bash 复制代码
$ ls /sys/firmware/
acpi  dmi  efi  memmap

2.8.1 ACPI表信息
bash 复制代码
$ ls /sys/firmware/acpi/tables/
ACPI  APIC  ASF!  BOOT  DSDT  ECDT  FACS  FADT  HPET  MCFG  SSDT  TCPA  WAET

# 查看ACPI表内容
$ hexdump -C /sys/firmware/acpi/tables/DSDT | head -20
00000000  44 53 44 54 6c 06 00 00  01 d9 49 4e 54 45 4c 20  |DSDTl.....INTEL |
00000010  45 44 4b 32 20 20 20 20  01 01 49 4e 54 4c 20 20  |EDK2    ..INTL  |
00000020  01 01 0c 4d 54 4c 20 2d  20 00 00 00 49 4e 54 4c  |...MTL - ...INTL|
00000030  01 01 13 13 20 20 20 20  5b 80 00 00 4f 41 4d 4c  |....    [...OAML|

2.8.2 DMI信息(SMBIOS)
bash 复制代码
$ ls /sys/firmware/dmi/entries/
1-0  11-0  13-0  15-0  17-0  19-0  2-0  4-0  6-0  8-0
10-0  12-0  14-0  16-0  18-0  20-0  3-0  5-0  7-0  9-0

$ ls /sys/firmware/dmi/id/
bios_date        board_name       board_serial    chassis_asset_tag  chassis_type    chassis_version  modalias         product_family   product_name    product_serial   product_sku      product_uuid     sys_vendor
bios_release     board_vendor     chassis_asset_tag  chassis_serial     chassis_vendor  ec_firmware_release  poweroff        product_version  product_serial   product_version  product_sku      system_sku       uevent

2.9 /sys/fs - 文件系统相关信息

/sys/fs包含了各种文件系统的配置和控制接口:

bash 复制代码
$ ls /sys/fs/
bpf        cgroup     ext4       fuse       mqueue     nfsd       overlayfs  pstore     xfs

2.9.1 cgroup控制组
bash 复制代码
$ ls /sys/fs/cgroup/
blkio  cpu,cpuacct  cpuset   freezer  memory  net_cls,net_prio  perf_event  pids  rdma  systemd
cpu    cpuacct      devices  hugetlb  misc    net_prio          pids        rdma  unified

2.10 /sys/dev - 字符和块设备的主次设备号视图

/sys/dev按照设备类型(字符设备/块设备)和设备号组织设备:

bash 复制代码
$ ls /sys/dev/
block  char

# 查看字符设备
$ ls /sys/dev/char/ | head -10
10:1  10:58  10:157  10:229  10:62  1:3    1:9    4:0   4:65
10:10  10:59  10:158  10:23   10:63  1:4    1:10   4:1   4:66
10:11  10:60  10:159  10:24   10:64  1:5    10:0   4:10  4:67

第三部分:sysfs属性文件详解

3.1 属性文件的类型

sysfs中的文件主要有以下几种类型:

  1. 属性文件(Attribute Files):包含设备或驱动的属性值,可读或可读写
  2. 符号链接(Symbolic Links):指向其他目录,建立设备间的关联
  3. 二进制文件(Binary Files):包含二进制数据,如固件镜像
  4. 目录(Directories):组织其他文件和子目录

3.2 常用属性文件解析

3.2.1 uevent文件

uevent文件用于触发uevent事件,当向该文件写入"add"、"remove"等命令时,会触发相应的内核事件:

bash 复制代码
# 触发设备添加事件
$ echo add > /sys/bus/pci/devices/0000:00:1f.2/uevent

# 查看uevent内容(设备属性)
$ cat /sys/bus/pci/devices/0000:00:1f.2/uevent
DRIVER=ahci
PCI_CLASS=10601
PCI_ID=8086:A352
PCI_SUBSYS_ID=17AA:2246
PCI_SLOT_NAME=0000:00:1f.2
MODALIAS=pci:v00008086d0000A352sv000017AAsd00002246bc01sc06i01

3.2.2 modalias文件

modalias文件包含了设备的标识信息,用于自动加载相应的驱动程序:

bash 复制代码
$ cat /sys/bus/pci/devices/0000:00:1f.2/modalias
pci:v00008086d0000A352sv000017AAsd00002246bc01sc06i01

这个字符串的格式为:

  • pci: - 总线类型
  • v00008086 - 厂商ID(16位十六进制)
  • d0000A352 - 设备ID(16位十六进制)
  • sv000017AA - 子系统厂商ID
  • sd00002246 - 子系统设备ID
  • bc01 - 基类代码
  • sc06 - 子类代码
  • i01 - 编程接口

3.2.3 driver符号链接

driver符号链接指向设备当前绑定的驱动程序:

bash 复制代码
$ ls -l /sys/bus/pci/devices/0000:00:1f.2/driver
lrwxrwxrwx 1 root root 0 May 15 11:00 /sys/bus/pci/devices/0000:00:1f.2/driver -> ../../../../bus/pci/drivers/ahci

3.2.4 subsystem符号链接

subsystem符号链接指向设备所属的总线子系统:

bash 复制代码
$ ls -l /sys/bus/pci/devices/0000:00:1f.2/subsystem
lrwxrwxrwx 1 root root 0 May 15 11:00 /sys/bus/pci/devices/0000:00:1f.2/subsystem -> ../../../../bus/pci

3.3 设备特定属性

不同设备类型有不同的属性文件,以下是一些常见示例:


3.3.1 网络设备属性
bash 复制代码
# 查看网络设备状态
$ cat /sys/class/net/enp0s31f6/operstate
up

# 查看MAC地址
$ cat /sys/class/net/enp0s31f6/address
8c:16:45:12:34:56

# 查看最大传输单元
$ cat /sys/class/net/enp0s31f6/mtu
1500

# 查看速度和双工模式
$ cat /sys/class/net/enp0s31f6/speed
1000
$ cat /sys/class/net/enp0s31f6/duplex
full

3.3.2 块设备属性
bash 复制代码
# 查看磁盘大小(扇区数)
$ cat /sys/class/block/sda/size
976773168

# 转换为GB大小
$ echo "$(cat /sys/class/block/sda/size) * 512 / 1024 / 1024 / 1024" | bc
465

# 查看只读属性
$ cat /sys/class/block/sda/ro
0

# 查看队列调度器
$ cat /sys/class/block/sda/queue/scheduler
[mq-deadline] kyber bfq none

3.3.3 CPU相关属性
bash 复制代码
# 查看CPU频率信息
$ ls /sys/devices/system/cpu/cpu0/cpufreq/
affected_cpus                  cpuinfo_cur_freq     cpuinfo_min_freq  related_cpus        scaling_driver
cpuinfo_max_freq               cpuinfo_transition_latency  scaling_available_frequencies  scaling_governor
cpuinfo_min_freq               cpuinfo_transition_latency  scaling_available_governors    scaling_max_freq
cpuinfo_transition_latency     related_cpus         scaling_cur_freq  scaling_min_freq

# 查看当前CPU频率
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
1800000

# 查看可用调控器
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
performance powersave

# 查看CPU拓扑信息
$ cat /sys/devices/system/cpu/cpu0/topology/core_id
0
$ cat /sys/devices/system/cpu/cpu0/topology/thread_siblings
00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f

3.3.4 温度传感器属性
bash 复制代码
# 查看温度传感器
$ find /sys/class/hwmon -name "temp*_input" | head -5
/sys/class/hwmon/hwmon0/temp1_input
/sys/class/hwmon/hwmon1/temp1_input
/sys/class/hwmon/hwmon2/temp1_input

# 读取CPU温度(需要除以1000得到摄氏度)
$ cat /sys/class/hwmon/hwmon0/temp1_input
45000  # 表示45°C

# 查看温度标签
$ cat /sys/class/hwmon/hwmon0/temp1_label
Package id 0

3.3.5 背光控制属性
bash 复制代码
# 查看背光设备
$ ls /sys/class/backlight/
intel_backlight

# 查看最大亮度
$ cat /sys/class/backlight/intel_backlight/max_brightness
4437

# 查看当前亮度
$ cat /sys/class/backlight/intel_backlight/brightness
2218

# 调整亮度(需要root权限)
$ echo 3000 | sudo tee /sys/class/backlight/intel_backlight/brightness

3.3.6 LED控制属性
bash 复制代码
# 查看系统LED
$ ls /sys/class/leds/
input0::capslock  input0::numlock  input0::scrolllock  phy0-led

# 查看LED的触发条件
$ cat /sys/class/leds/input0::capslock/trigger
none kbd-capslock kbd-scrolllock kbd-numlock usbport disk-activity disk-read disk-write ide-disk mtd nand-disk heartbeat cpu cpu0 cpu1 cpu2 cpu3 [ac] ac-online ac-offline rfkill0 rfkill1 rfkill2 rfkill3 rfkill4 rfkill5

# 设置触发条件(方括号表示当前激活的触发器)
$ echo heartbeat | sudo tee /sys/class/leds/input0::capslock/trigger

# 查看亮度
$ cat /sys/class/leds/input0::capslock/brightness
0

# 设置亮度(0-255)
$ echo 255 | sudo tee /sys/class/leds/input0::capslock/brightness

第四部分:sysfs与设备管理实践

4.1 设备发现与枚举

4.1.1 使用sysfs发现USB设备
bash 复制代码
#!/bin/bash
# usb_device_scanner.sh - 使用sysfs扫描USB设备

echo "USB设备扫描报告"
echo "================"
echo "扫描时间: $(date)"
echo ""

# 查找所有USB设备
find /sys/bus/usb/devices/ -maxdepth 2 -name "vendor" -o -name "product" | while read file; do
    dir=$(dirname "$file")
    
    # 跳过重复目录
    if [[ $dir == *":1."* ]] || [[ $dir == *"usb"*"/"*":1."* ]]; then
        continue
    fi
    
    # 获取设备信息
    if [ -f "$dir/product" ] && [ -f "$dir/manufacturer" ]; then
        product=$(cat "$dir/product" 2>/dev/null)
        manufacturer=$(cat "$dir/manufacturer" 2>/dev/null)
        busnum=$(cat "$dir/busnum" 2>/dev/null)
        devnum=$(cat "$dir/devnum" 2>/dev/null)
        speed=$(cat "$dir/speed" 2>/dev/null 2>/dev/null)
        
        if [ -n "$product" ] && [ "$product" != "(null)" ]; then
            echo "设备: $manufacturer $product"
            echo "  总线: $busnum, 设备号: $devnum"
            echo "  速度: $speed"
            echo ""
        fi
    fi
done

echo "USB端口状态:"
for port in /sys/bus/usb/devices/usb*/port*/status; do
    if [ -f "$port" ]; then
        port_num=$(echo "$port" | grep -o 'port[0-9]*' | cut -c5-)
        status=$(cat "$port")
        echo "  端口 $port_num: $status"
    fi
done

4.1.2 PCI设备扫描
bash 复制代码
#!/bin/bash
# pci_device_scanner.sh - 使用sysfs扫描PCI设备

echo "PCI设备扫描报告"
echo "================"
echo "扫描时间: $(date)"
echo ""

# 遍历所有PCI设备
for device in /sys/bus/pci/devices/*; do
    if [ -d "$device" ]; then
        # 获取设备信息
        vendor=$(cat "$device/vendor" 2>/dev/null | sed 's/^0x//')
        device_id=$(cat "$device/device" 2>/dev/null | sed 's/^0x//')
        class=$(cat "$device/class" 2>/dev/null | sed 's/^0x//')
        irq=$(cat "$device/irq" 2>/dev/null)
        driver=$(basename $(readlink "$device/driver" 2>/dev/null) 2>/dev/null)
        
        # 获取设备名称
        device_name=$(basename "$device")
        
        # 转换十六进制值为可读名称
        vendor_name=$(lspci -d "$vendor:" 2>/dev/null | head -1 | cut -d' ' -f3- | cut -d':' -f1 || echo "未知厂商")
        device_name_full=$(lspci -d "$vendor:$device_id" 2>/dev/null | head -1 | cut -d':' -f3- || echo "未知设备")
        
        echo "设备: $device_name"
        echo "  位置: $device_name"
        echo "  厂商: $vendor_name ($vendor)"
        echo "  设备: $device_name_full ($device_id)"
        echo "  类别: 0x$class"
        echo "  IRQ: $irq"
        echo "  驱动: ${driver:-无}"
        echo ""
    fi
done

# 统计信息
echo "PCI设备统计:"
echo "  总设备数: $(ls -d /sys/bus/pci/devices/* | wc -l)"
echo "  有驱动设备: $(ls -d /sys/bus/pci/devices/*/driver 2>/dev/null | wc -l)"
echo "  无驱动设备: $(ls -d /sys/bus/pci/devices/*/driver 2>/dev/null | grep -c ^$ || echo 0)"

4.2 设备绑定与解绑

4.2.1 手动绑定驱动
bash 复制代码
#!/bin/bash
# 设备驱动绑定工具

# 查找未绑定的PCI设备
find_unbound_pci_devices() {
    echo "未绑定的PCI设备:"
    echo "----------------"
    
    for device in /sys/bus/pci/devices/*; do
        if [ ! -L "$device/driver" ]; then
            device_id=$(basename "$device")
            vendor=$(cat "$device/vendor" 2>/dev/null | sed 's/^0x//')
            device_num=$(cat "$device/device" 2>/dev/null | sed 's/^0x//')
            
            echo "  $device_id: $vendor:$device_num"
        fi
    done
}

# 绑定设备到驱动
bind_device_to_driver() {
    local device=$1
    local driver=$2
    
    if [ ! -d "/sys/bus/pci/devices/$device" ]; then
        echo "错误: 设备 $device 不存在"
        return 1
    fi
    
    if [ ! -d "/sys/bus/pci/drivers/$driver" ]; then
        echo "错误: 驱动 $driver 不存在"
        return 1
    fi
    
    # 检查设备是否已绑定
    if [ -L "/sys/bus/pci/devices/$device/driver" ]; then
        current_driver=$(basename $(readlink "/sys/bus/pci/devices/$device/driver"))
        echo "设备已绑定到驱动: $current_driver"
        echo "需要先解绑"
        return 1
    fi
    
    # 绑定设备
    echo "绑定设备 $device 到驱动 $driver..."
    echo "$device" | sudo tee "/sys/bus/pci/drivers/$driver/bind" > /dev/null 2>&1
    
    if [ $? -eq 0 ]; then
        echo "绑定成功"
    else
        echo "绑定失败"
        return 1
    fi
}

# 从驱动解绑设备
unbind_device_from_driver() {
    local device=$1
    
    if [ ! -d "/sys/bus/pci/devices/$device" ]; then
        echo "错误: 设备 $device 不存在"
        return 1
    fi
    
    # 检查设备是否已绑定
    if [ ! -L "/sys/bus/pci/devices/$device/driver" ]; then
        echo "设备未绑定到任何驱动"
        return 1
    fi
    
    driver_link=$(readlink "/sys/bus/pci/devices/$device/driver")
    driver=$(basename "$driver_link")
    
    echo "从驱动 $driver 解绑设备 $device..."
    echo "$device" | sudo tee "/sys/bus/pci/drivers/$driver/unbind" > /dev/null 2>&1
    
    if [ $? -eq 0 ]; then
        echo "解绑成功"
    else
        echo "解绑失败"
        return 1
    fi
}

# 主菜单
main_menu() {
    echo "PCI设备驱动管理工具"
    echo "===================="
    echo "1. 查看未绑定设备"
    echo "2. 绑定设备到驱动"
    echo "3. 从驱动解绑设备"
    echo "4. 查看可用驱动"
    echo "5. 退出"
    echo ""
    
    read -p "请选择操作 [1-5]: " choice
    
    case $choice in
        1)
            find_unbound_pci_devices
            ;;
        2)
            read -p "请输入设备ID (如0000:00:1f.2): " device
            read -p "请输入驱动名称: " driver
            bind_device_to_driver "$device" "$driver"
            ;;
        3)
            read -p "请输入设备ID (如0000:00:1f.2): " device
            unbind_device_from_driver "$device"
            ;;
        4)
            echo "可用PCI驱动:"
            ls /sys/bus/pci/drivers/
            ;;
        5)
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    echo ""
    read -p "按回车键继续..."
    main_menu
}

# 检查root权限
if [ "$EUID" -ne 0 ]; then
    echo "需要root权限运行此脚本"
    exit 1
fi

main_menu

4.2.2 驱动黑名单管理
bash 复制代码
#!/bin/bash
# 驱动黑名单管理工具

# 查看当前黑名单
view_blacklist() {
    echo "当前驱动黑名单:"
    echo "----------------"
    
    if [ -f /etc/modprobe.d/blacklist.conf ]; then
        grep -E '^blacklist' /etc/modprobe.d/blacklist.conf
    else
        echo "黑名单文件不存在"
    fi
    
    echo ""
    echo "系统已加载的模块:"
    echo "-----------------"
    lsmod | head -20
}

# 添加驱动到黑名单
add_to_blacklist() {
    local driver=$1
    
    if [ -z "$driver" ]; then
        read -p "请输入要加入黑名单的驱动名称: " driver
    fi
    
    if [ -z "$driver" ]; then
        echo "错误: 驱动名称不能为空"
        return 1
    fi
    
    # 检查是否已在黑名单中
    if grep -q "^blacklist $driver" /etc/modprobe.d/blacklist.conf 2>/dev/null; then
        echo "驱动 $driver 已在黑名单中"
        return 0
    fi
    
    # 添加到黑名单
    echo "添加驱动 $driver 到黑名单..."
    echo "blacklist $driver" | sudo tee -a /etc/modprobe.d/blacklist.conf > /dev/null
    
    if [ $? -eq 0 ]; then
        echo "添加成功"
        
        # 建议重新生成initramfs
        read -p "是否重新生成initramfs?(y/n): " regen
        if [ "$regen" = "y" ] || [ "$regen" = "Y" ]; then
            sudo update-initramfs -u
        fi
    else
        echo "添加失败"
        return 1
    fi
}

# 从黑名单移除驱动
remove_from_blacklist() {
    local driver=$1
    
    if [ -z "$driver" ]; then
        read -p "请输入要从黑名单移除的驱动名称: " driver
    fi
    
    if [ -z "$driver" ]; then
        echo "错误: 驱动名称不能为空"
        return 1
    fi
    
    # 检查是否在黑名单中
    if ! grep -q "^blacklist $driver" /etc/modprobe.d/blacklist.conf 2>/dev/null; then
        echo "驱动 $driver 不在黑名单中"
        return 0
    fi
    
    # 从黑名单移除
    echo "从黑名单移除驱动 $driver..."
    sudo sed -i "/^blacklist $driver$/d" /etc/modprobe.d/blacklist.conf
    
    if [ $? -eq 0 ]; then
        echo "移除成功"
        
        # 建议重新生成initramfs
        read -p "是否重新生成initramfs?(y/n): " regen
        if [ "$regen" = "y" ] || [ "$regen" = "Y" ]; then
            sudo update-initramfs -u
        fi
    else
        echo "移除失败"
        return 1
    fi
}

# 立即卸载驱动
unload_driver() {
    local driver=$1
    
    if [ -z "$driver" ]; then
        read -p "请输入要卸载的驱动名称: " driver
    fi
    
    if [ -z "$driver" ]; then
        echo "错误: 驱动名称不能为空"
        return 1
    fi
    
    # 检查驱动是否已加载
    if ! lsmod | grep -q "^$driver"; then
        echo "驱动 $driver 未加载"
        return 0
    fi
    
    # 检查是否有设备正在使用该驱动
    device_count=$(find /sys/bus/*/drivers/$driver -type l 2>/dev/null | wc -l)
    
    if [ $device_count -gt 0 ]; then
        echo "警告: 有 $device_count 个设备正在使用驱动 $driver"
        read -p "确定要强制卸载吗?(y/n): " confirm
        
        if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
            echo "取消卸载"
            return 0
        fi
        
        # 先解绑所有设备
        echo "解绑设备..."
        for device_link in /sys/bus/*/drivers/$driver/*; do
            if [ -L "$device_link" ]; then
                device_id=$(basename "$device_link")
                bus_type=$(basename $(dirname $(dirname "$device_link")))
                
                echo "解绑设备 $device_id..."
                echo "$device_id" | sudo tee "/sys/bus/$bus_type/drivers/$driver/unbind" > /dev/null 2>&1
            fi
        done
    fi
    
    # 卸载驱动模块
    echo "卸载驱动模块 $driver..."
    sudo modprobe -r $driver
    
    if [ $? -eq 0 ]; then
        echo "卸载成功"
    else
        echo "卸载失败"
        return 1
    fi
}

# 主菜单
main_menu() {
    clear
    echo "驱动黑名单管理工具"
    echo "==================="
    echo "1. 查看当前黑名单和已加载模块"
    echo "2. 添加驱动到黑名单"
    echo "3. 从黑名单移除驱动"
    echo "4. 立即卸载驱动"
    echo "5. 退出"
    echo ""
    
    read -p "请选择操作 [1-5]: " choice
    
    case $choice in
        1)
            view_blacklist
            ;;
        2)
            add_to_blacklist
            ;;
        3)
            remove_from_blacklist
            ;;
        4)
            unload_driver
            ;;
        5)
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    echo ""
    read -p "按回车键继续..."
    main_menu
}

# 检查root权限
if [ "$EUID" -ne 0 ]; then
    echo "需要root权限运行此脚本"
    exit 1
fi

main_menu

4.3 电源管理控制

4.3.1 CPU频率调控
bash 复制代码
#!/bin/bash
# CPU频率管理工具

# 显示所有CPU的频率信息
show_cpu_freq_info() {
    echo "CPU频率信息"
    echo "============"
    
    for cpu in $(ls -d /sys/devices/system/cpu/cpu[0-9]* | sort -V); do
        cpu_num=$(basename $cpu)
        
        if [ -f "$cpu/cpufreq/scaling_cur_freq" ]; then
            cur_freq=$(cat "$cpu/cpufreq/scaling_cur_freq")
            min_freq=$(cat "$cpu/cpufreq/scaling_min_freq")
            max_freq=$(cat "$cpu/cpufreq/scaling_max_freq")
            governor=$(cat "$cpu/cpufreq/scaling_governor")
            
            # 转换为MHz
            cur_freq_mhz=$((cur_freq / 1000))
            min_freq_mhz=$((min_freq / 1000))
            max_freq_mhz=$((max_freq / 1000))
            
            echo "$cpu_num:"
            echo "  当前频率: $cur_freq_mhz MHz"
            echo "  最小频率: $min_freq_mhz MHz"
            echo "  最大频率: $max_freq_mhz MHz"
            echo "  调控器: $governor"
            echo ""
        fi
    done
    
    # 显示可用的调控器
    echo "可用调控器:"
    cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
    echo ""
}

# 设置CPU调控器
set_cpu_governor() {
    local governor=$1
    
    if [ -z "$governor" ]; then
        read -p "请输入要设置的调控器: " governor
    fi
    
    # 检查调控器是否可用
    available_governors=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors)
    if [[ ! " $available_governors " =~ " $governor " ]]; then
        echo "错误: 调控器 $governor 不可用"
        echo "可用调控器: $available_governors"
        return 1
    fi
    
    echo "设置所有CPU调控器为 $governor..."
    
    for cpu in $(ls -d /sys/devices/system/cpu/cpu[0-9]*); do
        if [ -f "$cpu/cpufreq/scaling_governor" ]; then
            echo "$governor" | sudo tee "$cpu/cpufreq/scaling_governor" > /dev/null
            echo "  $(basename $cpu): 已设置"
        fi
    done
    
    echo "设置完成"
}

# 设置CPU频率限制
set_cpu_frequency() {
    local min_freq=$1
    local max_freq=$2
    
    if [ -z "$min_freq" ]; then
        read -p "请输入最小频率(KHz): " min_freq
    fi
    
    if [ -z "$max_freq" ]; then
        read -p "请输入最大频率(KHz): " max_freq
    fi
    
    # 验证输入
    if ! [[ $min_freq =~ ^[0-9]+$ ]] || ! [[ $max_freq =~ ^[0-9]+$ ]]; then
        echo "错误: 频率必须是数字"
        return 1
    fi
    
    if [ $min_freq -gt $max_freq ]; then
        echo "错误: 最小频率不能大于最大频率"
        return 1
    fi
    
    echo "设置CPU频率范围: $min_freq KHz - $max_freq KHz..."
    
    for cpu in $(ls -d /sys/devices/system/cpu/cpu[0-9]*); do
        if [ -f "$cpu/cpufreq/scaling_min_freq" ] && [ -f "$cpu/cpufreq/scaling_max_freq" ]; then
            # 检查是否在允许范围内
            cpu_min=$(cat "$cpu/cpufreq/cpuinfo_min_freq")
            cpu_max=$(cat "$cpu/cpufreq/cpuinfo_max_freq")
            
            if [ $min_freq -lt $cpu_min ]; then
                echo "  $(basename $cpu): 最小频率 $min_freq 低于硬件支持的最小值 $cpu_min,使用 $cpu_min"
                min_freq_actual=$cpu_min
            else
                min_freq_actual=$min_freq
            fi
            
            if [ $max_freq -gt $cpu_max ]; then
                echo "  $(basename $cpu): 最大频率 $max_freq 高于硬件支持的最大值 $cpu_max,使用 $cpu_max"
                max_freq_actual=$cpu_max
            else
                max_freq_actual=$max_freq
            fi
            
            # 设置频率
            echo "$min_freq_actual" | sudo tee "$cpu/cpufreq/scaling_min_freq" > /dev/null
            echo "$max_freq_actual" | sudo tee "$cpu/cpufreq/scaling_max_freq" > /dev/null
            
            echo "  $(basename $cpu): 已设置范围 $min_freq_actual-$max_freq_actual KHz"
        fi
    done
    
    echo "设置完成"
}

# 启用/禁用CPU核心
toggle_cpu_core() {
    local cpu_num=$1
    local action=$2
    
    if [ -z "$cpu_num" ]; then
        read -p "请输入CPU核心编号(0-$(($(nproc)-1))): " cpu_num
    fi
    
    if [ -z "$action" ]; then
        read -p "请选择操作 (enable/disable): " action
    fi
    
    cpu_path="/sys/devices/system/cpu/cpu$cpu_num"
    
    if [ ! -d "$cpu_path" ]; then
        echo "错误: CPU核心 $cpu_num 不存在"
        return 1
    fi
    
    if [ "$action" = "disable" ] || [ "$action" = "0" ]; then
        if [ $cpu_num -eq 0 ]; then
            echo "错误: 不能禁用CPU0"
            return 1
        fi
        
        echo "禁用CPU核心 $cpu_num..."
        echo 0 | sudo tee "$cpu_path/online" > /dev/null
        
        if [ $? -eq 0 ]; then
            echo "CPU核心 $cpu_num 已禁用"
        else
            echo "禁用失败"
        fi
        
    elif [ "$action" = "enable" ] || [ "$action" = "1" ]; then
        echo "启用CPU核心 $cpu_num..."
        echo 1 | sudo tee "$cpu_path/online" > /dev/null
        
        if [ $? -eq 0 ]; then
            echo "CPU核心 $cpu_num 已启用"
        else
            echo "启用失败"
        fi
        
    else
        echo "错误: 无效的操作 '$action',请使用 enable/disable 或 1/0"
        return 1
    fi
}

# 主菜单
main_menu() {
    clear
    echo "CPU频率管理工具"
    echo "================"
    echo "1. 显示CPU频率信息"
    echo "2. 设置CPU调控器"
    echo "3. 设置CPU频率范围"
    echo "4. 启用/禁用CPU核心"
    echo "5. 退出"
    echo ""
    
    read -p "请选择操作 [1-5]: " choice
    
    case $choice in
        1)
            show_cpu_freq_info
            ;;
        2)
            set_cpu_governor
            ;;
        3)
            set_cpu_frequency
            ;;
        4)
            toggle_cpu_core
            ;;
        5)
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    echo ""
    read -p "按回车键继续..."
    main_menu
}

# 检查root权限(某些操作需要)
if [ "$EUID" -ne 0 ]; then
    echo "注意: 部分操作需要root权限"
    read -p "继续吗?(y/n): " continue_as_user
    if [ "$continue_as_user" != "y" ] && [ "$continue_as_user" != "Y" ]; then
        exit 1
    fi
fi

main_menu

4.3.2 电源状态监控
bash 复制代码
#!/bin/bash
# 系统电源状态监控工具

# 显示ACPI电源信息
show_acpi_power_info() {
    echo "ACPI电源信息"
    echo "============"
    
    # 检查AC适配器状态
    if [ -d "/sys/class/power_supply/AC" ] || [ -d "/sys/class/power_supply/ACAD" ]; then
        ac_dir=$(find /sys/class/power_supply -name "AC*" -type d | head -1)
        
        if [ -n "$ac_dir" ]; then
            echo "AC适配器:"
            echo "  状态: $(cat $ac_dir/online)"
            echo "  型号: $(cat $ac_dir/model_name 2>/dev/null || echo '未知')"
            echo "  制造商: $(cat $ac_dir/manufacturer 2>/dev/null || echo '未知')"
            echo ""
        fi
    fi
    
    # 检查电池信息
    battery_count=0
    for battery in /sys/class/power_supply/BAT*; do
        if [ -d "$battery" ]; then
            battery_count=$((battery_count + 1))
            
            echo "电池 $battery_count:"
            echo "  状态: $(cat $battery/status 2>/dev/null)"
            echo "  容量: $(cat $battery/capacity 2>/dev/null)%"
            echo "  健康状态: $(cat $battery/health 2>/dev/null || echo '未知')"
            echo "  技术: $(cat $battery/technology 2>/dev/null || echo '未知')"
            echo "  当前: $(cat $battery/current_now 2>/dev/null || echo '未知') μA"
            echo "  电压: $(cat $battery/voltage_now 2>/dev/null || echo '未知') μV"
            echo "  功耗: $(cat $battery/power_now 2>/dev/null || echo '未知') μW"
            echo ""
        fi
    done
    
    if [ $battery_count -eq 0 ]; then
        echo "未检测到电池"
        echo ""
    fi
}

# 显示CPU电源状态
show_cpu_power_info() {
    echo "CPU电源状态"
    echo "==========="
    
    # 检查Intel P-state状态
    if [ -d "/sys/devices/system/cpu/intel_pstate" ]; then
        echo "Intel P-state:"
        echo "  状态: $(cat /sys/devices/system/cpu/intel_pstate/status)"
        echo "  最大性能: $(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct)%"
        echo "  最小性能: $(cat /sys/devices/system/cpu/intel_pstate/min_perf_pct)%"
        echo "  Turbo Boost: $(cat /sys/devices/system/cpu/intel_pstate/no_turbo)"
        echo ""
    fi
    
    # 检查CPU空闲状态
    if [ -d "/sys/devices/system/cpu/cpuidle" ]; then
        echo "CPU空闲状态:"
        
        for cpu in $(ls -d /sys/devices/system/cpu/cpu[0-9]* | sort -V); do
            cpu_num=$(basename $cpu)
            
            if [ -d "$cpu/cpuidle" ]; then
                echo "  $cpu_num:"
                
                for state in $(ls -d $cpu/cpuidle/state* 2>/dev/null | sort -V); do
                    state_name=$(cat $state/name 2>/dev/null)
                    state_desc=$(cat $state/desc 2>/dev/null)
                    state_latency=$(cat $state/latency 2>/dev/null)
                    state_power=$(cat $state/power 2>/dev/null)
                    state_time=$(cat $state/time 2>/dev/null)
                    state_usage=$(cat $state/usage 2>/dev/null)
                    
                    echo "    $state_name: 延迟=${state_latency}us, 功耗=${state_power}mW, 时间=${state_time}us, 使用=${state_usage}"
                done
            fi
        done
        echo ""
    fi
}

# 显示设备电源状态
show_device_power_info() {
    echo "设备电源状态"
    echo "============"
    
    # 查找所有可控制电源的设备
    device_count=0
    find /sys/devices -name "power" -type d | while read power_dir; do
        device_dir=$(dirname "$power_dir")
        device_name=$(basename "$device_dir")
        
        # 跳过一些系统设备
        if [[ $device_name =~ ^(cpu|virtual|platform) ]] || [ ${#device_name} -gt 50 ]; then
            continue
        fi
        
        if [ -f "$power_dir/control" ] && [ -f "$power_dir/runtime_status" ]; then
            device_count=$((device_count + 1))
            
            control=$(cat "$power_dir/control" 2>/dev/null)
            runtime_status=$(cat "$power_dir/runtime_status" 2>/dev/null)
            runtime_enabled=$(cat "$power_dir/runtime_enabled" 2>/dev/null 2>/dev/null || echo "未知")
            
            echo "设备 $device_count: $device_name"
            echo "  控制: $control"
            echo "  运行状态: $runtime_status"
            echo "  运行时启用: $runtime_enabled"
            
            # 显示唤醒能力
            if [ -f "$power_dir/wakeup" ]; then
                wakeup=$(cat "$power_dir/wakeup" 2>/dev/null)
                echo "  唤醒能力: $wakeup"
            fi
            
            echo ""
        fi
    done
    
    if [ $device_count -eq 0 ]; then
        echo "未找到可控制电源的设备"
        echo ""
    fi
}

# 显示系统唤醒源
show_wakeup_sources() {
    echo "系统唤醒源"
    echo "=========="
    
    if [ -f "/sys/power/wakeup_count" ]; then
        echo "唤醒计数: $(cat /sys/power/wakeup_count)"
    fi
    
    echo ""
    echo "可唤醒设备:"
    
    # 查找所有支持唤醒的设备
    find /sys/devices -name "wakeup" -type f | while read wakeup_file; do
        device_dir=$(dirname $(dirname "$wakeup_file"))
        device_name=$(basename "$device_dir")
        
        wakeup_status=$(cat "$wakeup_file" 2>/dev/null)
        
        if [ "$wakeup_status" = "enabled" ] || [ "$wakeup_status" = "disabled" ]; then
            echo "  $device_name: $wakeup_status"
        fi
    done | sort
    
    echo ""
}

# 电源管理统计
show_power_stats() {
    echo "电源管理统计"
    echo "============"
    
    # 显示系统挂起统计
    if [ -d "/sys/power" ]; then
        echo "系统挂起统计:"
        
        if [ -f "/sys/power/pm_print_times" ]; then
            echo "  PM打印时间: $(cat /sys/power/pm_print_times)"
        fi
        
        if [ -f "/sys/power/pm_debug_messages" ]; then
            echo "  PM调试消息: $(cat /sys/power/pm_debug_messages)"
        fi
        
        echo ""
    fi
    
    # 显示CPU频率统计
    echo "CPU频率统计:"
    
    total_time=0
    for cpu in $(ls -d /sys/devices/system/cpu/cpu[0-9]* | sort -V); do
        if [ -f "$cpu/cpufreq/stats/time_in_state" ]; then
            cpu_num=$(basename $cpu)
            echo "  $cpu_num:"
            
            cat "$cpu/cpufreq/stats/time_in_state" | while read freq time; do
                freq_mhz=$((freq / 1000))
                time_sec=$((time / 100))
                total_time=$((total_time + time_sec))
                
                echo "    ${freq_mhz}MHz: ${time_sec}s"
            done
        fi
    done
    
    echo "  总统计时间: ${total_time}s"
    echo ""
}

# 主菜单
main_menu() {
    clear
    echo "系统电源状态监控工具"
    echo "===================="
    echo "1. 显示ACPI电源信息"
    echo "2. 显示CPU电源状态"
    echo "3. 显示设备电源状态"
    echo "4. 显示系统唤醒源"
    echo "5. 显示电源管理统计"
    echo "6. 全部显示"
    echo "7. 退出"
    echo ""
    
    read -p "请选择操作 [1-7]: " choice
    
    case $choice in
        1)
            show_acpi_power_info
            ;;
        2)
            show_cpu_power_info
            ;;
        3)
            show_device_power_info
            ;;
        4)
            show_wakeup_sources
            ;;
        5)
            show_power_stats
            ;;
        6)
            show_acpi_power_info
            show_cpu_power_info
            show_device_power_info
            show_wakeup_sources
            show_power_stats
            ;;
        7)
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    echo ""
    read -p "按回车键继续..."
    main_menu
}

main_menu

第五部分:sysfs编程接口与实践

5.1 C语言操作sysfs

5.1.1 基础sysfs操作函数
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <dirent.h>

// 读取sysfs属性文件
int read_sysfs_attr(const char *path, char *buffer, size_t size) {
    int fd;
    ssize_t bytes_read;
    
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        return -errno;
    }
    
    bytes_read = read(fd, buffer, size - 1);
    close(fd);
    
    if (bytes_read < 0) {
        return -errno;
    }
    
    buffer[bytes_read] = '\0';
    
    // 去掉换行符
    if (bytes_read > 0 && buffer[bytes_read - 1] == '\n') {
        buffer[bytes_read - 1] = '\0';
    }
    
    return 0;
}

// 写入sysfs属性文件
int write_sysfs_attr(const char *path, const char *value) {
    int fd;
    ssize_t bytes_written;
    
    fd = open(path, O_WRONLY);
    if (fd < 0) {
        return -errno;
    }
    
    bytes_written = write(fd, value, strlen(value));
    close(fd);
    
    if (bytes_written < 0) {
        return -errno;
    }
    
    return 0;
}

// 遍历sysfs目录
int walk_sysfs_dir(const char *path, int depth) {
    DIR *dir;
    struct dirent *entry;
    char full_path[PATH_MAX];
    struct stat statbuf;
    
    dir = opendir(path);
    if (!dir) {
        return -errno;
    }
    
    while ((entry = readdir(dir)) != NULL) {
        // 跳过 "." 和 ".."
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        // 构建完整路径
        snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
        
        // 获取文件信息
        if (lstat(full_path, &statbuf) < 0) {
            continue;
        }
        
        // 缩进显示
        for (int i = 0; i < depth; i++) {
            printf("  ");
        }
        
        printf("%s", entry->d_name);
        
        if (S_ISDIR(statbuf.st_mode)) {
            printf("/\n");
            walk_sysfs_dir(full_path, depth + 1);
        } else if (S_ISLNK(statbuf.st_mode)) {
            char link_target[PATH_MAX];
            ssize_t len = readlink(full_path, link_target, sizeof(link_target) - 1);
            
            if (len > 0) {
                link_target[len] = '\0';
                printf(" -> %s\n", link_target);
            } else {
                printf(" -> [error]\n");
            }
        } else {
            // 如果是普通文件,显示前80个字符的内容
            if (S_ISREG(statbuf.st_mode) && statbuf.st_size > 0) {
                FILE *fp = fopen(full_path, "r");
                if (fp) {
                    char content[81] = {0};
                    size_t read_size = fread(content, 1, 80, fp);
                    fclose(fp);
                    
                    if (read_size > 0) {
                        // 替换换行符
                        for (size_t i = 0; i < read_size; i++) {
                            if (content[i] == '\n') {
                                content[i] = ' ';
                            }
                        }
                        printf(": %s\n", content);
                    } else {
                        printf("\n");
                    }
                } else {
                    printf("\n");
                }
            } else {
                printf("\n");
            }
        }
    }
    
    closedir(dir);
    return 0;
}

// 获取网络接口信息
void get_network_info() {
    DIR *dir;
    struct dirent *entry;
    char path[PATH_MAX];
    char buffer[256];
    
    dir = opendir("/sys/class/net");
    if (!dir) {
        perror("无法打开 /sys/class/net");
        return;
    }
    
    printf("网络接口信息:\n");
    printf("=============\n");
    
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        printf("\n接口: %s\n", entry->d_name);
        
        // 获取MAC地址
        snprintf(path, sizeof(path), "/sys/class/net/%s/address", entry->d_name);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  MAC地址: %s\n", buffer);
        }
        
        // 获取操作状态
        snprintf(path, sizeof(path), "/sys/class/net/%s/operstate", entry->d_name);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  状态: %s\n", buffer);
        }
        
        // 获取MTU
        snprintf(path, sizeof(path), "/sys/class/net/%s/mtu", entry->d_name);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  MTU: %s\n", buffer);
        }
        
        // 获取速度
        snprintf(path, sizeof(path), "/sys/class/net/%s/speed", entry->d_name);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  速度: %s Mbps\n", buffer);
        }
    }
    
    closedir(dir);
}

// 获取CPU信息
void get_cpu_info() {
    char path[PATH_MAX];
    char buffer[256];
    int cpu_count = 0;
    
    printf("CPU信息:\n");
    printf("========\n");
    
    // 统计CPU数量
    for (int i = 0; ; i++) {
        snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d", i);
        if (access(path, F_OK) != 0) {
            break;
        }
        cpu_count++;
    }
    
    printf("CPU核心数: %d\n\n", cpu_count);
    
    // 显示每个CPU的信息
    for (int i = 0; i < cpu_count; i++) {
        printf("CPU%d:\n", i);
        
        // 获取当前频率
        snprintf(path, sizeof(path), 
                "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            long freq = atol(buffer);
            printf("  当前频率: %.2f GHz\n", freq / 1000000.0);
        }
        
        // 获取调控器
        snprintf(path, sizeof(path), 
                "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", i);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  调控器: %s\n", buffer);
        }
        
        // 获取拓扑信息
        snprintf(path, sizeof(path), 
                "/sys/devices/system/cpu/cpu%d/topology/core_id", i);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  核心ID: %s\n", buffer);
        }
        
        // 获取在线状态
        snprintf(path, sizeof(path), 
                "/sys/devices/system/cpu/cpu%d/online", i);
        if (read_sysfs_attr(path, buffer, sizeof(buffer)) == 0) {
            printf("  在线状态: %s\n", buffer);
        }
        
        printf("\n");
    }
}

int main(int argc, char *argv[]) {
    int choice;
    
    printf("sysfs信息查看工具\n");
    printf("================\n");
    printf("1. 查看网络接口信息\n");
    printf("2. 查看CPU信息\n");
    printf("3. 遍历sysfs目录结构\n");
    printf("4. 退出\n");
    printf("\n请选择操作: ");
    
    scanf("%d", &choice);
    
    switch (choice) {
        case 1:
            get_network_info();
            break;
        case 2:
            get_cpu_info();
            break;
        case 3:
            printf("sysfs目录结构 (限于/sys/class):\n");
            printf("==============================\n");
            walk_sysfs_dir("/sys/class", 0);
            break;
        case 4:
            return 0;
        default:
            printf("无效选择\n");
            break;
    }
    
    return 0;
}

5.1.2 内核模块创建sysfs接口
c 复制代码
/*
 * sysfs_example.c - 创建sysfs接口的内核模块示例
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sysfs Example Author");
MODULE_DESCRIPTION("Example of creating sysfs interfaces");
MODULE_VERSION("1.0");

// 自定义数据结构
struct example_data {
    int value;
    char name[32];
    struct kobject kobj;
};

// 全局变量
static struct kobject *example_kobj;
static struct example_data *data_obj;

// 属性显示函数
static ssize_t value_show(struct kobject *kobj, struct kobj_attribute *attr,
                         char *buf)
{
    struct example_data *data = container_of(kobj, struct example_data, kobj);
    return sprintf(buf, "%d\n", data->value);
}

// 属性存储函数
static ssize_t value_store(struct kobject *kobj, struct kobj_attribute *attr,
                          const char *buf, size_t count)
{
    struct example_data *data = container_of(kobj, struct example_data, kobj);
    int new_value;
    
    if (sscanf(buf, "%d", &new_value) != 1) {
        return -EINVAL;
    }
    
    // 简单的验证
    if (new_value < 0 || new_value > 1000) {
        return -EINVAL;
    }
    
    data->value = new_value;
    return count;
}

// 只读属性显示函数
static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *attr,
                        char *buf)
{
    struct example_data *data = container_of(kobj, struct example_data, kobj);
    return sprintf(buf, "%s\n", data->name);
}

// 操作函数示例
static ssize_t action_show(struct kobject *kobj, struct kobj_attribute *attr,
                          char *buf)
{
    return sprintf(buf, "Current time: %llu\n", ktime_get_ns());
}

static ssize_t action_store(struct kobject *kobj, struct kobj_attribute *attr,
                           const char *buf, size_t count)
{
    struct example_data *data = container_of(kobj, struct example_data, kobj);
    
    printk(KERN_INFO "sysfs_example: Action triggered with data: %s\n", buf);
    printk(KERN_INFO "sysfs_example: Current value: %d, name: %s\n", 
           data->value, data->name);
    
    return count;
}

// 定义属性
static struct kobj_attribute value_attr = __ATTR(value, 0664, 
                                                value_show, value_store);
static struct kobj_attribute name_attr = __ATTR(name, 0444, 
                                               name_show, NULL);
static struct kobj_attribute action_attr = __ATTR(action, 0644, 
                                                 action_show, action_store);

// 属性组
static struct attribute *example_attrs[] = {
    &value_attr.attr,
    &name_attr.attr,
    &action_attr.attr,
    NULL,
};

static struct attribute_group example_attr_group = {
    .attrs = example_attrs,
};

// kobject释放函数
static void example_release(struct kobject *kobj)
{
    struct example_data *data = container_of(kobj, struct example_data, kobj);
    printk(KERN_INFO "sysfs_example: Releasing data object\n");
    kfree(data);
}

// kobject类型定义
static struct kobj_type example_ktype = {
    .release = example_release,
    .sysfs_ops = &kobj_sysfs_ops,
    .default_groups = (const struct attribute_group *[]) {
        &example_attr_group,
        NULL,
    },
};

// 模块初始化
static int __init sysfs_example_init(void)
{
    int ret;
    
    printk(KERN_INFO "sysfs_example: Initializing module\n");
    
    // 创建顶层kobject
    example_kobj = kobject_create_and_add("example", kernel_kobj);
    if (!example_kobj) {
        printk(KERN_ERR "sysfs_example: Failed to create kobject\n");
        return -ENOMEM;
    }
    
    // 分配并初始化数据对象
    data_obj = kzalloc(sizeof(*data_obj), GFP_KERNEL);
    if (!data_obj) {
        kobject_put(example_kobj);
        printk(KERN_ERR "sysfs_example: Failed to allocate data object\n");
        return -ENOMEM;
    }
    
    // 初始化数据
    data_obj->value = 42;
    strncpy(data_obj->name, "sysfs_example", sizeof(data_obj->name) - 1);
    data_obj->name[sizeof(data_obj->name) - 1] = '\0';
    
    // 初始化kobject
    ret = kobject_init_and_add(&data_obj->kobj, &example_ktype,
                               example_kobj, "data");
    if (ret) {
        kfree(data_obj);
        kobject_put(example_kobj);
        printk(KERN_ERR "sysfs_example: Failed to initialize kobject\n");
        return ret;
    }
    
    printk(KERN_INFO "sysfs_example: Module initialized successfully\n");
    printk(KERN_INFO "sysfs_example: Sysfs entries created at /sys/kernel/example/data\n");
    
    return 0;
}

// 模块清理
static void __exit sysfs_example_exit(void)
{
    printk(KERN_INFO "sysfs_example: Cleaning up module\n");
    
    // 删除kobject(会自动调用release函数)
    kobject_put(&data_obj->kobj);
    kobject_put(example_kobj);
    
    printk(KERN_INFO "sysfs_example: Module removed\n");
}

module_init(sysfs_example_init);
module_exit(sysfs_example_exit);

对应的Makefile:

makefile 复制代码
obj-m += sysfs_example.o

KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean

install:
	sudo insmod sysfs_example.ko

uninstall:
	sudo rmmod sysfs_example

5.2 Python操作sysfs

5.2.1 Python sysfs工具库
python 复制代码
#!/usr/bin/env python3
"""
sysfs_utils.py - Python sysfs操作工具库
"""

import os
import glob
import re
from pathlib import Path
from typing import List, Dict, Optional, Union

class SysfsError(Exception):
    """sysfs操作异常"""
    pass

class SysfsDevice:
    """sysfs设备基类"""
    
    def __init__(self, path: str):
        self.path = Path(path)
        if not self.path.exists():
            raise SysfsError(f"Path does not exist: {path}")
    
    def read_attr(self, attr: str) -> str:
        """读取属性文件"""
        attr_path = self.path / attr
        if not attr_path.exists():
            raise SysfsError(f"Attribute does not exist: {attr_path}")
        
        try:
            with open(attr_path, 'r') as f:
                value = f.read().strip()
            return value
        except IOError as e:
            raise SysfsError(f"Failed to read {attr_path}: {e}")
    
    def write_attr(self, attr: str, value: str) -> None:
        """写入属性文件"""
        attr_path = self.path / attr
        if not attr_path.exists():
            raise SysfsError(f"Attribute does not exist: {attr_path}")
        
        try:
            with open(attr_path, 'w') as f:
                f.write(str(value))
        except IOError as e:
            raise SysfsError(f"Failed to write {attr_path}: {e}")
    
    def list_attrs(self) -> List[str]:
        """列出所有属性文件"""
        attrs = []
        for item in self.path.iterdir():
            if item.is_file():
                attrs.append(item.name)
        return sorted(attrs)
    
    def list_links(self) -> Dict[str, str]:
        """列出所有符号链接"""
        links = {}
        for item in self.path.iterdir():
            if item.is_symlink():
                try:
                    target = os.readlink(item)
                    links[item.name] = target
                except OSError:
                    continue
        return links
    
    def get_uevent(self) -> Dict[str, str]:
        """解析uevent文件内容"""
        try:
            uevent_content = self.read_attr("uevent")
            uevent_dict = {}
            for line in uevent_content.split('\n'):
                if '=' in line:
                    key, value = line.split('=', 1)
                    uevent_dict[key] = value
            return uevent_dict
        except SysfsError:
            return {}

class SysfsNetwork(SysfsDevice):
    """网络设备类"""
    
    @property
    def name(self) -> str:
        """设备名称"""
        return self.path.name
    
    @property
    def mac_address(self) -> str:
        """MAC地址"""
        return self.read_attr("address")
    
    @property
    def operstate(self) -> str:
        """操作状态"""
        return self.read_attr("operstate")
    
    @property
    def mtu(self) -> int:
        """MTU值"""
        return int(self.read_attr("mtu"))
    
    @property
    def speed(self) -> Optional[int]:
        """连接速度(Mbps)"""
        try:
            return int(self.read_attr("speed"))
        except (SysfsError, ValueError):
            return None
    
    @property
    def duplex(self) -> str:
        """双工模式"""
        try:
            return self.read_attr("duplex")
        except SysfsError:
            return "unknown"
    
    def get_statistics(self) -> Dict[str, int]:
        """获取网络统计信息"""
        stats_path = self.path / "statistics"
        if not stats_path.exists():
            return {}
        
        stats = {}
        for stat_file in stats_path.iterdir():
            if stat_file.is_file():
                try:
                    value = self.read_attr(f"statistics/{stat_file.name}")
                    stats[stat_file.name] = int(value)
                except (SysfsError, ValueError):
                    continue
        
        return stats
    
    def set_mtu(self, mtu: int) -> None:
        """设置MTU值"""
        self.write_attr("mtu", str(mtu))
    
    def bring_up(self) -> None:
        """启用接口"""
        # 实际中需要通过netlink或ioctl操作
        # 这里只是示例
        os.system(f"ip link set {self.name} up")
    
    def bring_down(self) -> None:
        """禁用接口"""
        os.system(f"ip link set {self.name} down")

class SysfsCPU(SysfsDevice):
    """CPU设备类"""
    
    @property
    def cpu_id(self) -> int:
        """CPU ID"""
        match = re.search(r'cpu(\d+)', str(self.path))
        if match:
            return int(match.group(1))
        return -1
    
    @property
    def online(self) -> bool:
        """是否在线"""
        try:
            return self.read_attr("online") == "1"
        except SysfsError:
            return True  # CPU0可能没有online文件
    
    @property
    def current_frequency(self) -> int:
        """当前频率(KHz)"""
        cpufreq_path = self.path / "cpufreq"
        if not cpufreq_path.exists():
            return 0
        
        try:
            freq = self.read_attr("cpufreq/scaling_cur_freq")
            return int(freq)
        except (SysfsError, ValueError):
            return 0
    
    @property
    def governor(self) -> str:
        """当前调控器"""
        try:
            return self.read_attr("cpufreq/scaling_governor")
        except SysfsError:
            return ""
    
    @property
    def topology(self) -> Dict[str, str]:
        """拓扑信息"""
        topology_path = self.path / "topology"
        if not topology_path.exists():
            return {}
        
        topology = {}
        for item in topology_path.iterdir():
            if item.is_file():
                try:
                    value = self.read_attr(f"topology/{item.name}")
                    topology[item.name] = value
                except SysfsError:
                    continue
        
        return topology
    
    def set_online(self, online: bool) -> None:
        """设置在线状态"""
        if self.cpu_id == 0:
            raise SysfsError("Cannot change online state of CPU0")
        
        self.write_attr("online", "1" if online else "0")
    
    def set_governor(self, governor: str) -> None:
        """设置调控器"""
        self.write_attr("cpufreq/scaling_governor", governor)

class SysfsBlock(SysfsDevice):
    """块设备类"""
    
    @property
    def name(self) -> str:
        """设备名称"""
        return self.path.name
    
    @property
    def size(self) -> int:
        """设备大小(扇区数)"""
        try:
            return int(self.read_attr("size"))
        except (SysfsError, ValueError):
            return 0
    
    @property
    def size_gb(self) -> float:
        """设备大小(GB)"""
        sectors = self.size
        return sectors * 512 / (1024**3)
    
    @property
    def removable(self) -> bool:
        """是否可移动"""
        try:
            return self.read_attr("removable") == "1"
        except (SysfsError, ValueError):
            return False
    
    @property
    def read_only(self) -> bool:
        """是否只读"""
        try:
            return self.read_attr("ro") == "1"
        except (SysfsError, ValueError):
            return False
    
    def get_queue_info(self) -> Dict[str, str]:
        """获取队列信息"""
        queue_path = self.path / "queue"
        if not queue_path.exists():
            return {}
        
        queue_info = {}
        for item in queue_path.iterdir():
            if item.is_file():
                try:
                    value = self.read_attr(f"queue/{item.name}")
                    queue_info[item.name] = value
                except SysfsError:
                    continue
        
        return queue_info
    
    def get_partitions(self) -> List['SysfsBlock']:
        """获取分区列表"""
        partitions = []
        for item in self.path.iterdir():
            if item.name.startswith(self.name) and item.name != self.name:
                try:
                    partitions.append(SysfsBlock(item))
                except SysfsError:
                    continue
        
        return partitions

class SysfsManager:
    """sysfs管理器"""
    
    @staticmethod
    def discover_network_devices() -> List[SysfsNetwork]:
        """发现网络设备"""
        devices = []
        net_path = Path("/sys/class/net")
        
        if not net_path.exists():
            return devices
        
        for item in net_path.iterdir():
            if item.is_dir() and not item.name.startswith('.'):
                try:
                    devices.append(SysfsNetwork(item))
                except SysfsError:
                    continue
        
        return devices
    
    @staticmethod
    def discover_cpus() -> List[SysfsCPU]:
        """发现CPU设备"""
        cpus = []
        cpu_pattern = "/sys/devices/system/cpu/cpu[0-9]*"
        
        for cpu_path in glob.glob(cpu_pattern):
            try:
                cpus.append(SysfsCPU(cpu_path))
            except SysfsError:
                continue
        
        return sorted(cpus, key=lambda cpu: cpu.cpu_id)
    
    @staticmethod
    def discover_block_devices() -> List[SysfsBlock]:
        """发现块设备"""
        devices = []
        block_path = Path("/sys/class/block")
        
        if not block_path.exists():
            return devices
        
        for item in block_path.iterdir():
            # 跳过分区,只显示物理设备
            if item.is_dir() and not re.search(r'\d+$', item.name):
                try:
                    devices.append(SysfsBlock(item))
                except SysfsError:
                    continue
        
        return devices
    
    @staticmethod
    def find_device_by_name(name: str) -> Optional[SysfsDevice]:
        """按名称查找设备"""
        # 尝试在网络设备中查找
        net_path = Path(f"/sys/class/net/{name}")
        if net_path.exists():
            return SysfsNetwork(net_path)
        
        # 尝试在块设备中查找
        block_path = Path(f"/sys/class/block/{name}")
        if block_path.exists():
            return SysfsBlock(block_path)
        
        # 尝试在CPU中查找
        cpu_path = Path(f"/sys/devices/system/cpu/{name}")
        if cpu_path.exists():
            return SysfsCPU(cpu_path)
        
        return None
    
    @staticmethod
    def get_system_info() -> Dict[str, Union[str, int, List[str]]]:
        """获取系统信息"""
        info = {}
        
        # 获取内核版本
        try:
            with open("/proc/version", "r") as f:
                info["kernel_version"] = f.read().strip()
        except IOError:
            info["kernel_version"] = "unknown"
        
        # 获取CPU架构
        try:
            with open("/proc/cpuinfo", "r") as f:
                for line in f:
                    if line.startswith("model name"):
                        info["cpu_model"] = line.split(":")[1].strip()
                        break
        except IOError:
            info["cpu_model"] = "unknown"
        
        # 获取内存信息
        try:
            with open("/proc/meminfo", "r") as f:
                for line in f:
                    if line.startswith("MemTotal"):
                        info["total_memory"] = line.split(":")[1].strip()
                        break
        except IOError:
            info["total_memory"] = "unknown"
        
        # 获取已加载模块
        modules = []
        module_path = Path("/sys/module")
        if module_path.exists():
            for item in module_path.iterdir():
                if item.is_dir():
                    modules.append(item.name)
        info["loaded_modules"] = modules
        
        return info

# 示例使用
if __name__ == "__main__":
    import sys
    
    def print_network_info():
        print("网络设备信息:")
        print("=" * 50)
        manager = SysfsManager()
        
        for device in manager.discover_network_devices():
            print(f"\n设备: {device.name}")
            print(f"  MAC地址: {device.mac_address}")
            print(f"  状态: {device.operstate}")
            print(f"  MTU: {device.mtu}")
            
            speed = device.speed
            if speed:
                print(f"  速度: {speed} Mbps")
            
            print(f"  双工模式: {device.duplex}")
            
            # 显示部分统计信息
            stats = device.get_statistics()
            if stats:
                print(f"  接收包: {stats.get('rx_packets', 0)}")
                print(f"  发送包: {stats.get('tx_packets', 0)}")
    
    def print_cpu_info():
        print("CPU信息:")
        print("=" * 50)
        manager = SysfsManager()
        
        cpus = manager.discover_cpus()
        print(f"CPU核心数: {len(cpus)}\n")
        
        for cpu in cpus:
            print(f"CPU{cpu.cpu_id}:")
            print(f"  在线: {'是' if cpu.online else '否'}")
            
            freq_khz = cpu.current_frequency
            if freq_khz > 0:
                freq_ghz = freq_khz / 1000000
                print(f"  频率: {freq_ghz:.2f} GHz")
            
            governor = cpu.governor
            if governor:
                print(f"  调控器: {governor}")
            
            topology = cpu.topology
            if topology:
                print(f"  物理ID: {topology.get('physical_package_id', 'N/A')}")
                print(f"  核心ID: {topology.get('core_id', 'N/A')}")
            
            print()
    
    def print_block_info():
        print("块设备信息:")
        print("=" * 50)
        manager = SysfsManager()
        
        for device in manager.discover_block_devices():
            print(f"\n设备: {device.name}")
            print(f"  大小: {device.size_gb:.2f} GB")
            print(f"  可移动: {'是' if device.removable else '否'}")
            print(f"  只读: {'是' if device.read_only else '否'}")
            
            # 显示分区
            partitions = device.get_partitions()
            if partitions:
                print(f"  分区数: {len(partitions)}")
                for part in partitions:
                    print(f"    - {part.name}: {part.size_gb:.2f} GB")
    
    def print_system_info():
        print("系统信息:")
        print("=" * 50)
        manager = SysfsManager()
        
        info = manager.get_system_info()
        
        for key, value in info.items():
            if key == "loaded_modules":
                print(f"{key}: {len(value)} 个模块已加载")
                if value:
                    print(f"  示例: {', '.join(value[:5])}...")
            else:
                print(f"{key}: {value}")
    
    # 命令行参数处理
    if len(sys.argv) > 1:
        command = sys.argv[1]
        
        if command == "network":
            print_network_info()
        elif command == "cpu":
            print_cpu_info()
        elif command == "block":
            print_block_info()
        elif command == "system":
            print_system_info()
        elif command == "all":
            print_system_info()
            print()
            print_cpu_info()
            print()
            print_block_info()
            print()
            print_network_info()
        else:
            print(f"未知命令: {command}")
            print("可用命令: network, cpu, block, system, all")
    else:
        print("请指定命令:")
        print("  network - 显示网络设备信息")
        print("  cpu - 显示CPU信息")
        print("  block - 显示块设备信息")
        print("  system - 显示系统信息")
        print("  all - 显示所有信息")

5.2.2 sysfs监控工具
python 复制代码
#!/usr/bin/env python3
"""
sysfs_monitor.py - sysfs实时监控工具
"""

import os
import sys
import time
import select
import threading
from pathlib import Path
from collections import defaultdict
from datetime import datetime

class SysfsMonitor:
    """sysfs监控器"""
    
    def __init__(self):
        self.watches = {}  # 监视的文件描述符 -> (路径, 回调)
        self.running = False
        self.thread = None
        
    def add_watch(self, path, callback):
        """添加监视"""
        if not os.path.exists(path):
            print(f"警告: 路径不存在: {path}")
            return False
        
        try:
            fd = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
            self.watches[fd] = (path, callback)
            return True
        except OSError as e:
            print(f"无法打开文件 {path}: {e}")
            return False
    
    def remove_watch(self, path):
        """移除监视"""
        for fd, (watch_path, _) in list(self.watches.items()):
            if watch_path == path:
                os.close(fd)
                del self.watches[fd]
                return True
        return False
    
    def start(self):
        """开始监控"""
        if self.running:
            print("监控已经在运行")
            return
        
        self.running = True
        self.thread = threading.Thread(target=self._monitor_loop)
        self.thread.daemon = True
        self.thread.start()
        print(f"开始监控 {len(self.watches)} 个文件...")
    
    def stop(self):
        """停止监控"""
        self.running = False
        if self.thread:
            self.thread.join(timeout=2)
        
        # 关闭所有文件描述符
        for fd in list(self.watches.keys()):
            os.close(fd)
        self.watches.clear()
        print("监控已停止")
    
    def _monitor_loop(self):
        """监控循环"""
        while self.running and self.watches:
            try:
                # 使用select等待文件变化
                fds = list(self.watches.keys())
                readable, _, _ = select.select(fds, [], [], 1.0)
                
                for fd in readable:
                    if fd in self.watches:
                        path, callback = self.watches[fd]
                        
                        try:
                            # 读取文件内容
                            os.lseek(fd, 0, os.SEEK_SET)
                            content = os.read(fd, 4096).decode('utf-8', errors='ignore').strip()
                            
                            # 调用回调函数
                            if callback:
                                callback(path, content)
                        except OSError as e:
                            print(f"读取文件 {path} 失败: {e}")
                            # 文件可能被删除,移除监视
                            self.remove_watch(path)
                
                # 检查是否有新文件需要监视
                time.sleep(0.1)
                
            except (KeyboardInterrupt, SystemExit):
                break
            except Exception as e:
                print(f"监控循环错误: {e}")
                time.sleep(1)
    
    @staticmethod
    def monitor_cpu_frequency():
        """监控CPU频率"""
        monitor = SysfsMonitor()
        
        # 查找所有CPU频率文件
        cpu_pattern = "/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq"
        cpu_files = []
        
        for cpu_file in glob.glob(cpu_pattern):
            cpu_files.append(cpu_file)
        
        if not cpu_files:
            print("未找到CPU频率文件")
            return
        
        print(f"监控 {len(cpu_files)} 个CPU的频率...")
        print("按 Ctrl+C 停止")
        print("-" * 50)
        
        last_values = {}
        
        def callback(path, content):
            cpu_id = path.split('/')[5]  # 提取cpuX
            try:
                freq_khz = int(content)
                freq_ghz = freq_khz / 1000000
                
                # 只显示变化的值
                if cpu_id not in last_values or last_values[cpu_id] != freq_ghz:
                    timestamp = datetime.now().strftime("%H:%M:%S")
                    print(f"{timestamp} {cpu_id}: {freq_ghz:.2f} GHz")
                    last_values[cpu_id] = freq_ghz
            except ValueError:
                pass
        
        # 添加所有CPU频率文件的监视
        for cpu_file in cpu_files:
            monitor.add_watch(cpu_file, callback)
        
        try:
            monitor.start()
            # 保持主线程运行
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\n停止监控...")
        finally:
            monitor.stop()
    
    @staticmethod
    def monitor_network_stats():
        """监控网络统计"""
        monitor = SysfsMonitor()
        
        # 查找所有网络接口的统计文件
        net_pattern = "/sys/class/net/*/statistics/rx_packets"
        net_files = []
        
        for net_file in glob.glob(net_pattern):
            net_files.append(net_file)
        
        if not net_files:
            print("未找到网络统计文件")
            return
        
        print(f"监控 {len(net_files)} 个网络接口的包统计...")
        print("按 Ctrl+C 停止")
        print("-" * 50)
        
        last_rx = defaultdict(int)
        last_tx = defaultdict(int)
        last_time = time.time()
        
        def callback(path, content):
            nonlocal last_time
            
            interface = path.split('/')[4]  # 提取接口名
            stat_type = path.split('/')[-1]  # rx_packets 或 tx_packets
            
            try:
                current_value = int(content)
                current_time = time.time()
                time_diff = current_time - last_time
                
                if time_diff >= 1.0:  # 每秒计算一次速率
                    if stat_type == "rx_packets":
                        if interface in last_rx:
                            rate = (current_value - last_rx[interface]) / time_diff
                            timestamp = datetime.now().strftime("%H:%M:%S")
                            print(f"{timestamp} {interface}: RX {rate:.1f} packets/s")
                        last_rx[interface] = current_value
                    
                    elif stat_type == "tx_packets":
                        if interface in last_tx:
                            rate = (current_value - last_tx[interface]) / time_diff
                            timestamp = datetime.now().strftime("%H:%M:%S")
                            print(f"{timestamp} {interface}: TX {rate:.1f} packets/s")
                        last_tx[interface] = current_value
                    
                    last_time = current_time
            except ValueError:
                pass
        
        # 添加接收和发送包的监视
        for net_file in net_files:
            monitor.add_watch(net_file, callback)
            
            # 同时添加对应的tx_packets文件
            tx_file = net_file.replace("rx_packets", "tx_packets")
            if os.path.exists(tx_file):
                monitor.add_watch(tx_file, callback)
        
        try:
            monitor.start()
            # 保持主线程运行
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\n停止监控...")
        finally:
            monitor.stop()
    
    @staticmethod
    def monitor_temperature():
        """监控温度传感器"""
        monitor = SysfsMonitor()
        
        # 查找所有温度传感器文件
        temp_pattern = "/sys/class/hwmon/hwmon*/temp*_input"
        temp_files = []
        
        for temp_file in glob.glob(temp_pattern):
            temp_files.append(temp_file)
        
        if not temp_files:
            print("未找到温度传感器文件")
            return
        
        print(f"监控 {len(temp_files)} 个温度传感器...")
        print("按 Ctrl+C 停止")
        print("-" * 50)
        
        sensor_labels = {}
        
        # 尝试获取传感器标签
        for temp_file in temp_files:
            temp_dir = os.path.dirname(temp_file)
            temp_base = os.path.basename(temp_file).replace("_input", "")
            
            # 尝试读取标签文件
            label_file = os.path.join(temp_dir, f"{temp_base}_label")
            if os.path.exists(label_file):
                try:
                    with open(label_file, 'r') as f:
                        label = f.read().strip()
                    sensor_labels[temp_file] = label
                except:
                    sensor_labels[temp_file] = temp_base
        
        last_values = {}
        
        def callback(path, content):
            try:
                temp_c = int(content) / 1000.0
                
                # 只显示变化的值(变化超过0.1°C)
                if path not in last_values or abs(temp_c - last_values[path]) >= 0.1:
                    label = sensor_labels.get(path, os.path.basename(path))
                    timestamp = datetime.now().strftime("%H:%M:%S")
                    print(f"{timestamp} {label}: {temp_c:.1f}°C")
                    last_values[path] = temp_c
            except ValueError:
                pass
        
        # 添加所有温度传感器的监视
        for temp_file in temp_files:
            monitor.add_watch(temp_file, callback)
        
        try:
            monitor.start()
            # 保持主线程运行
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\n停止监控...")
        finally:
            monitor.stop()

# 命令行接口
if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="sysfs实时监控工具")
    parser.add_argument("target", choices=["cpu", "network", "temp", "all"],
                       help="监控目标")
    parser.add_argument("--interval", type=float, default=1.0,
                       help="更新间隔(秒)")
    
    args = parser.parse_args()
    
    monitor = SysfsMonitor()
    
    if args.target == "cpu":
        SysfsMonitor.monitor_cpu_frequency()
    elif args.target == "network":
        SysfsMonitor.monitor_network_stats()
    elif args.target == "temp":
        SysfsMonitor.monitor_temperature()
    elif args.target == "all":
        print("同时监控多个目标需要多线程实现")
        print("请分别运行不同的监控目标")

5.3 Bash脚本操作sysfs

5.3.1 sysfs信息收集脚本
bash 复制代码
#!/bin/bash
# sysfs_info_collector.sh - 收集sysfs系统信息

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
}

log_debug() {
    if [ "$DEBUG" = "true" ]; then
        echo -e "${BLUE}[DEBUG]${NC} $1"
    fi
}

# 收集CPU信息
collect_cpu_info() {
    echo -e "${CYAN}=== CPU 信息 ===${NC}"
    
    # CPU数量
    cpu_count=$(ls -d /sys/devices/system/cpu/cpu[0-9]* 2>/dev/null | wc -l)
    echo "CPU核心数: $cpu_count"
    
    # 每CPU信息
    for cpu in /sys/devices/system/cpu/cpu[0-9]*; do
        cpu_id=$(basename $cpu)
        echo -e "\n${MAGENTA}$cpu_id:${NC}"
        
        # 在线状态
        if [ -f "$cpu/online" ]; then
            online=$(cat "$cpu/online")
            echo "  在线: $([ "$online" = "1" ] && echo "是" || echo "否")"
        fi
        
        # 频率信息
        if [ -d "$cpu/cpufreq" ]; then
            if [ -f "$cpu/cpufreq/scaling_cur_freq" ]; then
                cur_freq=$(cat "$cpu/cpufreq/scaling_cur_freq")
                cur_freq_mhz=$(echo "scale=2; $cur_freq / 1000" | bc)
                echo "  当前频率: ${cur_freq_mhz} MHz"
            fi
            
            if [ -f "$cpu/cpufreq/scaling_governor" ]; then
                governor=$(cat "$cpu/cpufreq/scaling_governor")
                echo "  调控器: $governor"
            fi
        fi
        
        # 拓扑信息
        if [ -d "$cpu/topology" ]; then
            if [ -f "$cpu/topology/core_id" ]; then
                core_id=$(cat "$cpu/topology/core_id")
                echo "  核心ID: $core_id"
            fi
            
            if [ -f "$cpu/topology/physical_package_id" ]; then
                package_id=$(cat "$cpu/topology/physical_package_id")
                echo "  物理包ID: $package_id"
            fi
        fi
    done
}

# 收集内存信息
collect_memory_info() {
    echo -e "\n${CYAN}=== 内存 信息 ===${NC}"
    
    # 内存设备
    mem_blocks=$(find /sys/devices/system/memory -name "memory*" -type d | wc -l)
    echo "内存块数: $mem_blocks"
    
    # 检查每个内存块的状态
    online_count=0
    offline_count=0
    
    for mem_block in /sys/devices/system/memory/memory*; do
        if [ -f "$mem_block/state" ]; then
            state=$(cat "$mem_block/state")
            if [ "$state" = "online" ]; then
                online_count=$((online_count + 1))
            else
                offline_count=$((offline_count + 1))
            fi
        fi
    done
    
    echo "在线内存块: $online_count"
    echo "离线内存块: $offline_count"
}

# 收集PCI设备信息
collect_pci_info() {
    echo -e "\n${CYAN}=== PCI 设备 信息 ===${NC}"
    
    pci_count=$(ls -d /sys/bus/pci/devices/* 2>/dev/null | wc -l)
    echo "PCI设备数: $pci_count"
    
    # 显示一些关键PCI设备
    echo -e "\n关键PCI设备:"
    printf "%-20s %-12s %-30s %-15s\n" "设备" "厂商" "设备名称" "驱动"
    printf "%-20s %-12s %-30s %-15s\n" "------" "------" "----------" "------"
    
    for pci_dev in /sys/bus/pci/devices/*; do
        dev_id=$(basename $pci_dev)
        
        # 获取厂商和设备ID
        vendor=$(cat "$pci_dev/vendor" 2>/dev/null | sed 's/^0x//')
        device=$(cat "$pci_dev/device" 2>/dev/null | sed 's/^0x//')
        
        # 获取设备类
        class=$(cat "$pci_dev/class" 2>/dev/null | cut -c3-6)
        
        # 根据类筛选关键设备
        case $class in
            0300) # 显示设备
                dev_type="VGA"
                ;;
            0200) # 网络设备
                dev_type="Network"
                ;;
            0106) # SATA控制器
                dev_type="SATA"
                ;;
            0c03) # USB控制器
                dev_type="USB"
                ;;
            0403) # 音频设备
                dev_type="Audio"
                ;;
            *)
                continue
                ;;
        esac
        
        # 获取设备名称
        if command -v lspci >/dev/null 2>&1; then
            dev_name=$(lspci -d "$vendor:$device" 2>/dev/null | cut -d':' -f3- | head -1)
            dev_name=${dev_name:0:30}
        else
            dev_name="未知"
        fi
        
        # 获取驱动
        if [ -L "$pci_dev/driver" ]; then
            driver=$(basename $(readlink "$pci_dev/driver"))
        else
            driver="无"
        fi
        
        printf "%-20s %-12s %-30s %-15s\n" "$dev_id" "$vendor:$device" "$dev_name" "$driver"
    done | head -10
}

# 收集USB设备信息
collect_usb_info() {
    echo -e "\n${CYAN}=== USB 设备 信息 ===${NC}"
    
    usb_count=$(find /sys/bus/usb/devices/usb* -maxdepth 0 -type d 2>/dev/null | wc -l)
    echo "USB控制器数: $usb_count"
    
    # 显示USB设备树
    echo -e "\nUSB设备树:"
    for usb_dev in /sys/bus/usb/devices/usb*; do
        if [ ! -d "$usb_dev" ]; then
            continue
        fi
        
        dev_id=$(basename $usb_dev)
        
        # 获取USB版本
        version=$(cat "$usb_dev/version" 2>/dev/null || echo "未知")
        speed=$(cat "$usb_dev/speed" 2>/dev/null || echo "未知")
        
        echo "  $dev_id: USB $version, 速度: $speed"
        
        # 查找连接的设备
        for child in "$usb_dev"/*; do
            if [ -d "$child" ] && [[ $(basename $child) =~ ^[0-9]+-[0-9]+ ]]; then
                child_id=$(basename $child)
                
                # 尝试获取产品信息
                if [ -f "$child/product" ]; then
                    product=$(cat "$child/product")
                    echo "    └── $child_id: $product"
                else
                    echo "    └── $child_id"
                fi
            fi
        done
    done
}

# 收集网络设备信息
collect_network_info() {
    echo -e "\n${CYAN}=== 网络 设备 信息 ===${NC}"
    
    net_count=$(ls -d /sys/class/net/* 2>/dev/null | grep -v lo | wc -l)
    echo "网络接口数: $net_count"
    
    for iface in /sys/class/net/*; do
        iface_name=$(basename $iface)
        
        # 跳过回环接口
        if [ "$iface_name" = "lo" ]; then
            continue
        fi
        
        echo -e "\n${MAGENTA}接口: $iface_name${NC}"
        
        # 基本信息
        if [ -f "$iface/address" ]; then
            mac=$(cat "$iface/address")
            echo "  MAC地址: $mac"
        fi
        
        if [ -f "$iface/operstate" ]; then
            state=$(cat "$iface/operstate")
            echo "  状态: $state"
        fi
        
        if [ -f "$iface/mtu" ]; then
            mtu=$(cat "$iface/mtu")
            echo "  MTU: $mtu"
        fi
        
        # 速度和双工
        if [ -f "$iface/speed" ]; then
            speed=$(cat "$iface/speed")
            echo "  速度: $speed Mbps"
        fi
        
        if [ -f "$iface/duplex" ]; then
            duplex=$(cat "$iface/duplex")
            echo "  双工: $duplex"
        fi
        
        # 驱动信息
        if [ -L "$iface/device/driver" ]; then
            driver=$(basename $(readlink "$iface/device/driver"))
            echo "  驱动: $driver"
        fi
    done
}

# 收集块设备信息
collect_block_info() {
    echo -e "\n${CYAN}=== 块 设备 信息 ===${NC}"
    
    # 物理设备(排除分区和loop设备)
    phys_count=0
    
    for block in /sys/class/block/*; do
        dev_name=$(basename $block)
        
        # 跳过分区和loop设备
        if [[ $dev_name =~ ^(loop|ram|dm-) ]] || [[ $dev_name =~ [0-9]$ ]]; then
            continue
        fi
        
        phys_count=$((phys_count + 1))
        
        echo -e "\n${MAGENTA}设备: $dev_name${NC}"
        
        # 设备大小
        if [ -f "$block/size" ]; then
            size_sectors=$(cat "$block/size")
            size_gb=$(echo "scale=2; $size_sectors * 512 / 1000000000" | bc)
            echo "  大小: $size_gb GB"
        fi
        
        # 可移动性
        if [ -f "$block/removable" ]; then
            removable=$(cat "$block/removable")
            echo "  可移动: $([ "$removable" = "1" ] && echo "是" || echo "否")"
        fi
        
        # 只读
        if [ -f "$block/ro" ]; then
            ro=$(cat "$block/ro")
            echo "  只读: $([ "$ro" = "1" ] && echo "是" || echo "否")"
        fi
        
        # 调度器
        if [ -f "$block/queue/scheduler" ]; then
            scheduler=$(cat "$block/queue/scheduler")
            echo "  调度器: $scheduler"
        fi
        
        # 分区信息
        partition_count=0
        for partition in "$block"/$dev_name[0-9]*; do
            if [ -d "$partition" ]; then
                partition_count=$((partition_count + 1))
            fi
        done
        
        if [ $partition_count -gt 0 ]; then
            echo "  分区数: $partition_count"
        fi
    done
    
    echo -e "\n物理块设备总数: $phys_count"
}

# 收集温度传感器信息
collect_temperature_info() {
    echo -e "\n${CYAN}=== 温度 传感器 信息 ===${NC}"
    
    hwmon_count=$(ls -d /sys/class/hwmon/hwmon* 2>/dev/null | wc -l)
    
    if [ $hwmon_count -eq 0 ]; then
        echo "未找到温度传感器"
        return
    fi
    
    echo "硬件监控器数: $hwmon_count"
    
    for hwmon in /sys/class/hwmon/hwmon*; do
        hwmon_name=$(cat "$hwmon/name" 2>/dev/null || basename $hwmon)
        echo -e "\n${MAGENTA}传感器: $hwmon_name${NC}"
        
        # 查找温度输入
        temp_count=0
        for temp_input in "$hwmon"/temp*_input; do
            if [ -f "$temp_input" ]; then
                temp_count=$((temp_count + 1))
                
                temp_base=$(basename "$temp_input" _input)
                temp_value=$(cat "$temp_input")
                temp_c=$(echo "scale=1; $temp_value / 1000" | bc)
                
                # 尝试获取标签
                temp_label=""
                if [ -f "$hwmon/${temp_base}_label" ]; then
                    temp_label=$(cat "$hwmon/${temp_base}_label")
                fi
                
                # 获取临界温度
                temp_crit=""
                if [ -f "$hwmon/${temp_base}_crit" ]; then
                    temp_crit_value=$(cat "$hwmon/${temp_base}_crit")
                    temp_crit=$(echo "scale=1; $temp_crit_value / 1000" | bc)
                fi
                
                if [ -n "$temp_label" ]; then
                    echo "  $temp_label: ${temp_c}°C$([ -n "$temp_crit" ] && echo " (临界: ${temp_crit}°C)" || echo "")"
                else
                    echo "  $temp_base: ${temp_c}°C$([ -n "$temp_crit" ] && echo " (临界: ${temp_crit}°C)" || echo "")"
                fi
            fi
        done
        
        if [ $temp_count -eq 0 ]; then
            echo "  无温度传感器"
        fi
    done
}

# 收集电源信息
collect_power_info() {
    echo -e "\n${CYAN}=== 电源 信息 ===${NC}"
    
    # 检查AC适配器
    ac_found=false
    for ac in /sys/class/power_supply/AC*; do
        if [ -d "$ac" ]; then
            ac_found=true
            ac_name=$(basename $ac)
            ac_online=$(cat "$ac/online" 2>/dev/null || echo "未知")
            
            echo "AC适配器 ($ac_name): $([ "$ac_online" = "1" ] && echo "已连接" || echo "未连接")"
        fi
    done
    
    if [ "$ac_found" = false ]; then
        echo "AC适配器: 未找到"
    fi
    
    # 检查电池
    battery_count=0
    for battery in /sys/class/power_supply/BAT*; do
        if [ -d "$battery" ]; then
            battery_count=$((battery_count + 1))
            battery_name=$(basename $battery)
            
            echo -e "\n${MAGENTA}电池: $battery_name${NC}"
            
            # 电池状态
            if [ -f "$battery/status" ]; then
                status=$(cat "$battery/status")
                echo "  状态: $status"
            fi
            
            # 电池容量
            if [ -f "$battery/capacity" ]; then
                capacity=$(cat "$battery/capacity")
                echo "  容量: $capacity%"
            fi
            
            # 电池健康
            if [ -f "$battery/health" ]; then
                health=$(cat "$battery/health")
                echo "  健康: $health"
            fi
            
            # 电池技术
            if [ -f "$battery/technology" ]; then
                technology=$(cat "$battery/technology")
                echo "  技术: $technology"
            fi
        fi
    done
    
    if [ $battery_count -eq 0 ]; then
        echo "电池: 未找到"
    fi
}

# 收集模块信息
collect_module_info() {
    echo -e "\n${CYAN}=== 内核 模块 信息 ===${NC}"
    
    module_count=$(ls -d /sys/module/* 2>/dev/null | wc -l)
    echo "已加载模块数: $module_count"
    
    # 显示一些关键模块
    echo -e "\n关键模块:"
    printf "%-25s %-15s %-10s\n" "模块名" "内存使用" "引用计数"
    printf "%-25s %-15s %-10s\n" "------" "--------" "--------"
    
    for module in /sys/module/*; do
        module_name=$(basename $module)
        
        # 跳过自动加载的模块
        if [[ $module_name =~ ^(usbcore|pci|acpi) ]]; then
            continue
        fi
        
        # 获取内存使用(如果可用)
        memory="N/A"
        if [ -f "/sys/module/$module_name/sections/.text" ]; then
            text_size=$(stat -c%s "/sys/module/$module_name/sections/.text" 2>/dev/null || echo 0)
            data_size=$(stat -c%s "/sys/module/$module_name/sections/.data" 2>/dev/null || echo 0)
            bss_size=$(stat -c%s "/sys/module/$module_name/sections/.bss" 2>/dev/null || echo 0)
            
            total_size=$((text_size + data_size + bss_size))
            if [ $total_size -gt 0 ]; then
                memory=$(numfmt --to=iec $total_size)
            fi
        fi
        
        # 获取引用计数
        refcnt=$(cat "$module/refcnt" 2>/dev/null || echo "0")
        
        printf "%-25s %-15s %-10s\n" "$module_name" "$memory" "$refcnt"
    done | head -15
}

# 主函数
main() {
    echo -e "${BLUE}=========================================${NC}"
    echo -e "${BLUE}        Sysfs 系统信息收集工具          ${NC}"
    echo -e "${BLUE}=========================================${NC}"
    echo ""
    
    # 收集开始时间
    start_time=$(date +%s)
    
    # 收集各种信息
    collect_cpu_info
    collect_memory_info
    collect_pci_info
    collect_usb_info
    collect_network_info
    collect_block_info
    collect_temperature_info
    collect_power_info
    collect_module_info
    
    # 收集结束时间
    end_time=$(date +%s)
    duration=$((end_time - start_time))
    
    echo -e "\n${GREEN}信息收集完成,耗时 ${duration} 秒${NC}"
}

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case $1 in
        --debug)
            DEBUG="true"
            shift
            ;;
        --help|-h)
            echo "用法: $0 [选项]"
            echo "选项:"
            echo "  --debug     启用调试输出"
            echo "  --help, -h  显示此帮助信息"
            exit 0
            ;;
        *)
            log_error "未知选项: $1"
            exit 1
            ;;
    esac
done

# 检查root权限
if [[ $EUID -ne 0 ]]; then
    log_warning "某些信息可能需要root权限才能访问"
    read -p "继续吗?(y/n): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

# 运行主函数
main

由于篇幅限制,我将继续完成Linux Sysfs文件系统的详解,重点放在高级主题、最佳实践和实际应用案例上。


第六部分:sysfs高级主题与最佳实践

6.1 sysfs与udev的协同工作

6.1.1 udev规则中的sysfs属性匹配
bash 复制代码
# 示例:为特定USB设备创建符号链接
# /etc/udev/rules.d/99-usb-camera.rules

# 通过厂商ID和产品ID匹配
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0825", SYMLINK+="webcam_logitech"

# 通过sysfs路径匹配
SUBSYSTEM=="block", KERNEL=="sd[a-z]", ENV{DEVTYPE}=="disk", PROGRAM="/usr/bin/readlink -f /sys/class/block/%k/device", RESULT=="*/usb[0-9]/*", SYMLINK+="usbdisk_%n"

# 通过设备属性匹配
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:11:22:33:44:55", NAME="lan0"

# 通过设备序列号匹配
SUBSYSTEM=="tty", ATTRS{serial}=="1234567890", SYMLINK+="tty_myserial"

6.1.2 动态设备权限管理
bash 复制代码
# 示例:为特定设备设置权限
# /etc/udev/rules.d/99-custom-permissions.rules

# USB串口设备权限
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", GROUP="dialout"

# 视频设备权限
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0825", MODE="0666", GROUP="video"

# 存储设备自动挂载(简化示例)
SUBSYSTEM=="block", ENV{DEVTYPE}=="partition", ACTION=="add", RUN+="/usr/bin/systemd-mount --no-block --automount=yes /dev/%k"

6.2 sysfs安全考虑

6.2.1 权限控制

sysfs文件系统的挂载选项提供了基本的安全控制:

bash 复制代码
# 查看sysfs挂载选项
$ mount | grep sysfs
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)

# 挂载选项说明:
# nosuid: 禁止setuid程序
# nodev: 禁止设备文件
# noexec: 禁止执行程序
# relatime: 使用相对访问时间

6.2.2 敏感信息保护

某些sysfs文件包含敏感信息,需要适当权限:

bash 复制代码
# 查看敏感文件的权限
$ ls -l /sys/kernel/debug/
drwx------ 2 root root 0 May 15 11:00 acpi
drwx------ 2 root root 0 May 15 11:00 bdi
# ...

# 查看模块参数的权限
$ ls -l /sys/module/kvm/parameters/
-rw------- 1 root root 4096 May 15 11:00 nx_huge_pages
-rw------- 1 root root 4096 May 15 11:00 ept
# ...

6.3 sysfs性能调优

6.3.1 减少sysfs访问频率

频繁访问sysfs可能影响性能,特别是在嵌入式系统中:

bash 复制代码
#!/bin/bash
# sysfs访问优化示例

# 1. 缓存频繁访问的值
cache_sysfs_value() {
    local cache_file="/tmp/sysfs_$1.cache"
    local max_age=5  # 缓存5秒
    
    if [ -f "$cache_file" ]; then
        local cache_time=$(stat -c %Y "$cache_file")
        local current_time=$(date +%s)
        local age=$((current_time - cache_time))
        
        if [ $age -lt $max_age ]; then
            cat "$cache_file"
            return 0
        fi
    fi
    
    # 读取新值并缓存
    local value
    value=$(cat "$1" 2>/dev/null)
    if [ $? -eq 0 ]; then
        echo "$value" > "$cache_file"
        echo "$value"
        return 0
    else
        return 1
    fi
}

# 使用缓存的示例
cpu_freq=$(cache_sysfs_value "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")

# 2. 批量读取相关属性
batch_read_sysfs() {
    local base_path=$1
    shift
    local attrs=("$@")
    
    for attr in "${attrs[@]}"; do
        if [ -f "$base_path/$attr" ]; then
            echo "$attr: $(cat "$base_path/$attr" 2>/dev/null)"
        fi
    done
}

# 批量读取网络接口属性
batch_read_sysfs "/sys/class/net/eth0" \
    "address" "mtu" "operstate" "speed" "duplex"

6.3.2 异步sysfs监控

对于需要实时监控的场景,使用inotify机制:

bash 复制代码
/*
 * sysfs_inotify.c - 使用inotify监控sysfs文件变化
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <limits.h>

#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))

int main(int argc, char *argv[]) {
    int inotify_fd;
    int watch_fd;
    char buffer[BUF_LEN];
    ssize_t num_read;
    struct inotify_event *event;
    
    if (argc < 2) {
        fprintf(stderr, "用法: %s <sysfs文件路径>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    
    // 创建inotify实例
    inotify_fd = inotify_init();
    if (inotify_fd == -1) {
        perror("inotify_init");
        exit(EXIT_FAILURE);
    }
    
    // 添加监视
    watch_fd = inotify_add_watch(inotify_fd, argv[1],
                                 IN_MODIFY | IN_CLOSE_WRITE);
    if (watch_fd == -1) {
        perror("inotify_add_watch");
        exit(EXIT_FAILURE);
    }
    
    printf("监控文件: %s\n", argv[1]);
    printf("按Ctrl+C停止监控\n\n");
    
    // 事件循环
    while (1) {
        num_read = read(inotify_fd, buffer, BUF_LEN);
        if (num_read == 0) {
            fprintf(stderr, "read()返回0\n");
            break;
        }
        
        if (num_read == -1) {
            perror("read");
            break;
        }
        
        // 处理事件
        for (char *ptr = buffer; ptr < buffer + num_read; ) {
            event = (struct inotify_event *)ptr;
            
            if (event->mask & IN_MODIFY) {
                printf("文件被修改: %s\n", argv[1]);
                
                // 读取文件内容
                FILE *fp = fopen(argv[1], "r");
                if (fp) {
                    char content[256];
                    if (fgets(content, sizeof(content), fp)) {
                        printf("新内容: %s", content);
                    }
                    fclose(fp);
                }
                printf("\n");
            }
            
            if (event->mask & IN_CLOSE_WRITE) {
                printf("写入完成: %s\n", argv[1]);
            }
            
            ptr += sizeof(struct inotify_event) + event->len;
        }
    }
    
    // 清理
    close(inotify_fd);
    
    return 0;
}

6.3.3 使用mmap优化大文件访问

对于较大的sysfs文件(如/sys/kernel/debug/tracing/trace_pipe),可以使用mmap进行高效访问:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>

int mmap_sysfs_file(const char *path) {
    int fd;
    struct stat st;
    void *map;
    
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    
    if (fstat(fd, &st) < 0) {
        perror("fstat");
        close(fd);
        return -1;
    }
    
    // 映射文件到内存
    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }
    
    // 使用映射的内存
    printf("File content (first 100 bytes):\n");
    fwrite(map, 1, st.st_size < 100 ? st.st_size : 100, stdout);
    printf("\n");
    
    // 清理
    munmap(map, st.st_size);
    close(fd);
    
    return 0;
}

6.3.4 异步I/O与epoll

对于需要监控多个sysfs文件的场景,可以使用epoll实现高效的异步I/O:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>

#define MAX_EVENTS 10

struct sysfs_monitor {
    int epoll_fd;
    struct monitored_file {
        int fd;
        char path[256];
        void (*callback)(const char *path, const char *content);
    } files[MAX_EVENTS];
    int file_count;
};

int sysfs_monitor_init(struct sysfs_monitor *monitor) {
    monitor->epoll_fd = epoll_create1(0);
    if (monitor->epoll_fd < 0) {
        perror("epoll_create1");
        return -1;
    }
    monitor->file_count = 0;
    return 0;
}

int sysfs_monitor_add(struct sysfs_monitor *monitor, 
                      const char *path,
                      void (*callback)(const char *, const char *)) {
    if (monitor->file_count >= MAX_EVENTS) {
        fprintf(stderr, "Too many monitored files\n");
        return -1;
    }
    
    int fd = open(path, O_RDONLY | O_NONBLOCK);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET; // 边缘触发模式
    ev.data.fd = fd;
    
    if (epoll_ctl(monitor->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        perror("epoll_ctl");
        close(fd);
        return -1;
    }
    
    struct monitored_file *file = &monitor->files[monitor->file_count];
    file->fd = fd;
    strncpy(file->path, path, sizeof(file->path) - 1);
    file->callback = callback;
    monitor->file_count++;
    
    return 0;
}

void sysfs_monitor_run(struct sysfs_monitor *monitor) {
    struct epoll_event events[MAX_EVENTS];
    char buffer[4096];
    
    printf("Starting sysfs monitor...\n");
    
    while (1) {
        int nfds = epoll_wait(monitor->epoll_fd, events, MAX_EVENTS, -1);
        if (nfds < 0) {
            if (errno == EINTR) continue;
            perror("epoll_wait");
            break;
        }
        
        for (int i = 0; i < nfds; i++) {
            int fd = events[i].data.fd;
            
            // 查找对应的文件
            struct monitored_file *file = NULL;
            for (int j = 0; j < monitor->file_count; j++) {
                if (monitor->files[j].fd == fd) {
                    file = &monitor->files[j];
                    break;
                }
            }
            
            if (!file) continue;
            
            // 读取文件内容
            ssize_t count;
            while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
                buffer[count] = '\0';
                if (file->callback) {
                    file->callback(file->path, buffer);
                }
            }
            
            // 重置文件指针(对于某些sysfs文件需要)
            lseek(fd, 0, SEEK_SET);
        }
    }
}

void sysfs_monitor_cleanup(struct sysfs_monitor *monitor) {
    for (int i = 0; i < monitor->file_count; i++) {
        close(monitor->files[i].fd);
    }
    close(monitor->epoll_fd);
}

// 示例回调函数
void print_file_change(const char *path, const char *content) {
    printf("[%s] Changed: %s\n", path, content);
}

int main() {
    struct sysfs_monitor monitor;
    
    if (sysfs_monitor_init(&monitor) < 0) {
        return 1;
    }
    
    // 监控CPU频率和温度
    sysfs_monitor_add(&monitor, 
                     "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
                     print_file_change);
    
    sysfs_monitor_add(&monitor,
                     "/sys/class/hwmon/hwmon0/temp1_input",
                     print_file_change);
    
    sysfs_monitor_run(&monitor);
    sysfs_monitor_cleanup(&monitor);
    
    return 0;
}

第七部分:sysfs故障排查与调试

7.1 常见问题排查

7.1.1 sysfs文件不存在
bash 复制代码
#!/bin/bash
# sysfs_troubleshoot.sh - sysfs故障排查工具

echo "Sysfs故障排查工具"
echo "=================="

check_sysfs_mount() {
    echo "1. 检查sysfs挂载状态"
    if mount | grep -q "sysfs on /sys"; then
        echo "   ✅ sysfs已挂载"
        
        # 检查挂载选项
        mount_options=$(mount | grep "sysfs on /sys" | grep -o "rw.*" | cut -d' ' -f1)
        echo "   挂载选项: $mount_options"
        
        # 检查是否只读
        if echo "$mount_options" | grep -q "ro"; then
            echo "   ⚠️  sysfs以只读模式挂载"
        fi
    else
        echo "   ❌ sysfs未挂载"
        echo "   尝试挂载: sudo mount -t sysfs sysfs /sys"
        return 1
    fi
    echo ""
}

check_sysfs_structure() {
    echo "2. 检查sysfs目录结构"
    
    required_dirs=(
        "/sys/devices"
        "/sys/bus"
        "/sys/class"
        "/sys/block"
        "/sys/module"
        "/sys/kernel"
        "/sys/power"
        "/sys/firmware"
        "/sys/fs"
    )
    
    missing_dirs=()
    for dir in "${required_dirs[@]}"; do
        if [ ! -d "$dir" ]; then
            missing_dirs+=("$dir")
        fi
    done
    
    if [ ${#missing_dirs[@]} -eq 0 ]; then
        echo "   ✅ sysfs目录结构完整"
    else
        echo "   ❌ 缺失以下目录:"
        for dir in "${missing_dirs[@]}"; do
            echo "      $dir"
        done
        return 1
    fi
    echo ""
}

check_kernel_config() {
    echo "3. 检查内核配置"
    
    # 检查/proc/config.gz或/boot/config
    if [ -f "/proc/config.gz" ]; then
        echo "   从/proc/config.gz读取配置"
        config_source="/proc/config.gz"
        cmd="zcat"
    elif [ -f "/boot/config-$(uname -r)" ]; then
        echo "   从/boot读取配置"
        config_source="/boot/config-$(uname -r)"
        cmd="cat"
    else
        echo "   ⚠️  无法找到内核配置文件"
        return 0
    fi
    
    # 检查重要的sysfs相关配置
    important_configs=(
        "CONFIG_SYSFS"
        "CONFIG_DEBUG_FS"
        "CONFIG_KALLSYMS"
        "CONFIG_HOTPLUG"
        "CONFIG_UEVENT_HELPER"
        "CONFIG_DEVTMPFS"
    )
    
    echo "   重要配置状态:"
    for config in "${important_configs[@]}"; do
        if $cmd "$config_source" 2>/dev/null | grep -q "^$config=y"; then
            echo "      ✅ $config: 已启用"
        elif $cmd "$config_source" 2>/dev/null | grep -q "^$config=m"; then
            echo "      ⚠️  $config: 模块形式"
        elif $cmd "$config_source" 2>/dev/null | grep -q "^$config=n"; then
            echo "      ❌ $config: 未启用"
        else
            echo "      ?  $config: 未找到"
        fi
    done
    echo ""
}

check_device_visibility() {
    echo "4. 检查设备可见性"
    
    echo "   4.1 CPU设备:"
    cpu_count=$(ls -d /sys/devices/system/cpu/cpu[0-9]* 2>/dev/null | wc -l)
    if [ $cpu_count -gt 0 ]; then
        echo "      ✅ 找到 $cpu_count 个CPU设备"
    else
        echo "      ❌ 未找到CPU设备"
    fi
    
    echo "   4.2 网络设备:"
    net_count=$(ls -d /sys/class/net/* 2>/dev/null | grep -v lo | wc -l)
    if [ $net_count -gt 0 ]; then
        echo "      ✅ 找到 $net_count 个网络设备"
    else
        echo "      ❌ 未找到网络设备"
    fi
    
    echo "   4.3 块设备:"
    block_count=$(ls -d /sys/class/block/* 2>/dev/null | grep -v loop | grep -v ram | wc -l)
    if [ $block_count -gt 0 ]; then
        echo "      ✅ 找到 $block_count 个块设备"
    else
        echo "      ❌ 未找到块设备"
    fi
    
    echo "   4.4 USB设备:"
    usb_controllers=$(ls -d /sys/bus/usb/devices/usb* 2>/dev/null | wc -l)
    if [ $usb_controllers -gt 0 ]; then
        echo "      ✅ 找到 $usb_controllers 个USB控制器"
    else
        echo "      ❌ 未找到USB控制器"
    fi
    echo ""
}

check_permissions() {
    echo "5. 检查权限问题"
    
    problematic_files=()
    
    # 检查关键文件的权限
    important_files=(
        "/sys/class/net/eth0/address"
        "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
        "/sys/class/backlight/intel_backlight/brightness"
        "/sys/class/hwmon/hwmon0/temp1_input"
    )
    
    for file in "${important_files[@]}"; do
        if [ -e "$file" ]; then
            permissions=$(stat -c "%A %U %G" "$file" 2>/dev/null)
            if [ $? -eq 0 ]; then
                # 检查是否可读
                if [ ! -r "$file" ]; then
                    problematic_files+=("$file (不可读)")
                fi
                echo "     $file: $permissions"
            fi
        fi
    done
    
    if [ ${#problematic_files[@]} -gt 0 ]; then
        echo "   ⚠️  权限问题:"
        for file in "${problematic_files[@]}"; do
            echo "     $file"
        done
    else
        echo "   ✅ 关键文件权限正常"
    fi
    echo ""
}

check_uevent() {
    echo "6. 检查uevent机制"
    
    # 测试uevent触发
    test_device="/sys/devices/virtual/net/lo"
    
    if [ -d "$test_device" ]; then
        echo "   测试uevent触发..."
        
        # 监听uevent
        timeout 2 udevadm monitor --property &> /tmp/udev_test.log &
        monitor_pid=$!
        
        # 触发uevent
        sleep 0.5
        echo "change" > "$test_device/uevent" 2>/dev/null
        
        wait $monitor_pid 2>/dev/null
        
        if grep -q "change" /tmp/udev_test.log; then
            echo "   ✅ uevent机制正常"
        else
            echo "   ⚠️  uevent触发可能有问题"
        fi
        
        rm -f /tmp/udev_test.log
    else
        echo "   ⚠️  无法测试uevent(测试设备不存在)"
    fi
    echo ""
}

collect_debug_info() {
    echo "7. 收集调试信息"
    
    debug_dir="/tmp/sysfs_debug_$(date +%Y%m%d_%H%M%S)"
    mkdir -p "$debug_dir"
    
    echo "   收集信息到: $debug_dir"
    
    # 系统信息
    uname -a > "$debug_dir/uname.txt"
    cat /proc/version > "$debug_dir/version.txt"
    
    # 内核模块
    lsmod > "$debug_dir/lsmod.txt"
    
    # sysfs挂载信息
    mount | grep sysfs > "$debug_dir/sysfs_mount.txt"
    
    # dmesg日志
    dmesg | tail -100 > "$debug_dir/dmesg.txt"
    
    # udev信息
    udevadm info --version > "$debug_dir/udev_version.txt"
    udevadm control --ping 2>&1 > "$debug_dir/udev_status.txt"
    
    # 重要sysfs目录列表
    find /sys -maxdepth 2 -type d | sort > "$debug_dir/sysfs_dirs.txt" 2>/dev/null
    
    echo "   调试信息已保存"
    echo ""
}

main() {
    echo "开始sysfs故障排查..."
    echo ""
    
    check_sysfs_mount
    check_sysfs_structure
    check_kernel_config
    check_device_visibility
    check_permissions
    check_uevent
    collect_debug_info
    
    echo "故障排查完成"
    echo "如果发现问题,请将调试信息提供给技术支持"
}

main

7.1.2 sysfs权限问题

权限问题通常由以下原因引起:

  1. 系统安全策略(如SELinux、AppArmor)
  2. 文件系统挂载选项
  3. 用户权限不足
bash 复制代码
# 检查SELinux状态
getenforce
sestatus

# 检查AppArmor状态
aa-status

# 检查文件系统挂载选项
mount | grep "sysfs"

# 临时关闭SELinux进行测试
sudo setenforce 0

# 检查audit日志
sudo ausearch -m avc -ts recent

7.1.3 sysfs文件内容异常
bash 复制代码
#!/bin/bash
# check_sysfs_integrity.sh

echo "检查sysfs文件完整性"
echo "=================="

# 检查文件大小异常
find /sys -type f -size +1M 2>/dev/null | while read file; do
    echo "警告: 大文件 $file ($(stat -c%s "$file") 字节)"
done

# 检查二进制文件
find /sys -type f -exec file {} \; 2>/dev/null | grep -E "ELF|executable" | head -5

# 检查损坏的符号链接
find /sys -type l ! -exec test -e {} \; -print 2>/dev/null | head -10

# 检查重复的设备
echo -e "\n检查重复的设备条目:"
ls -l /sys/class/net/ 2>/dev/null | grep -v "^total" | awk '{print $9, $11}' | sort | uniq -d

7.2 sysfs调试技巧

7.2.1 使用strace跟踪sysfs访问
bash 复制代码
# 跟踪进程的sysfs访问
strace -e openat,read,write -o /tmp/sysfs_trace.log \
  cat /sys/class/net/eth0/address

# 分析跟踪结果
grep -E "/sys/" /tmp/sysfs_trace.log | head -20

7.2.2 使用systemtap进行内核跟踪
stap 复制代码
# sysfs_access.stp - 跟踪sysfs访问
probe kernel.function("sysfs_read_file") {
    printf("PID %d reading %s\n", pid(), kernel_string($buf->name))
}

probe kernel.function("sysfs_write_file") {
    printf("PID %d writing to %s\n", pid(), kernel_string($buf->name))
}

probe begin {
    printf("开始跟踪sysfs访问...\n")
}

7.2.3 使用bpftrace进行动态跟踪
bash 复制代码
# 跟踪sysfs文件打开
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat {
    $path = str(args->filename);
    if (str($path) ~ "/sys/") {
        printf("PID %d opening %s\n", pid, $path);
    }
}'

# 跟踪sysfs读写操作
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_read,
                  tracepoint:syscalls:sys_enter_write {
    $fd = args->fd;
    $path = "";
    
    // 获取文件描述符对应的路径
    if ($fd > 0) {
        $path = str(pid, $fd);
    }
    
    if (str($path) ~ "/sys/") {
        printf("%s: PID %d %s %d bytes\n", 
               $path, pid, probe, args->count);
    }
}'

第八部分:sysfs的未来发展与替代方案

8.1 sysfs的局限性

尽管sysfs功能强大,但仍存在一些局限性:

  1. 性能问题:频繁访问sysfs可能对系统性能产生影响
  2. 并发访问:缺乏完善的并发控制机制
  3. 安全性:暴露过多系统内部信息
  4. 扩展性:难以适应新的硬件架构

8.2 sysfs的演进方向

8.2.1 configfs:配置专用文件系统

configfs用于内核对象的动态配置,与sysfs互补:

bash 复制代码
# configfs通常挂载在/config
$ ls /config/
usb_gadget  # USB gadget配置

# 使用configfs配置USB gadget
mkdir /config/usb_gadget/g1
cd /config/usb_gadget/g1

# 设置vendor和product ID
echo 0x1d6b > idVendor
echo 0x0104 > idProduct

# 创建配置
mkdir configs/c.1
mkdir functions/ecm.usb0

# 绑定功能到配置
ln -s functions/ecm.usb0 configs/c.1

8.2.2 debugfs:调试专用文件系统

debugfs专为调试目的设计,提供更灵活的接口:

bash 复制代码
# debugfs挂载点
$ mount | grep debugfs
debugfs on /sys/kernel/debug type debugfs (rw,relatime)

# 常用调试接口
$ ls /sys/kernel/debug/
asoc            bluetooth       cleancache     clk            device_component  extfrag        gpio           hid            iommu          memblock       mmc0           pinctrl        regmap         sched          sync           tracing        vfio           wakeup_sources

8.2.3 BPF和tracefs:现代跟踪方案

tracefs与BPF结合提供了更强大的系统跟踪能力:

bash 复制代码
# tracefs挂载点
$ mount | grep tracefs
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)

# 使用tracefs
$ echo 1 > /sys/kernel/tracing/events/syscalls/sys_enter_open/enable
$ cat /sys/kernel/tracing/trace_pipe

8.3 sysfs与io_uring的集成

io_uring是Linux新一代异步I/O框架,可以与sysfs结合提供更高性能的访问:

c 复制代码
#include <liburing.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#define ENTRIES 4

int main() {
    struct io_uring ring;
    struct io_uring_sqe *sqe;
    struct io_uring_cqe *cqe;
    
    // 初始化io_uring
    if (io_uring_queue_init(ENTRIES, &ring, 0) < 0) {
        perror("io_uring_queue_init");
        return 1;
    }
    
    // 打开sysfs文件
    int fd = open("/sys/class/net/eth0/address", O_RDONLY);
    if (fd < 0) {
        perror("open");
        io_uring_queue_exit(&ring);
        return 1;
    }
    
    char buffer[32];
    
    // 准备读取操作
    sqe = io_uring_get_sqe(&ring);
    io_uring_prep_read(sqe, fd, buffer, sizeof(buffer) - 1, 0);
    
    // 提交请求
    io_uring_submit(&ring);
    
    // 等待完成
    io_uring_wait_cqe(&ring, &cqe);
    
    if (cqe->res > 0) {
        buffer[cqe->res] = '\0';
        printf("MAC地址: %s", buffer);
    } else {
        printf("读取失败: %d\n", cqe->res);
    }
    
    // 完成处理
    io_uring_cqe_seen(&ring, cqe);
    
    // 清理
    close(fd);
    io_uring_queue_exit(&ring);
    
    return 0;
}

8.4 sysfs在云原生环境中的角色

在云原生和容器化环境中,sysfs的角色正在发生变化:

8.4.1 设备插件框架

Kubernetes设备插件框架利用sysfs进行设备发现和管理:

yaml 复制代码
# nvidia-device-plugin.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nvidia-device-plugin
spec:
  containers:
  - name: nvidia-device-plugin
    image: nvidia/k8s-device-plugin:v0.12.3
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]
    volumeMounts:
    - name: device-plugin
      mountPath: /var/lib/kubelet/device-plugins
    - name: sysfs
      mountPath: /sys
      readOnly: true
    - name: nvidia-mps
      mountPath: /tmp/nvidia-mps
  volumes:
  - name: device-plugin
    hostPath:
      path: /var/lib/kubelet/device-plugins
      type: DirectoryOrCreate
  - name: sysfs
    hostPath:
      path: /sys
      type: Directory
  - name: nvidia-mps
    hostPath:
      path: /tmp/nvidia-mps
      type: DirectoryOrCreate

8.4.2 虚拟化环境中的sysfs

在虚拟化环境中,sysfs展示了虚拟设备拓扑:

bash 复制代码
# 在KVM虚拟机中查看virtio设备
$ ls /sys/bus/virtio/devices/
virtio0  virtio1  virtio2

# 查看virtio-net设备
$ cat /sys/bus/virtio/devices/virtio1/device
0x1000  # Virtio network device

# 查看PCI透传设备
$ ls /sys/bus/pci/devices/0000:00:04.0/
config  device  enable  irq  resource  resource0  subsystem_device  vendor

第九部分:sysfs最佳实践总结

9.1 开发实践

  1. 错误处理:始终检查sysfs操作的返回值
  2. 资源管理:及时关闭文件描述符
  3. 并发安全:避免竞争条件
  4. 向后兼容:考虑不同内核版本的差异

9.2 部署实践

  1. 权限最小化:按需授予访问权限
  2. 监控告警:监控sysfs访问异常
  3. 安全加固:使用安全模块限制访问
  4. 性能优化:避免频繁访问关键路径

9.3 运维实践

  1. 定期检查:监控sysfs文件系统健康状态
  2. 备份配置:备份重要的sysfs配置
  3. 更新管理:跟踪内核更新对sysfs的影响
  4. 灾难恢复:准备sysfs故障恢复方案

结论

Linux sysfs文件系统作为内核与用户空间之间的桥梁,在现代Linux系统中扮演着至关重要的角色。通过本文的全面探讨,我们深入了解了:

  1. sysfs的设计哲学:基于kobject的统一设备模型
  2. 丰富的目录结构:从物理设备到逻辑分类的多维视图
  3. 强大的功能特性:设备管理、电源控制、性能监控
  4. 多样的访问方式:从命令行工具到编程接口
  5. 广泛的应用场景:从嵌入式设备到云原生环境
  6. 完整的生态系统:与udev、systemd、容器技术的集成

尽管sysfs面临着性能、安全性和扩展性方面的挑战,但它仍然是Linux设备管理的核心基础设施。随着Linux内核的持续发展,sysfs也在不断演进,与configfs、debugfs、tracefs等新型文件系统协同工作,共同构建了Linux强大而灵活的设备管理生态。

掌握sysfs的深入知识,对于系统开发者、运维工程师和性能调优专家都至关重要。无论你是调试硬件问题、优化系统性能,还是构建云原生应用,对sysfs的深入理解都将是你工具箱中的重要武器。

未来展望

  • 更紧密的容器集成
  • 更好的性能优化
  • 更强的安全特性
  • 更智能的设备管理

随着技术的发展,sysfs将继续演化,但它的核心理念------为用户空间提供统一、结构化的内核接口------将始终保持不变。这正是Linux哲学的精髓所在:提供简单、一致的抽象,让复杂系统的管理变得可能。

相关推荐
cooldream20094 小时前
Vim 报错 E325:swap 文件冲突的原理、处理流程与彻底避免方案
linux·编辑器·vim
i建模4 小时前
在 Rocky Linux 上安装轻量级的 XFCE 桌面
linux·运维·服务器
若风的雨5 小时前
WC (Write-Combining) 内存类型优化原理
linux
YMWM_5 小时前
不同局域网下登录ubuntu主机
linux·运维·ubuntu
zmjjdank1ng5 小时前
restart与reload的区别
linux·运维
哼?~5 小时前
进程替换与自主Shell
linux
浩浩测试一下5 小时前
DDOS 应急响应Linux防火墙 Iptable 使用方式方法
linux·网络·安全·web安全·网络安全·系统安全·ddos
niceffking6 小时前
linux 信号内核模型
linux·运维·服务器
嵌入小生0076 小时前
单向链表的常用操作方法---嵌入式入门---Linux
linux·开发语言·数据结构·算法·链表·嵌入式
.小墨迹6 小时前
C++学习——C++中`memcpy`和**赋值拷贝**的核心区别
java·linux·开发语言·c++·学习·算法·机器学习