From da712dc83496752c2a0bcd011520a9b6b58c3e66 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Thu, 7 May 2026 21:17:06 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=86=85=E5=AD=98?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=EF=BC=8C=E6=94=B9=E7=94=A8=20total=20-=20cac?= =?UTF-8?q?hed=20-=20free=20=E5=85=AC=E5=BC=8F=EF=BC=8C=E4=B8=8E=20Activit?= =?UTF-8?q?y=20Monitor=20=E5=AE=8C=E5=85=A8=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FloatingMonitor/SystemMonitor.swift | 136 +++++++++++++++++++- 1 file changed, 129 insertions(+), 7 deletions(-) diff --git a/Sources/FloatingMonitor/SystemMonitor.swift b/Sources/FloatingMonitor/SystemMonitor.swift index 5035bc8..ae88169 100644 --- a/Sources/FloatingMonitor/SystemMonitor.swift +++ b/Sources/FloatingMonitor/SystemMonitor.swift @@ -28,6 +28,15 @@ struct SystemInfo { var topCPUProcesses: [ProcessInfo] var topMemoryProcesses: [ProcessInfo] + // 网络速度 + var networkDownloadBytesPerSec: UInt64 + var networkUploadBytesPerSec: UInt64 + var networkInterface: String + + // 磁盘 I/O + var diskReadBytesPerSec: UInt64 + var diskWriteBytesPerSec: UInt64 + struct ProcessInfo: Identifiable { let id = UUID() let pid: Int32 @@ -48,12 +57,20 @@ final class SystemMonitor: ObservableObject { swapTotal: 0, swapUsed: 0, swapUsagePercent: 0, swapIns: 0, swapOuts: 0, cpuTemperature: nil, gpuTemperature: nil, - topCPUProcesses: [], topMemoryProcesses: [] + topCPUProcesses: [], topMemoryProcesses: [], + networkDownloadBytesPerSec: 0, networkUploadBytesPerSec: 0, networkInterface: "", + diskReadBytesPerSec: 0, diskWriteBytesPerSec: 0 ) private var timer: Timer? private var previousCPU: host_cpu_load_info? + // 网络 / 磁盘上次采样 + private var previousNetwork: (inBytes: UInt64, outBytes: UInt64)? + private var previousNetworkTime: Date? + private var previousDisk: (readBytes: UInt64, writeBytes: UInt64)? + private var previousDiskTime: Date? + // 采集频率 var interval: TimeInterval = 1.5 { didSet { restartTimer() } @@ -96,6 +113,8 @@ final class SystemMonitor: ObservableObject { let swap = fetchSwap() let temp = fetchTemperature() let procs = fetchTopProcesses(count: topN) + let net = fetchNetwork() + let disk = fetchDiskIO() info = SystemInfo( cpuUsage: cpu.usage, @@ -114,7 +133,12 @@ final class SystemMonitor: ObservableObject { cpuTemperature: temp.cpu, gpuTemperature: temp.gpu, topCPUProcesses: procs.cpu, - topMemoryProcesses: procs.mem + topMemoryProcesses: procs.mem, + networkDownloadBytesPerSec: net.download, + networkUploadBytesPerSec: net.upload, + networkInterface: net.interface, + diskReadBytesPerSec: disk.read, + diskWriteBytesPerSec: disk.write ) } @@ -248,12 +272,10 @@ final class SystemMonitor: ObservableObject { guard result == KERN_SUCCESS else { return (total, 0, 0) } - let activePages = UInt64(vmStat.active_count) - let wiredPages = UInt64(vmStat.wire_count) - let compressedPages = UInt64(vmStat.compressor_page_count) - let pageSizeU64 = UInt64(pageSize) - let usedRaw = (activePages + wiredPages + compressedPages) * pageSizeU64 + let cachedFiles = (UInt64(vmStat.external_page_count) + UInt64(vmStat.purgeable_count)) * pageSizeU64 + let freeBytes = UInt64(vmStat.free_count) * pageSizeU64 + let usedRaw = total - cachedFiles - freeBytes let percent = Double(usedRaw) / Double(total) * 100 return (total, usedRaw, min(percent, 100)) @@ -347,4 +369,104 @@ final class SystemMonitor: ObservableObject { return results } + + // MARK: - 网络速度 (getifaddrs) + + private func fetchNetwork() -> (download: UInt64, upload: UInt64, interface: String) { + var ifaddr: UnsafeMutablePointer? + guard getifaddrs(&ifaddr) == 0, let first = ifaddr else { + return (0, 0, "") + } + defer { freeifaddrs(first) } + + var inBytes: UInt64 = 0 + var outBytes: UInt64 = 0 + var ifName = "" + + var ptr = first + while true { + let name = String(cString: ptr.pointee.ifa_name) + // 优先 Wi-Fi,其次以太网 + if (name == "en0" || name.hasPrefix("en")) && Int32(ptr.pointee.ifa_addr?.pointee.sa_family ?? 0) == AF_LINK { + let data = ptr.pointee.ifa_data?.assumingMemoryBound(to: if_data.self) + if let d = data { + let ib = UInt64(d.pointee.ifi_ibytes) + let ob = UInt64(d.pointee.ifi_obytes) + // en0(Wi-Fi)优先级最高 + if name == "en0" || (ib + ob) > (inBytes + outBytes) { + inBytes = ib + outBytes = ob + ifName = name + } + } + } + guard let next = ptr.pointee.ifa_next else { break } + ptr = next + } + + let now = Date() + defer { + previousNetwork = (inBytes, outBytes) + previousNetworkTime = now + } + + guard let prev = previousNetwork, let prevTime = previousNetworkTime else { + return (0, 0, ifName) + } + + let elapsed = max(now.timeIntervalSince(prevTime), 0.1) + let dl = UInt64(max(0, Double(inBytes) - Double(prev.inBytes)) / elapsed) + let ul = UInt64(max(0, Double(outBytes) - Double(prev.outBytes)) / elapsed) + + return (dl, ul, ifName) + } + + // MARK: - 磁盘 I/O (IOKit IOBlockStorageDriver) + + private func fetchDiskIO() -> (read: UInt64, write: UInt64) { + var readBytes: UInt64 = 0 + var writeBytes: UInt64 = 0 + + var iterator: io_iterator_t = 0 + let matchDict = IOServiceMatching("IOBlockStorageDriver") + guard IOServiceGetMatchingServices(kIOMainPortDefault, matchDict, &iterator) == KERN_SUCCESS else { + return (0, 0) + } + defer { IOObjectRelease(iterator) } + + var entry = IOIteratorNext(iterator) + while entry != 0 { + if let statsCF = IORegistryEntryCreateCFProperty(entry, "Statistics" as CFString, kCFAllocatorDefault, 0) { + let stats = statsCF.takeUnretainedValue() as? [String: Any] + if let r = stats?["Bytes (Read)"] as? UInt64 { + readBytes += r + } else if let r = stats?["Bytes (Read)"] as? Int { + readBytes += UInt64(r) + } + if let w = stats?["Bytes (Write)"] as? UInt64 { + writeBytes += w + } else if let w = stats?["Bytes (Write)"] as? Int { + writeBytes += UInt64(w) + } + } + IOObjectRelease(entry) + entry = IOIteratorNext(iterator) + } + + let now = Date() + defer { + previousDisk = (readBytes, writeBytes) + previousDiskTime = now + } + + guard let prev = previousDisk, let prevTime = previousDiskTime else { + return (0, 0) + } + + let elapsed = max(now.timeIntervalSince(prevTime), 0.1) + let r = UInt64(max(0, Double(readBytes) - Double(prev.readBytes)) / elapsed) + let w = UInt64(max(0, Double(writeBytes) - Double(prev.writeBytes)) / elapsed) + + return (r, w) + } }