10

以下(正确)不起作用:

package main

import (
        "os"
        "time"
)

func main() {
        os.Args[0] = "custom name"
        println("sleeping")
        time.Sleep(1000 * time.Second)
        println("done")
}

某些语言提供了将进程名称设置为内置功能(例如,在 Ruby 中,只需分配给 $0)或第三方库(Python)的这种特性。

我正在寻找一种至少在 Linux 上有效的解决方案。

4

3 回答 3

13

有多种方法可以实现这一点,其中许多仅在某些情况下有效。我真的不建议这样做,因为(一方面)它可能会导致您的流程在不同情况下出现不同的名称。它们需要使用系统调用和/或不安全,因此您故意破坏 Go 语言的安全性。尽管如此,您的选择似乎是:

修改 argv[0]

func SetProcessName(name string) error {
    argv0str := (*reflect.StringHeader)(unsafe.Pointer(&os.Args[0]))
    argv0 := (*[1 << 30]byte)(unsafe.Pointer(argv0str.Data))[:argv0str.Len]

    n := copy(argv0, name)
    if n < len(argv0) {
            argv0[n] = 0
    }

    return nil
}

在 Go 中,您无法访问实际的 argv 数组本身(无需调用内部运行时函数),因此您只能使用不超过当前进程名称长度的新名称。

这似乎主要适用于达尔文和 Linux。

致电 PR_SET_NAME

func SetProcessName(name string) error {
    bytes := append([]byte(name), 0)
    ptr := unsafe.Pointer(&bytes[0])
    if _, _, errno := syscall.RawSyscall6(syscall.SYS_PRCTL, syscall.PR_SET_NAME, uintptr(ptr), 0, 0, 0, 0); errno != 0 {
            return syscall.Errno(errno)
    }
    return nil
}

新名称最多为 16 个字节。

这在 Darwin 上不起作用,并且在 Linux 上似乎没有多大作用,尽管它成功并且 PR_GET_NAME 之后报告了正确的名称。不过,这可能是我的 Linux VM 的一些特殊之处。

于 2013-02-18T18:39:50.037 回答
4

要在 Linux 上更改进程名称,您需要结合使用prctl 系统调用PR_SET_NAME选项。

目前,我认为你不能在 Go 代码中做到这一点。但是,您可以构建一个小的 C 模块来执行此操作,然后将其集成到您的 Go 构建中。

于 2013-02-17T21:34:37.160 回答
0

我不认为“流程标题”是一个定义明确的术语。无论如何,Ruby 与 Go 有什么关系?os.Args的文档没有提到任何“进程标题”,也没有说在分配给切片项目时会发生任何魔术。后者实际上是 Go 的一般属性。结构字段、数组/切片项的变量没有神奇的 getter/setter,所以一个简单的赋值只是简单地赋值,什么也不做,也不能做更多的事情。

简而言之,缺乏魔法是预期的正确行为。

对于通过“os”包处理可移植访问的进程属性以外的进程属性,必须以特定于平台的方式使用包“syscall”。但是随后构建约束(在此处讨论)可以帮助正确处理跨平台的内容。

于 2013-02-17T21:21:42.780 回答