@@ -212,10 +212,316 @@ func updateSystemLoad(ret *model.SystemInfo) {
212212 }
213213}
214214
215+ func getCpuCoreDetails (ret * model.SystemInfo ) {
216+ // 默认值:使用runtime.NumCPU()作为逻辑核心数
217+ ret .CpuLogicalCores = runtime .NumCPU ()
218+ ret .CpuPhysicalCores = 0 // 初始化为0,如果获取失败则保持为0
219+ ret .CpuThreadsPerCore = 1
220+ ret .CpuSockets = 1
221+
222+ // 首先尝试使用gopsutil获取核心信息(跨平台支持)
223+ if ! getCpuCoreDetailsFromGopsutil (ret ) {
224+ // 如果gopsutil失败,根据不同操作系统使用特定方法
225+ switch runtime .GOOS {
226+ case "linux" :
227+ getCpuCoreDetailsLinux (ret )
228+ case "windows" :
229+ getCpuCoreDetailsWindows (ret )
230+ case "darwin" :
231+ getCpuCoreDetailsDarwin (ret )
232+ }
233+ }
234+
235+ // 最终降级处理:如果没有成功获取物理核心数,设置为逻辑核心数
236+ if ret .CpuPhysicalCores <= 0 {
237+ ret .CpuPhysicalCores = ret .CpuLogicalCores
238+ ret .CpuThreadsPerCore = 1
239+ }
240+ }
241+
242+ func getCpuCoreDetailsFromGopsutil (ret * model.SystemInfo ) bool {
243+ // 获取逻辑核心数
244+ logicalCores , err := cpu .Counts (true )
245+ if err == nil && logicalCores > 0 {
246+ ret .CpuLogicalCores = logicalCores
247+ } else {
248+ return false
249+ }
250+
251+ // 获取物理核心数
252+ physicalCores , err := cpu .Counts (false )
253+ if err == nil && physicalCores > 0 {
254+ ret .CpuPhysicalCores = physicalCores
255+ } else {
256+ return false
257+ }
258+
259+ // 计算每核心线程数
260+ if ret .CpuPhysicalCores > 0 && ret .CpuLogicalCores >= ret .CpuPhysicalCores {
261+ ret .CpuThreadsPerCore = ret .CpuLogicalCores / ret .CpuPhysicalCores
262+ }
263+
264+ // 尝试从cpu.Info()获取socket信息
265+ cpuInfo , err := cpu .Info ()
266+ if err == nil && len (cpuInfo ) > 0 {
267+ physicalIDs := make (map [string ]bool )
268+ for _ , info := range cpuInfo {
269+ if info .PhysicalID != "" {
270+ physicalIDs [info .PhysicalID ] = true
271+ }
272+ }
273+ if len (physicalIDs ) > 0 {
274+ ret .CpuSockets = len (physicalIDs )
275+ }
276+ }
277+
278+ return ret .CpuPhysicalCores > 0
279+ }
280+
281+ func getCpuCoreDetailsLinux (ret * model.SystemInfo ) {
282+ var foundDetails bool
283+ var coresPerSocket int
284+
285+ // 尝试从lscpu获取信息
286+ cmd := exec .Command ("lscpu" , "-B" )
287+ output , err := cmd .Output ()
288+ if err == nil {
289+ lines := strings .Split (string (output ), "\n " )
290+ for _ , line := range lines {
291+ fields := strings .Split (line , ":" )
292+ if len (fields ) < 2 {
293+ continue
294+ }
295+ key := strings .TrimSpace (fields [0 ])
296+ value := strings .TrimSpace (fields [1 ])
297+
298+ switch key {
299+ case "CPU(s)" :
300+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
301+ ret .CpuLogicalCores = v
302+ foundDetails = true
303+ }
304+ case "Thread(s) per core" :
305+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
306+ ret .CpuThreadsPerCore = v
307+ foundDetails = true
308+ }
309+ case "Core(s) per socket" :
310+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
311+ coresPerSocket = v
312+ foundDetails = true
313+ }
314+ case "Socket(s)" :
315+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
316+ ret .CpuSockets = v
317+ foundDetails = true
318+ }
319+ }
320+ }
321+
322+ // 计算总物理核心数 = 每插槽核心数 × 插槽数
323+ if coresPerSocket > 0 && ret .CpuSockets > 0 {
324+ ret .CpuPhysicalCores = coresPerSocket * ret .CpuSockets
325+ }
326+ }
327+
328+ // 如果lscpu没有获取到完整信息,尝试从/proc/cpuinfo获取
329+ if ! foundDetails || ret .CpuPhysicalCores <= 0 {
330+ getCpuCoreDetailsFromProcCpuinfo (ret )
331+ }
332+ }
333+
334+ func getCpuCoreDetailsFromProcCpuinfo (ret * model.SystemInfo ) {
335+ content , err := os .ReadFile ("/proc/cpuinfo" )
336+ if err != nil {
337+ return
338+ }
339+
340+ physicalIDs := make (map [string ]bool )
341+ var siblings , cpuCores int
342+ var foundInfo bool
343+
344+ lines := strings .Split (string (content ), "\n " )
345+ for _ , line := range lines {
346+ fields := strings .Split (line , ":" )
347+ if len (fields ) < 2 {
348+ continue
349+ }
350+ key := strings .TrimSpace (fields [0 ])
351+ value := strings .TrimSpace (fields [1 ])
352+
353+ switch key {
354+ case "physical id" :
355+ physicalIDs [value ] = true
356+ foundInfo = true
357+ case "siblings" :
358+ if v , err := strconv .Atoi (value ); err == nil && siblings == 0 {
359+ siblings = v
360+ foundInfo = true
361+ }
362+ case "cpu cores" :
363+ if v , err := strconv .Atoi (value ); err == nil && cpuCores == 0 {
364+ cpuCores = v
365+ foundInfo = true
366+ }
367+ }
368+ }
369+
370+ // 只有在成功获取到信息时才更新
371+ if foundInfo {
372+ if len (physicalIDs ) > 0 {
373+ ret .CpuSockets = len (physicalIDs )
374+ }
375+ if cpuCores > 0 && len (physicalIDs ) > 0 {
376+ ret .CpuPhysicalCores = cpuCores * len (physicalIDs )
377+ } else if cpuCores > 0 {
378+ // 如果没有physical id信息,至少使用cpu cores
379+ ret .CpuPhysicalCores = cpuCores
380+ }
381+ if siblings > 0 && cpuCores > 0 && siblings >= cpuCores {
382+ ret .CpuThreadsPerCore = siblings / cpuCores
383+ }
384+ }
385+ }
386+
387+ func getCpuCoreDetailsWindows (ret * model.SystemInfo ) {
388+ // Windows下尝试使用wmic获取信息
389+ cmd := exec .Command ("wmic" , "cpu" , "get" , "NumberOfCores,NumberOfLogicalProcessors" , "/format:list" )
390+ output , err := cmd .Output ()
391+ if err != nil {
392+ return
393+ }
394+
395+ lines := strings .Split (string (output ), "\n " )
396+ var totalCores , totalLogical int
397+ for _ , line := range lines {
398+ if strings .Contains (line , "NumberOfCores=" ) {
399+ parts := strings .Split (line , "=" )
400+ if len (parts ) == 2 {
401+ value := strings .TrimSpace (parts [1 ])
402+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
403+ totalCores += v
404+ }
405+ }
406+ } else if strings .Contains (line , "NumberOfLogicalProcessors=" ) {
407+ parts := strings .Split (line , "=" )
408+ if len (parts ) == 2 {
409+ value := strings .TrimSpace (parts [1 ])
410+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
411+ totalLogical += v
412+ }
413+ }
414+ }
415+ }
416+
417+ // 只有在成功获取到信息时才更新
418+ if totalCores > 0 {
419+ ret .CpuPhysicalCores = totalCores
420+ }
421+ if totalLogical > 0 {
422+ ret .CpuLogicalCores = totalLogical
423+ }
424+ if totalCores > 0 && totalLogical > 0 && totalLogical >= totalCores {
425+ ret .CpuThreadsPerCore = totalLogical / totalCores
426+ }
427+ }
428+
429+ func getCpuCoreDetailsDarwin (ret * model.SystemInfo ) {
430+ // macOS下尝试使用sysctl获取信息
431+ path , exists := utils .GetPATH ("sysctl" )
432+ if ! exists {
433+ return
434+ }
435+
436+ // 获取物理核心数
437+ if out , err := exec .Command (path , "-n" , "hw.physicalcpu" ).Output (); err == nil {
438+ value := strings .TrimSpace (string (out ))
439+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
440+ ret .CpuPhysicalCores = v
441+ }
442+ }
443+
444+ // 获取逻辑核心数
445+ if out , err := exec .Command (path , "-n" , "hw.logicalcpu" ).Output (); err == nil {
446+ value := strings .TrimSpace (string (out ))
447+ if v , err := strconv .Atoi (value ); err == nil && v > 0 {
448+ ret .CpuLogicalCores = v
449+ }
450+ }
451+
452+ // 计算每核心线程数
453+ if ret .CpuPhysicalCores > 0 && ret .CpuLogicalCores > 0 && ret .CpuLogicalCores >= ret .CpuPhysicalCores {
454+ ret .CpuThreadsPerCore = ret .CpuLogicalCores / ret .CpuPhysicalCores
455+ }
456+ }
457+
458+ func formatCpuCores (ret * model.SystemInfo , cpuType string , lang string ) string {
459+ if ret .CpuLogicalCores == 0 {
460+ return ""
461+ }
462+
463+ // 检查是否成功获取了详细信息(物理核心数与逻辑核心数不同,或者有多线程)
464+ hasDetailedInfo := (ret .CpuPhysicalCores > 0 &&
465+ (ret .CpuPhysicalCores != ret .CpuLogicalCores || ret .CpuThreadsPerCore > 1 ))
466+
467+ // 降级处理:如果没有获取到详细信息,使用简单格式
468+ if ! hasDetailedInfo {
469+ if lang == "zh" || lang == "cn" || lang == "chinese" {
470+ return fmt .Sprintf ("%d %s CPU(s)" , ret .CpuLogicalCores , cpuType )
471+ } else {
472+ return fmt .Sprintf ("%d %s CPU(s)" , ret .CpuLogicalCores , cpuType )
473+ }
474+ }
475+
476+ // 检测是否为混合架构(简单判断:如果线程数不是整数倍)
477+ isHybrid := (ret .CpuLogicalCores != ret .CpuPhysicalCores * ret .CpuThreadsPerCore )
478+
479+ if lang == "zh" || lang == "cn" || lang == "chinese" {
480+ if isHybrid {
481+ // 混合架构,可能有大小核
482+ return fmt .Sprintf ("%d 插槽, %d 物理核心, %d 逻辑线程 (混合架构)" ,
483+ ret .CpuSockets , ret .CpuPhysicalCores , ret .CpuLogicalCores )
484+ } else if ret .CpuThreadsPerCore > 1 {
485+ // 有超线程
486+ return fmt .Sprintf ("%d 插槽, %d 物理核心, %d 逻辑线程" ,
487+ ret .CpuSockets , ret .CpuPhysicalCores , ret .CpuLogicalCores )
488+ } else {
489+ // 无超线程
490+ return fmt .Sprintf ("%d 插槽, %d 物理核心" , ret .CpuSockets , ret .CpuPhysicalCores )
491+ }
492+ } else {
493+ if isHybrid {
494+ // Hybrid architecture
495+ return fmt .Sprintf ("%d Socket(s), %d Physical Core(s), %d Logical Thread(s) (Hybrid)" ,
496+ ret .CpuSockets , ret .CpuPhysicalCores , ret .CpuLogicalCores )
497+ } else if ret .CpuThreadsPerCore > 1 {
498+ // With hyper-threading
499+ return fmt .Sprintf ("%d Socket(s), %d Physical Core(s), %d Logical Thread(s)" ,
500+ ret .CpuSockets , ret .CpuPhysicalCores , ret .CpuLogicalCores )
501+ } else {
502+ // No hyper-threading
503+ return fmt .Sprintf ("%d Socket(s), %d Physical Core(s)" , ret .CpuSockets , ret .CpuPhysicalCores )
504+ }
505+ }
506+ }
507+
215508func getCpuInfo (ret * model.SystemInfo , cpuType string ) (* model.SystemInfo , error ) {
216- if runtime .NumCPU () != 0 {
509+ // 获取详细的核心信息
510+ getCpuCoreDetails (ret )
511+
512+ // 根据系统语言格式化输出
513+ lang := os .Getenv ("LANG" )
514+ if strings .Contains (strings .ToLower (lang ), "zh" ) {
515+ ret .CpuCores = formatCpuCores (ret , cpuType , "zh" )
516+ } else {
517+ ret .CpuCores = formatCpuCores (ret , cpuType , "en" )
518+ }
519+
520+ // 如果格式化失败,使用默认格式
521+ if ret .CpuCores == "" && runtime .NumCPU () != 0 {
217522 ret .CpuCores = fmt .Sprintf ("%d %s CPU(s)" , runtime .NumCPU (), cpuType )
218523 }
524+
219525 switch runtime .GOOS {
220526 case "windows" :
221527 return getWindowsCpuInfo (ret )
0 commit comments