//go:build linux // +build linux package staros import ( "bytes" "encoding/binary" "errors" "io/ioutil" "os" "os/user" "strconv" "strings" "sync" "syscall" "time" "unsafe" ) var ( clockTicksOnce sync.Once clockTicksValue uint64 = 100 ) // StartTime 开机时间 func StartTime() time.Time { tmp, _ := readAsString("/proc/stat") data := splitBy(ReplaceByte9(tmp), " ") btime, _ := strconv.ParseInt(strings.TrimSpace(data["btime"]), 10, 64) return time.Unix(btime, 0) } // IsRoot 当前是否是管理员用户 func IsRoot() bool { uid, err := user.Current() return err == nil && uid != nil && uid.Uid == "0" } func Whoami() (uid, gid int, uname, gname, home string, err error) { var me *user.User var gup *user.Group me, err = user.Current() if err != nil { return } uid, _ = strconv.Atoi(me.Uid) gid, _ = strconv.Atoi(me.Gid) home = me.HomeDir uname = me.Username gup, err = user.LookupGroupId(me.Gid) if err != nil { return } gname = gup.Name return } func clockTicks() uint64 { clockTicksOnce.Do(initClockTicks) if clockTicksValue == 0 { return 100 } return clockTicksValue } func initClockTicks() { ticks, err := readClockTicksFromAuxv() if err != nil || ticks == 0 { return } clockTicksValue = ticks } func readClockTicksFromAuxv() (uint64, error) { data, err := os.ReadFile("/proc/self/auxv") if err != nil { return 0, err } wordSize := int(unsafe.Sizeof(uintptr(0))) if wordSize != 4 && wordSize != 8 { return 0, errors.New("unsupported pointer size") } order := nativeEndian() entrySize := wordSize * 2 for offset := 0; offset+entrySize <= len(data); offset += entrySize { key := readAuxvWord(data[offset:offset+wordSize], order) val := readAuxvWord(data[offset+wordSize:offset+entrySize], order) if key == 0 { break } if key == 17 { return val, nil } } return 0, errors.New("AT_CLKTCK not found") } func readAuxvWord(data []byte, order binary.ByteOrder) uint64 { if len(data) == 4 { return uint64(order.Uint32(data)) } return order.Uint64(data) } func nativeEndian() binary.ByteOrder { var n uint16 = 1 if *(*byte)(unsafe.Pointer(&n)) == 1 { return binary.LittleEndian } return binary.BigEndian } func cpuUsageOverDuration(delta float64, sleep time.Duration) float64 { if delta < 0 || sleep <= 0 { return 0 } seconds := sleep.Seconds() if seconds <= 0 { return 0 } return delta / seconds * 100 } func cpuUsagePercent(busyTicks, totalTicks float64) float64 { if busyTicks < 0 || totalTicks <= 0 { return 0 } return 100 * busyTicks / totalTicks } func getCPUSample() (idle, total uint64) { contents, err := ioutil.ReadFile("/proc/stat") if err != nil { return } lines := strings.Split(string(contents), "\n") for _, line := range lines { fields := strings.Fields(line) if len(fields) == 0 { continue } if fields[0] == "cpu" { numFields := len(fields) for i := 1; i < numFields; i++ { val, err := strconv.ParseUint(fields[i], 10, 64) if err != nil { continue } total += val // tally up all the numbers to get total ticks if i == 4 || i == 5 { // idle is the 5th field in the cpu line idle += val } } return } } return } func splitProcStat(content []byte) []string { nameStart := bytes.IndexByte(content, '(') nameEnd := bytes.LastIndexByte(content, ')') restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') ' name := content[nameStart+1 : nameEnd] pid := strings.TrimSpace(string(content[:nameStart])) fields := make([]string, 3, len(restFields)+3) fields[1] = string(pid) fields[2] = string(name) fields = append(fields, restFields...) return fields } func getCPUSampleByPid(pid int) float64 { contents, err := ioutil.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat") if err != nil { return 0 } fields := splitProcStat(contents) utime, err := strconv.ParseFloat(fields[14], 64) if err != nil { return 0 } stime, err := strconv.ParseFloat(fields[15], 64) if err != nil { return 0 } // There is no such thing as iotime in stat file. As an approximation, we // will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux // docs). Note: I am assuming at least Linux 2.6.18 var iotime float64 if len(fields) > 42 { iotime, err = strconv.ParseFloat(fields[42], 64) if err != nil { iotime = 0 // Ancient linux version, most likely } } else { iotime = 0 // e.g. SmartOS containers } ticks := float64(clockTicks()) return utime/ticks + stime/ticks + iotime/ticks } func CpuUsageByPid(pid int, sleep time.Duration) float64 { if sleep <= 0 { return 0 } total1 := getCPUSampleByPid(pid) time.Sleep(sleep) total2 := getCPUSampleByPid(pid) return cpuUsageOverDuration(total2-total1, sleep) } // CpuUsage 获取CPU使用量 func CpuUsage(sleep time.Duration) float64 { if sleep <= 0 { return 0 } idle0, total0 := getCPUSample() time.Sleep(sleep) idle1, total1 := getCPUSample() idleTicks := float64(idle1 - idle0) totalTicks := float64(total1 - total0) cpuUsage := cpuUsagePercent(totalTicks-idleTicks, totalTicks) return cpuUsage //fmt.Printf("CPU usage is %f%% [busy: %f, total: %f]\n", cpuUsage, totalTicks-idleTicks, totalTicks) } func DiskUsage(path string) (disk DiskStatus) { disk, _ = DiskUsageE(path) return } func DiskUsageE(path string) (disk DiskStatus, err error) { if path == "" { path = "." } fs := syscall.Statfs_t{} if err = syscall.Statfs(path, &fs); err != nil { return } disk.All = fs.Blocks * uint64(fs.Bsize) disk.Free = fs.Bfree * uint64(fs.Bsize) disk.Available = fs.Bavail * uint64(fs.Bsize) disk.Used = disk.All - disk.Free return }