卸载时,“延迟”不会按预期运行

时间:2017-09-03 03:01:13

标签: go

这是我的main.go,我使用go run main.go run sh创建一个在其中运行shell的进程。

package main

import (
    "io/ioutil"
    "os"
    "os/exec"
    "path/filepath"
    "strconv"
    "syscall"

    "github.com/sirupsen/logrus"
)

func main() {
    if len(os.Args) < 2 {
        logrus.Errorf("missing commands")
        return
    }
    switch os.Args[1] {
    case "run":
        run()
    case "child":
        child()
    default:
        logrus.Errorf("wrong command")
        return
    }
}

func run() {
    logrus.Info("Setting up...")
    cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
    }
    check(cmd.Run())
}

func child() {
    logrus.Infof("Running %v", os.Args[2:])
    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    check(syscall.Sethostname([]byte("newhost")))
    check(syscall.Chroot("/root/busybox"))
    check(os.Chdir("/"))
    check(syscall.Mount("proc", "proc", "proc", 0, ""))
    check(syscall.Mount("tempdir", "temp", "tmpfs", 0, ""))
    check(cmd.Run())
    check(syscall.Unmount("proc", 0))
    check(syscall.Unmount("temp", 0))
}

func check(err error) {
    if err != nil {
        logrus.Errorln(err)
    }
}

当我在新shell中运行mount时,它会返回

proc on /proc type proc (rw,relatime)
tempdir on /temp type tmpfs (rw,relatime) 

只是工作正常。

但是当我将子功能改为

func child() {
    ...
    check(os.Chdir("/"))
    check(syscall.Mount("proc", "proc", "proc", 0, ""))
    check(syscall.Mount("tempdir", "temp", "tmpfs", 0, ""))
    defer check(syscall.Unmount("proc", 0))
    defer check(syscall.Unmount("temp", 0))
    check(cmd.Run())
}

再次运行mount,它会返回mount: no /proc/mounts

defer用于将函数推迟到外部函数的末尾。但似乎syscall.Umount()之前调用了cmd.Run()。谢谢你的帮助。

2 个答案:

答案 0 :(得分:2)

defer check(syscall.Unmount("proc", 0))
defer check(syscall.Unmount("temp", 0))

您正在推迟check - 调用,但会立即评估其参数,这意味着,syscall.Unmount不会被推迟。见https://golang.org/ref/spec#Defer_statements

  

每次&#34;推迟&#34;语句执行,函数值和   调用的参数将照常计算并重新保存但是   不调用实际函数。相反,延迟函数是   在周围函数返回之前立即调用   相反的顺序他们被推迟了。如果是延迟函数值   求值为nil,在调用函数时执行恐慌,而不是   什么时候推迟&#34;声明已经执行。

如果您无法取消支票,请使用匿名功能:

defer func() { check(syscall.Unmount("proc", 0)) }()
defer func() { check(syscall.Unmount("temp", 0)) }()

答案 1 :(得分:1)

  

The Go Programming Language Specification

     

Defer statements

     

每次&#34;推迟&#34;语句执行,函数值和   呼叫的参数照常评估并重新保存,但是   不调用实际函数。代替,   在周围之前立即调用延迟函数   函数返回,按逆序延迟。

有关

check(cmd.Run())
check(syscall.Unmount("proc", 0))
check(syscall.Unmount("temp", 0))

按相反顺序推迟

defer check(syscall.Unmount("temp", 0))
defer check(syscall.Unmount("proc", 0))
check(cmd.Run())

对于您的第二个问题,&#34;文件系统在调用cmd.Run()之前卸载&#34;,&#34;每次&#34;延迟&#34;语句执行时,函数值和调用参数将照常计算并重新保存,但不会调用实际函数。&#34;