英文:
Go `defer` does not behave as expected when Unmount
问题
以下是您提供的代码的翻译:
这是我的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("缺少命令")
return
}
switch os.Args[1] {
case "run":
run()
case "child":
child()
default:
logrus.Errorf("错误的命令")
return
}
}
func run() {
logrus.Info("设置中...")
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("运行 %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)
这个结果是正常的。
但是,当我将child函数更改为以下内容时:
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()
之前被调用。感谢您的帮助。
英文:
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.
答案1
得分: 2
你正在推迟调用check
函数,但它的参数会立即求值,这意味着syscall.Unmount
函数不会被推迟执行。请参考https://golang.org/ref/spec#Defer_statements
每次执行“defer”语句时,函数值和调用的参数会像平常一样被求值并重新保存,但实际的函数不会被调用。相反,推迟执行的函数会在包围它的函数返回之前立即被调用,按照它们被推迟的相反顺序执行。如果推迟执行的函数值为nil,则在调用函数时会引发panic,而不是在执行“defer”语句时。
如果无法摆脱check调用,请使用匿名函数:
defer func() { check(syscall.Unmount("proc", 0)) }()
defer func() { check(syscall.Unmount("temp", 0)) }()
英文:
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)) }()
答案2
得分: 1
《Go编程语言规范》
延迟语句
每次执行"defer"语句时,函数值和调用的参数会像平常一样被求值并保存,但实际的函数不会被调用。相反,延迟函数会在包围它的函数返回之前立即被调用,按照它们被延迟的相反顺序。
对于以下代码:
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())
对于你的第二个问题,"在调用cmd.Run()
之前,文件系统被卸载","每次执行"defer"语句时,函数值和调用的参数会像平常一样被求值并保存,但实际的函数不会被调用。"
英文:
> The Go Programming Language Specification
>
> 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.
For
check(cmd.Run())
check(syscall.Unmount("proc", 0))
check(syscall.Unmount("temp", 0))
defer in reverse order
defer check(syscall.Unmount("temp", 0))
defer check(syscall.Unmount("proc", 0))
check(cmd.Run())
For your second problem, "the file systems are unmounted before cmd.Run()
is invoked ", "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."
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论