Skip to content

Commit fe96d73

Browse files
committed
fix: 添加CPU的混合架构识别的相关逻辑
1 parent a61b78b commit fe96d73

File tree

4 files changed

+323
-13
lines changed

4 files changed

+323
-13
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
run: |
2828
git config --global user.name 'github-actions'
2929
git config --global user.email 'github-actions@github.com'
30-
TAG="v0.0.15-$(date +'%Y%m%d%H%M%S')"
30+
TAG="v0.0.16-$(date +'%Y%m%d%H%M%S')"
3131
git tag $TAG
3232
git push origin $TAG
3333
echo "TAG=$TAG" >> $GITHUB_ENV

cmd/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import "testing"
44

55
func Test_main(t *testing.T) {
66
main()
7-
}
7+
}

model/model.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package model
22

3-
const BasicsVersion = "v0.0.15"
3+
const BasicsVersion = "v0.0.16"
44

55
var EnableLoger bool
66

@@ -96,11 +96,15 @@ var TranslationMap = map[string]string{
9696
}
9797

9898
type CpuInfo struct {
99-
CpuModel string
100-
CpuCores string
101-
CpuCache string
102-
CpuAesNi string
103-
CpuVAH string
99+
CpuModel string
100+
CpuCores string
101+
CpuPhysicalCores int // 物理核心数
102+
CpuLogicalCores int // 逻辑核心数(线程数)
103+
CpuThreadsPerCore int // 每核心线程数
104+
CpuSockets int // CPU插槽数
105+
CpuCache string
106+
CpuAesNi string
107+
CpuVAH string
104108
}
105109

106110
type MemoryInfo struct {
@@ -111,11 +115,11 @@ type MemoryInfo struct {
111115
}
112116

113117
type DiskInfo struct {
114-
DiskUsage []string
115-
DiskTotal []string
118+
DiskUsage []string
119+
DiskTotal []string
116120
DiskRealPath []string
117-
Percentage []string
118-
BootPath string
121+
Percentage []string
122+
BootPath string
119123
}
120124

121125
type SystemInfo struct {

system/cpu.go

Lines changed: 307 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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+
215508
func 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

Comments
 (0)