【问题标题】:How to set memory limit to a process in Golang如何在 Golang 中为进程设置内存限制
【发布时间】:2014-07-02 04:07:28
【问题描述】:

我使用 syscall prlimit 来设置进程的资源限制,它适用于限制 CPU 时间,但是在测试内存使用情况时,我遇到了问题。

package sandbox

import (
    "syscall"
    "unsafe"
)

func prLimit(pid int, limit uintptr, rlimit *syscall.Rlimit) error {
    _, _, errno := syscall.RawSyscall6(syscall.SYS_PRLIMIT64, uintptr(pid), limit, uintptr(unsafe.Pointer(rlimit)), 0, 0, 0)
    var err error
    if errno != 0 {
        err = errno
        return err
    } else {
        return nil
    }
}

这是我的测试。

func TestMemoryLimit(t *testing.T) {
    proc, err := os.StartProcess("test/memo", []string{"memo"}, &os.ProcAttr{})
    if err != nil {
        panic(err)
    }
    defer proc.Kill()
    var rlimit syscall.Rlimit
    rlimit.Cur = 10
    rlimit.Max = 10 + 1024
    prLimit(proc.Pid, syscall.RLIMIT_DATA, &rlimit)
    status, err := proc.Wait()
    if status.Success() {
        t.Fatal("memory test failed")
    }
}

这是备忘录:

package main

func main() {
    var a [10000][]int
    for i := 0; i < 1000; i++ {
        a[i] = make([]int, 1024)
    }
}

我做了大量的内存,只设置了10个字节的内存,但它不会以任何方式发出段错误信号。

【问题讨论】:

    标签: unix memory go


    【解决方案1】:

    RLIMIT_DATA 描述进程数据段的最大大小。传统上,分配内存的程序会通过调用brk() 来扩大数据段以从操作系统分配内存。

    Go 不使用这种方法。相反,它使用mmap() 系统调用的变体来请求地址空间中任何位置的内存区域。这比基于brk() 的方法灵活得多,因为您可以使用munmap() 释放任意内存区域,而基于brk() 的方法只能从数据段的末尾释放内存。

    这样做的结果是RLIMIT_DATA 在控制进程使用的内存量方面无效。请尝试改用RLIMIT_AS,但请注意,此限制还包含您用于文件映射的地址空间,尤其是在共享库的情况下。

    【讨论】:

    • 如果我想要更通用的用途。那么c和其他语言呢?
    • 它只是以代码2退出,我如何跟踪信号,或与其他退出代码区分开来?
    • @ggaaooppeenngg RLIMIT_DATA 机制也适用于 C 程序。我不明白你的第二个问题。
    • 我使用 ptrace 来跟踪进程,在 ptrace 的循环中,我检查了我启动的进程的信号。如果我设置了 RLIMIT_DATA,并且进程超过了限制,它将发出段错误信号(SIGSEGV),如果设置了 RLIMIT_AS,它只是强制退出。我读了man setrlimit。它说它将返回 ENOMEM ,并且只有当堆栈超过限制时,它才会发出 SIGSEGV 信号。我如何找到关于 ENOMEM 的定义,似乎我的测试以代码 2 或 137 退出,但我不知道退出代码的含义。
    • @ggaaooppeenngg 显然,如果内存耗尽并且您没有捕捉到由此产生的恐慌,Go 运行时会终止一个退出状态为 2 的进程。 ENOMEM 是内存分配系统调用在没有可用内存时产生的错误状态(Error no memory)。
    【解决方案2】:

    有一个提案Soft memory limit,可能会在go 1.18之后发布

    此选项有两种形式:一个名为 SetMemoryLimit 的新 runtime/debug 函数和一个 GOMEMLIMIT 环境变量。总之,运行时将尝试通过限制堆的大小以及更积极地将内存返回给底层平台来维持这个内存限制。这包括帮助减轻垃圾收集死亡螺旋的机制。最后,通过设置 GOGC=off,Go 运行时将始终将堆增长到完整的内存限制。

    这个新选项使应用程序可以更好地控制其资源经济性。它使用户能够:

    • 更好地利用他们已有的内存,
    • 自信地降低他们的内存限制,知道 Go 会尊重他们,
    • 避免不支持的垃圾收集调整形式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-18
      • 2010-09-16
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      相关资源
      最近更新 更多