diff --git a/docs/api/packages/system.md b/docs/api/packages/system.md index c1bfc04..45fb0b2 100644 --- a/docs/api/packages/system.md +++ b/docs/api/packages/system.md @@ -31,6 +31,11 @@ import ( - [CompareOsEnv](#CompareOsEnv) - [ExecCommand](#ExecCommand) - [GetOsBits](#GetOsBits) +- [StartProcess](#StartProcess) +- [StopProcess](#StopProcess) +- [KillProcess](#KillProcess) +- [GetProcessInfo](#GetProcessInfo) +
@@ -248,7 +253,7 @@ func main() { ```go type ( - Option func(*exec.Cmd) + Option func(*exec.Cmd) ) func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error) ``` @@ -403,4 +408,37 @@ func main() { // Output: //根据进程id获取进程信息。
+ +函数签名: + +```go +func GetProcessInfo(pid int) (*ProcessInfo, error) +``` + +示例:[运行](todo) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("ls", "-a") + if err != nil { + return + } + + processInfo, err := system.GetProcessInfo(pid) + if err != nil { + return + } + + fmt.Println(processInfo) +} ``` \ No newline at end of file diff --git a/docs/en/api/packages/system.md b/docs/en/api/packages/system.md index 964f5fe..4a99a05 100644 --- a/docs/en/api/packages/system.md +++ b/docs/en/api/packages/system.md @@ -34,6 +34,8 @@ import ( - [StartProcess](#StartProcess) - [StopProcess](#StopProcess) - [KillProcess](#KillProcess) +- [GetProcessInfo](#GetProcessInfo) + @@ -407,4 +409,37 @@ func main() { // Output: //Retrieves detailed process information by pid.
+ +Signature: + +```go +func GetProcessInfo(pid int) (*ProcessInfo, error) +``` + +Example:[Run](todo) + +```go +import ( + "fmt" + "github.com/duke-git/lancet/v2/system" +) + +func main() { + pid, err := system.StartProcess("ls", "-a") + if err != nil { + return + } + + processInfo, err := system.GetProcessInfo(pid) + if err != nil { + return + } + + fmt.Println(processInfo) +} ``` \ No newline at end of file diff --git a/system/os.go b/system/os.go index f7dcdb0..669f8d8 100644 --- a/system/os.go +++ b/system/os.go @@ -6,9 +6,12 @@ package system import ( "bytes" + "fmt" "os" "os/exec" "runtime" + "strconv" + "strings" "unicode/utf8" "github.com/duke-git/lancet/v2/validator" @@ -166,3 +169,156 @@ func KillProcess(pid int) error { return process.Kill() } + +// ProcessInfo contains detailed information about a process. +type ProcessInfo struct { + PID int + CPU string + Memory string + State string + User string + Cmd string + Threads []string + IOStats string + StartTime string + ParentPID int + NetworkConnections string +} + +// GetProcessInfo retrieves detailed process information by pid. +// Play: todo +func GetProcessInfo(pid int) (*ProcessInfo, error) { + var cmd *exec.Cmd + + if runtime.GOOS == "windows" { + cmd = exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/FO", "CSV", "/V") + } else { + cmd = exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "pid,%cpu,%mem,state,user,comm") + } + + output, err := cmd.Output() + if err != nil { + return nil, err + } + + processInfo, err := parseProcessInfo(output, pid) + if err != nil { + return nil, err + } + + if runtime.GOOS != "windows" { + processInfo.Threads, _ = getThreadsInfo(pid) + processInfo.IOStats, _ = getIOStats(pid) + processInfo.StartTime, _ = getProcessStartTime(pid) + processInfo.ParentPID, _ = getParentProcess(pid) + processInfo.NetworkConnections, _ = getNetworkConnections(pid) + } + + return processInfo, nil +} + +// parseProcessInfo parses the output of `ps` or `tasklist` to fill the ProcessInfo structure. +func parseProcessInfo(output []byte, pid int) (*ProcessInfo, error) { + lines := strings.Split(string(output), "\n") + + if len(lines) < 2 { + return nil, fmt.Errorf("no process found with PID %d", pid) + } + + var processInfo ProcessInfo + if runtime.GOOS == "windows" { + fields := strings.Split(lines[1], "\",\"") + if len(fields) < 9 { + return nil, fmt.Errorf("unexpected tasklist output format") + } + + processInfo = ProcessInfo{ + PID: pid, + CPU: "N/A", + Memory: fields[4], // Memory usage in K + State: fields[5], + User: "N/A", + Cmd: fields[8], + } + } else { + fields := strings.Fields(lines[1]) + if len(fields) < 6 { + return nil, fmt.Errorf("unexpected ps output format") + } + + processInfo = ProcessInfo{ + PID: pid, + CPU: fields[1], + Memory: fields[2], + State: fields[3], + User: fields[4], + Cmd: fields[5], + } + } + + return &processInfo, nil +} + +func getThreadsInfo(pid int) ([]string, error) { + cmd := exec.Command("ps", "-T", "-p", strconv.Itoa(pid)) + output, err := cmd.Output() + if err != nil { + return nil, err + } + + lines := strings.Split(string(output), "\n") + + var threads []string + for _, line := range lines[1:] { + if strings.TrimSpace(line) != "" { + threads = append(threads, line) + } + } + + return threads, nil +} + +func getIOStats(pid int) (string, error) { + filePath := fmt.Sprintf("/proc/%d/io", pid) + data, err := os.ReadFile(filePath) + if err != nil { + return "", err + } + + return string(data), nil +} + +func getProcessStartTime(pid int) (string, error) { + cmd := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "lstart=") + output, err := cmd.Output() + if err != nil { + return "", err + } + + return strings.TrimSpace(string(output)), nil +} + +func getParentProcess(pid int) (int, error) { + cmd := exec.Command("ps", "-o", "ppid=", "-p", strconv.Itoa(pid)) + output, err := cmd.Output() + if err != nil { + return 0, err + } + + ppid, err := strconv.Atoi(strings.TrimSpace(string(output))) + if err != nil { + return 0, err + } + + return ppid, nil +} + +func getNetworkConnections(pid int) (string, error) { + cmd := exec.Command("lsof", "-p", strconv.Itoa(pid), "-i") + output, err := cmd.Output() + if err != nil { + return "", err + } + + return string(output), nil +} diff --git a/system/os_example_test.go b/system/os_example_test.go index ccdd317..bc7cf31 100644 --- a/system/os_example_test.go +++ b/system/os_example_test.go @@ -117,3 +117,17 @@ func ExampleKillProcess() { // Output: //