代码之家  ›  专栏  ›  技术社区  ›  tgogos

Golang-按名称终止进程

  •  9
  • tgogos  · 技术社区  · 9 年前

    如果您只知道进程名称,那么使用Go代码终止进程的有效方法是什么?我看到了 os 包装如下:

    func FindProcess(pid int) (*Process, error)
    func (p *Process) Kill() error
    func (p *Process) Signal(sig Signal) error
    

    是否有良好/常见的做法来获得 pid 而不必执行命令,然后解析输出?

    我找到了一种方法,使用如下命令返回pid:

    • echo $(ps cax | grep myapp | grep -o '^[ ]*[0-9]*')

    我有 used it with exec.Command() 但如果有更好的方法,我想避免这种情况。

    5 回复  |  直到 9 年前
        1
  •  8
  •   Peter Gloor    9 年前

    运行外部命令可能是最好的方法。但是,只要您是要杀死的进程的所有者,以下代码就可以在Ubuntu上运行。

    // killprocess project main.go
    package main
    
    import (
        "bytes"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "os"
        "path/filepath"
        "strconv"
        "strings"
    )
    
    // args holds the commandline args
    var args []string
    
    // findAndKillProcess walks iterative through the /process directory tree
    // looking up the process name found in each /proc/<pid>/status file. If
    // the name matches the name in the argument the process with the corresponding
    // <pid> will be killed.
    func findAndKillProcess(path string, info os.FileInfo, err error) error {
        // We just return in case of errors, as they are likely due to insufficient
        // privileges. We shouldn't get any errors for accessing the information we
        // are interested in. Run as root (sudo) and log the error, in case you want
        // this information.
        if err != nil {
            // log.Println(err)
            return nil
        }
    
        // We are only interested in files with a path looking like /proc/<pid>/status.
        if strings.Count(path, "/") == 3 {
            if strings.Contains(path, "/status") {
    
                // Let's extract the middle part of the path with the <pid> and
                // convert the <pid> into an integer. Log an error if it fails.
                pid, err := strconv.Atoi(path[6:strings.LastIndex(path, "/")])
                if err != nil {
                    log.Println(err)
                    return nil
                }
    
                // The status file contains the name of the process in its first line.
                // The line looks like "Name: theProcess".
                // Log an error in case we cant read the file.
                f, err := ioutil.ReadFile(path)
                if err != nil {
                    log.Println(err)
                    return nil
                }
    
                // Extract the process name from within the first line in the buffer
                name := string(f[6:bytes.IndexByte(f, '\n')])
    
                if name == args[1] {
                    fmt.Printf("PID: %d, Name: %s will be killed.\n", pid, name)
                    proc, err := os.FindProcess(pid)
                    if err != nil {
                        log.Println(err)
                    }
                    // Kill the process
                    proc.Kill()
    
                    // Let's return a fake error to abort the walk through the
                    // rest of the /proc directory tree
                    return io.EOF
                }
    
            }
        }
    
        return nil
    }
    
    // main is the entry point of any go application
    func main() {
        args = os.Args
        if len(args) != 2 {
            log.Fatalln("Usage: killprocess <processname>")
        }
        fmt.Printf("trying to kill process \"%s\"\n", args[1])
    
        err := filepath.Walk("/proc", findAndKillProcess)
        if err != nil {
            if err == io.EOF {
                // Not an error, just a signal when we are done
                err = nil
            } else {
                log.Fatal(err)
            }
        }
    }
    

        2
  •  6
  •   tgogos    8 年前

    我最终使用了如下内容:

    // `echo "sudo_password" | sudo -S [command]`
    // is used in order to run the command with `sudo`
    
    _, err := exec.Command("sh", "-c", "echo '"+ sudopassword +"' | sudo -S pkill -SIGINT my_app_name").Output()
    
    if err != nil {
        // ...
    } else {
        // ...
    }
    

    我用了 SIGINT 发出信号以优雅地停止应用程序。

    从…起 wikipedia :

    • 信号

      当用户希望中断进程时,SIGINT信号由其控制终端发送到进程。这通常通过按Ctrl+C启动,但在某些系统上,可以使用“删除”字符或“中断”键。

    • SIGKILL

      SIGKILL信号被发送到进程以使其立即终止(kill)。与SIGTERM和SIGINT相反,该信号不能被捕获或忽略,并且接收过程在接收到该信号时不能执行任何清除。以下例外适用:

        3
  •  6
  •   Clark McCauley MacGyver    5 年前

    跨平台(第三方)解决方案

    几个月来,我已经实施了各种解决方案来实现这一点,出于某种原因,我花了很长时间才找到 gopsutil .这是一个第三方库,可能会或可能不会破坏您的交易,但它在我们的跨平台项目中完美运行。下面的示例将终止具有匹配名称的第一个进程,但它可以很容易地调整为终止具有该名称的所有进程。

    import "github.com/shirou/gopsutil/v3/process"
    
    func KillProcess(name string) error {
        processes, err := process.Processes()
        if err != nil {
            return err
        }
        for _, p := range processes {
            n, err := p.Name()
            if err != nil {
                return err
            }
            if n == name {
                return p.Kill()
            }
        }
        return fmt.Errorf("process not found")
    }
    

    作为一个额外的好处,该库还支持所有流程相关操作的上下文取消,包括流程查询和终止流程。

    func KillAllProcessesCtx(ctx context.Context, name string) error {
        processes, err := process.ProcessesWithContext(ctx)
        if err != nil {
            return err
        }
        for _, p := range processes {
            n, err := p.NameWithContext(ctx)
            if err != nil {
                return err
            }
            if n == name {
                err = p.KillWithContext(ctx)
                if err != nil {
                    return err
                }
            }
        }
        return nil
    }
    

    优美终止

    库还通过向进程发送自己的信号来支持优雅终止。

    // Do this
    err = p.SendSignal(syscall.SIGINT)
            
    // Instead of this
    err = p.Kill()
    
        4
  •  0
  •   Zombo tliff    5 年前

    您已经可以使用Go按进程ID终止进程,所以真正的问题是 这里是从进程名称中获取进程ID。下面是一个例子 窗户:

    package main
    
    import (
       "fmt"
       "golang.org/x/sys/windows"
    )
    
    // unsafe.Sizeof(windows.ProcessEntry32{})
    const processEntrySize = 568
    
    func processID(name string) (uint32, error) {
       h, e := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
       if e != nil { return 0, e }
       p := windows.ProcessEntry32{Size: processEntrySize}
       for {
          e := windows.Process32Next(h, &p)
          if e != nil { return 0, e }
          if windows.UTF16ToString(p.ExeFile[:]) == name {
             return p.ProcessID, nil
          }
       }
       return 0, fmt.Errorf("%q not found", name)
    }
    
    func main() {
       n, e := processID("WindowsTerminal.exe")
       if e != nil {
          panic(e)
       }
       println(n)
    }
    

    https://pkg.go.dev/golang.org/x/sys/windows#CreateToolhelp32Snapshot

        5
  •  0
  •   RDX    4 年前

    对于Windows:

    您可以使用下面的方法。传递要终止的进程名称。

    func killProcessByName(procname string) int {
        kill := exec.Command("taskkill", "/im", procname, "/T", "/F")
        err := kill.Run()
        if err != nil {
            return -1
        }
        return 0
    }
    

    裁判: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/taskkill