文章目录
- [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设备管理存在几个主要问题:
- 信息分散:设备信息分散在/proc、/dev和各个专有目录中
- 结构混乱:没有统一的设备层次结构表示
- 交互困难:设备控制接口不一致
- 扩展性差:难以适应日益复杂的硬件环境
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中的文件主要有以下几种类型:
- 属性文件(Attribute Files):包含设备或驱动的属性值,可读或可读写
- 符号链接(Symbolic Links):指向其他目录,建立设备间的关联
- 二进制文件(Binary Files):包含二进制数据,如固件镜像
- 目录(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- 子系统厂商IDsd00002246- 子系统设备IDbc01- 基类代码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权限问题
权限问题通常由以下原因引起:
- 系统安全策略(如SELinux、AppArmor)
- 文件系统挂载选项
- 用户权限不足
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功能强大,但仍存在一些局限性:
- 性能问题:频繁访问sysfs可能对系统性能产生影响
- 并发访问:缺乏完善的并发控制机制
- 安全性:暴露过多系统内部信息
- 扩展性:难以适应新的硬件架构
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 开发实践
- 错误处理:始终检查sysfs操作的返回值
- 资源管理:及时关闭文件描述符
- 并发安全:避免竞争条件
- 向后兼容:考虑不同内核版本的差异
9.2 部署实践
- 权限最小化:按需授予访问权限
- 监控告警:监控sysfs访问异常
- 安全加固:使用安全模块限制访问
- 性能优化:避免频繁访问关键路径
9.3 运维实践
- 定期检查:监控sysfs文件系统健康状态
- 备份配置:备份重要的sysfs配置
- 更新管理:跟踪内核更新对sysfs的影响
- 灾难恢复:准备sysfs故障恢复方案
结论
Linux sysfs文件系统作为内核与用户空间之间的桥梁,在现代Linux系统中扮演着至关重要的角色。通过本文的全面探讨,我们深入了解了:
- sysfs的设计哲学:基于kobject的统一设备模型
- 丰富的目录结构:从物理设备到逻辑分类的多维视图
- 强大的功能特性:设备管理、电源控制、性能监控
- 多样的访问方式:从命令行工具到编程接口
- 广泛的应用场景:从嵌入式设备到云原生环境
- 完整的生态系统:与udev、systemd、容器技术的集成
尽管sysfs面临着性能、安全性和扩展性方面的挑战,但它仍然是Linux设备管理的核心基础设施。随着Linux内核的持续发展,sysfs也在不断演进,与configfs、debugfs、tracefs等新型文件系统协同工作,共同构建了Linux强大而灵活的设备管理生态。
掌握sysfs的深入知识,对于系统开发者、运维工程师和性能调优专家都至关重要。无论你是调试硬件问题、优化系统性能,还是构建云原生应用,对sysfs的深入理解都将是你工具箱中的重要武器。
未来展望:
- 更紧密的容器集成
- 更好的性能优化
- 更强的安全特性
- 更智能的设备管理
随着技术的发展,sysfs将继续演化,但它的核心理念------为用户空间提供统一、结构化的内核接口------将始终保持不变。这正是Linux哲学的精髓所在:提供简单、一致的抽象,让复杂系统的管理变得可能。