github.com/songzhibin97/gkit@v1.2.13/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  }