Golang读取Linux系统中的proc目录详解

Golang读取Linux系统中的proc目录详解 我正在尝试读取Linux文件系统的proc,更具体地说是comm文件。我的问题是,我的二进制文件在某些进程处卡住,不再执行任何操作。

我尝试使用以下库:“github.com/mitchellh/go-ps

processes, err := gops.Processes()

	if err != nil {
		log.Fatal(err)
	}

	for _, process := range processes {
		if process.Executable() == config.Cmd.Processname {
			running = true
			log.Debug("Process running: " + strconv.FormatBool(running))
			return running
		}
	}

	return running

或者传统方法

files, _ := ioutil.ReadDir("/proc")
	for _, f := range files {
		// check if folder is a integer (process number)
		if _, err := strconv.Atoi(f.Name()); err == nil {
			// open status file of process
			f, err := os.Open("/proc/" + f.Name() + "/status")
			if err != nil {
				log.Info(err)
				return running
			}

			// read status line by line
			scanner := bufio.NewScanner(f)

			// check if process name in status of process
			for scanner.Scan() {

				re := regexp.MustCompile("^Name:\\s*" + config.Cmd.Processname + ".*")
				match := re.MatchString(scanner.Text())

				if match == true {
					running = true
					log.Debug("Process running: " + strconv.FormatBool(running))
				}

			}
			if running == true {
				return running
			}

		}

	}

我很感谢任何建议。


更多关于Golang读取Linux系统中的proc目录详解的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我自己解决了这个问题。这个库有一个没有任何等待的无限循环。我在这个循环中添加了一个睡眠,现在它运行得很顺利。

更多关于Golang读取Linux系统中的proc目录详解的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


buddyspencer:

f, err := os.Open("/proc/" + f.Name() + “/status”)

目前已知是这行代码导致了问题。正是因为它,我的程序会自行挂起。

编辑:我使用的某个库运行了一个 Goroutine。只要我禁用这个例程,程序就能正常工作。但问题在于某些情况下我需要这个例程。 以下是引发问题的例程:

func StartElastic() {
	done = false
	wg.Add(1)
	go sendelastic()
}

在处理Linux的/proc目录时,直接读取文件可能会遇到进程状态变化或权限问题导致阻塞。以下是两种改进方法,使用标准库避免依赖第三方包,并处理可能的阻塞情况。

方法1:使用os.ReadDir和上下文超时

通过os.ReadDir读取/proc目录,并为每个进程目录的操作设置超时,防止卡在特定进程上。

package main

import (
    "bufio"
    "context"
    "fmt"
    "os"
    "path/filepath"
    "strconv"
    "strings"
    "time"
)

func isProcessRunning(processName string) (bool, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    entries, err := os.ReadDir("/proc")
    if err != nil {
        return false, err
    }

    for _, entry := range entries {
        select {
        case <-ctx.Done():
            return false, ctx.Err()
        default:
        }

        if pid, err := strconv.Atoi(entry.Name()); err == nil {
            statusFile := filepath.Join("/proc", strconv.Itoa(pid), "comm")
            running, err := checkProcessComm(ctx, statusFile, processName)
            if err != nil {
                continue
            }
            if running {
                return true, nil
            }
        }
    }
    return false, nil
}

func checkProcessComm(ctx context.Context, commFile, processName string) (bool, error) {
    file, err := os.Open(commFile)
    if err != nil {
        return false, err
    }
    defer file.Close()

    done := make(chan bool)
    go func() {
        scanner := bufio.NewScanner(file)
        if scanner.Scan() {
            name := strings.TrimSpace(scanner.Text())
            done <- (name == processName)
        } else {
            done <- false
        }
    }()

    select {
    case result := <-done:
        return result, nil
    case <-ctx.Done():
        return false, ctx.Err()
    }
}

func main() {
    running, err := isProcessRunning("your_process_name")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Process running: %t\n", running)
    }
}

方法2:直接读取/proc/[pid]/comm文件

comm文件包含进程的命令名,直接读取它比解析status文件更高效。

package main

import (
    "bufio"
    "os"
    "path/filepath"
    "strconv"
    "strings"
)

func isProcessRunningViaComm(processName string) (bool, error) {
    entries, err := os.ReadDir("/proc")
    if err != nil {
        return false, err
    }

    for _, entry := range entries {
        if pid, err := strconv.Atoi(entry.Name()); err == nil {
            commPath := filepath.Join("/proc", strconv.Itoa(pid), "comm")
            data, err := os.ReadFile(commPath)
            if err != nil {
                continue
            }
            name := strings.TrimSpace(string(data))
            if name == processName {
                return true, nil
            }
        }
    }
    return false, nil
}

func main() {
    running, err := isProcessRunningViaComm("your_process_name")
    if err != nil {
        panic(err)
    }
    println("Process running:", running)
}

关键点:

  • 使用os.ReadDir替代ioutil.ReadDir(已弃用)。
  • checkProcessComm中使用带超时的上下文,避免无限期阻塞。
  • 直接读取comm文件获取进程名,无需正则匹配。
  • 处理文件读取错误时继续循环,不中断整个检查。

这些方法减少了外部依赖,并提高了在/proc目录操作时的可靠性。

回到顶部