github.com/songzhibin97/gkit@v1.2.13/internal/sys/cpu/cgroupcpu.go (about) 1 package cpu 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 "strconv" 8 "strings" 9 10 "github.com/pkg/errors" 11 c "github.com/shirou/gopsutil/cpu" 12 ) 13 14 type cGroupCPU struct { 15 frequency uint64 16 quota float64 17 cores uint64 18 19 preSystem uint64 20 preTotal uint64 21 usage uint64 22 } 23 24 func newCGroupCPU() (cpu *cGroupCPU, err error) { 25 var cores int 26 cores, err = c.Counts(true) 27 if err != nil || cores == 0 { 28 var cpus []uint64 29 cpus, err = perCPUUsage() 30 if err != nil { 31 err = errors.Errorf("perCPUUsage() failed!err:=%v", err) 32 return 33 } 34 cores = len(cpus) 35 } 36 37 sets, err := cpuSets() 38 if err != nil { 39 err = errors.Errorf("cpuSets() failed!err:=%v", err) 40 return 41 } 42 quota := float64(len(sets)) 43 cq, err := cpuQuota() 44 if err == nil && cq != -1 { 45 var period uint64 46 if period, err = cpuPeriod(); err != nil { 47 err = errors.Errorf("cpuPeriod() failed!err:=%v", err) 48 return 49 } 50 limit := float64(cq) / float64(period) 51 if limit < quota { 52 quota = limit 53 } 54 } 55 maxFreq := cpuMaxFreq() 56 57 preSystem, err := systemCPUUsage() 58 if err != nil { 59 err = errors.Errorf("systemCPUUsage() failed!err:=%v", err) 60 return 61 } 62 preTotal, err := totalCPUUsage() 63 if err != nil { 64 err = errors.Errorf("totalCPUUsage() failed!err:=%v", err) 65 return 66 } 67 cpu = &cGroupCPU{ 68 frequency: maxFreq, 69 quota: quota, 70 cores: uint64(cores), 71 preSystem: preSystem, 72 preTotal: preTotal, 73 } 74 return 75 } 76 77 func (cpu *cGroupCPU) Usage() (u uint64, err error) { 78 var ( 79 total uint64 80 system uint64 81 ) 82 total, err = totalCPUUsage() 83 if err != nil { 84 return 85 } 86 system, err = systemCPUUsage() 87 if err != nil { 88 return 89 } 90 if system != cpu.preSystem { 91 u = uint64(float64((total-cpu.preTotal)*cpu.cores*1e3) / (float64(system-cpu.preSystem) * cpu.quota)) 92 } 93 cpu.preSystem = system 94 cpu.preTotal = total 95 return 96 } 97 98 func (cpu *cGroupCPU) Info() Info { 99 return Info{ 100 Frequency: cpu.frequency, 101 Quota: cpu.quota, 102 } 103 } 104 105 const nanoSecondsPerSecond = 1e9 106 107 // ErrNoCFSLimit: 没有配额限制 108 // var ErrNoCFSLimit = errors.Errorf("no quota limit") 109 110 var clockTicksPerSecond = uint64(getClockTicks()) 111 112 // systemCPUUsage 113 // 以纳秒为单位返回主机系统的cpu使用情况。 114 // 如果基础文件的格式不匹配,则返回错误。 115 // 用POSIX定义的 /proc/stat 查找cpu 统计信息行,然后汇总提供的前七个字段 116 // 有关特定字段的详细信息,请参见 man 5 proc. 117 func systemCPUUsage() (usage uint64, err error) { 118 var ( 119 line string 120 f *os.File 121 ) 122 if f, err = os.Open("/proc/stat"); err != nil { 123 return 124 } 125 bufReader := bufio.NewReaderSize(nil, 128) 126 defer func() { 127 bufReader.Reset(nil) 128 _ = f.Close() 129 }() 130 bufReader.Reset(f) 131 for err == nil { 132 if line, err = bufReader.ReadString('\n'); err != nil { 133 err = errors.WithStack(err) 134 return 135 } 136 parts := strings.Fields(line) 137 switch parts[0] { 138 case "cpu": 139 if len(parts) < 8 { 140 err = errors.WithStack(fmt.Errorf("bad format of cpu stats")) 141 return 142 } 143 var totalClockTicks uint64 144 for _, i := range parts[1:8] { 145 var v uint64 146 if v, err = strconv.ParseUint(i, 10, 64); err != nil { 147 err = errors.WithStack(fmt.Errorf("error parsing cpu stats")) 148 return 149 } 150 totalClockTicks += v 151 } 152 usage = (totalClockTicks * nanoSecondsPerSecond) / clockTicksPerSecond 153 return 154 } 155 } 156 err = errors.Errorf("bad stats format") 157 return 158 } 159 160 func totalCPUUsage() (usage uint64, err error) { 161 var cg *cGroup 162 if cg, err = currentcGroup(); err != nil { 163 return 164 } 165 return cg.CPUAcctUsage() 166 } 167 168 func perCPUUsage() (usage []uint64, err error) { 169 var cg *cGroup 170 if cg, err = currentcGroup(); err != nil { 171 return 172 } 173 return cg.CPUAcctUsagePerCPU() 174 } 175 176 func cpuSets() (sets []uint64, err error) { 177 var cg *cGroup 178 if cg, err = currentcGroup(); err != nil { 179 return 180 } 181 return cg.CPUSetCPUs() 182 } 183 184 func cpuQuota() (quota int64, err error) { 185 var cg *cGroup 186 if cg, err = currentcGroup(); err != nil { 187 return 188 } 189 return cg.CPUCFSQuotaUs() 190 } 191 192 func cpuPeriod() (peroid uint64, err error) { 193 var cg *cGroup 194 if cg, err = currentcGroup(); err != nil { 195 return 196 } 197 return cg.CPUCFSPeriodUs() 198 } 199 200 func cpuFreq() uint64 { 201 lines, err := readLines("/proc/cpuinfo") 202 if err != nil { 203 return 0 204 } 205 for _, line := range lines { 206 fields := strings.Split(line, ":") 207 if len(fields) < 2 { 208 continue 209 } 210 key := strings.TrimSpace(fields[0]) 211 value := strings.TrimSpace(fields[1]) 212 if key == "cpu MHz" || key == "clock" { 213 if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil { 214 return uint64(t * 1000.0 * 1000.0) 215 } 216 } 217 } 218 return 0 219 } 220 221 func cpuMaxFreq() uint64 { 222 feq := cpuFreq() 223 data, err := readFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") 224 if err != nil { 225 return feq 226 } 227 cfeq, err := parseUint(data) 228 if err == nil { 229 feq = cfeq 230 } 231 return feq 232 } 233 234 // getClockTicks 获取ticks 周期数 235 func getClockTicks() int { 236 return 100 237 }