dousi0144 2017-09-03 03:01
浏览 12
已采纳

卸载时,Go`defer`的行为不符合预期

Here is my main.go, and I use go run main.go run sh to create a process which runs shell in it.

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)
    }
}

When I run mount in the new shell, it returns

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

that just works fine.

But when I change the child function into

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())
}

and run mount again, it returns mount: no /proc/mounts.

defer is illustrated to defer the function after it to the end of the outer function. But it seems that syscall.Umount() is invoked before cmd.Run(). Thanks for your help.

  • 写回答

2条回答 默认 最新

  • duankezong4064 2017-09-03 05:16
    关注
    defer check(syscall.Unmount("proc", 0))
    defer check(syscall.Unmount("temp", 0))
    

    You are defering the check-call, but it's arguments are evaluated immediately, which means, syscall.Unmount is not defered. See https://golang.org/ref/spec#Defer_statements

    Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.

    Use a anonymous function if you can't get rid of the check-call:

    defer func() { check(syscall.Unmount("proc", 0)) }()
    defer func() { check(syscall.Unmount("temp", 0)) }()
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 关于大棚监测的pcb板设计
  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
  • ¥15 Vue3地图和异步函数使用
  • ¥15 C++ yoloV5改写遇到的问题