Go Linux网络名称空间意外行为

Go Linux网络名称空间意外行为,go,linux-kernel,kernel,ubuntu-14.04,linux-namespaces,Go,Linux Kernel,Kernel,Ubuntu 14.04,Linux Namespaces,所以我最近一直在玩弄网络名称空间。 我编写了一个简单的代码,构建了它,并注意到发生了一些非常奇怪的事情 代码如下: package main import ( "fmt" "log" "net" "os" "path" "syscall" ) const ( NsRunDir = "/var/run/netns" SelfNetNs = "/proc/self/ns/net" ) func main() { netN

所以我最近一直在玩弄网络名称空间。 我编写了一个简单的代码,构建了它,并注意到发生了一些非常奇怪的事情

代码如下:

package main

import (
    "fmt"
    "log"
    "net"
    "os"
    "path"
    "syscall"
)

const (
    NsRunDir  = "/var/run/netns"
    SelfNetNs = "/proc/self/ns/net"
)

func main() {
    netNsPath := path.Join(NsRunDir, "myns")
    os.Mkdir(NsRunDir, 0755)

    if err := syscall.Mount(NsRunDir, NsRunDir, "none", syscall.MS_BIND, ""); err != nil {
        log.Fatalf("Could not create Network namespace: %s", err)
    }

    fd, err := syscall.Open(netNsPath, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_EXCL, 0)
    if err != nil {
        log.Fatalf("Could not create Network namespace: %s", err)
    }
    syscall.Close(fd)

    if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil {
        log.Fatalf("Could not clone new Network namespace: %s", err)
    }

    if err := syscall.Mount(SelfNetNs, netNsPath, "none", syscall.MS_BIND, ""); err != nil {
        log.Fatalf("Could not Mount Network namespace: %s", err)
    }

    if err := syscall.Unmount(netNsPath, syscall.MNT_DETACH); err != nil {
        log.Fatalf("Could not Unmount new Network namespace: %s", err)
    }

    if err := syscall.Unlink(netNsPath); err != nil {
        log.Fatalf("Could not Unlink new Network namespace: %s", err)
    }

    ifcs, _ := net.Interfaces()
    for _, ifc := range ifcs {
        fmt.Printf("%#v\n", ifc)
    }
}
现在,当您在Trusty 14.04上运行此代码时,您将看到一些奇怪的事情发生。 当您连续多次运行二进制文件时,就会发生这种情况

有时它打印出所有主机的接口,有时它只是打印出一个环回接口,这意味着程序末尾的范围循环似乎在名称空间仍然附加时执行一次,有时在名称空间已经分离时执行一次

我完全搞不懂为什么会发生这种情况,但我认为这要么是我的代码,要么是我在程序执行或内核方面遗漏了一些东西

任何帮助都将不胜感激

谢谢

更新1: 因此,这种奇怪的行为似乎与golang如何跨操作系统线程调度GoRouting有关。因此,您需要确保能够很好地处理运行时。我的意思是,如果您将代码执行锁定到一个OS线程,您将获得一致的结果。可以通过添加以下运行时包语句来执行此操作:

runtime.LockOSThread()
然而,这仍然不能解决我的问题,但现在我认为这一切都归结于对名称空间的理解。我需要进一步调查

更新2:
为了让您了解为什么在运行一堆系统调用时应该使用上面的OS线程锁,并体验类似的奇怪的正确行为,请阅读本文。它描述了运行时和go调度程序。它是为go 1.1编写的,但它仍然提供了非常好的概述。

我只知道tip是否仍然存在这种情况。从今天开始,在go v1.3rc2 tip上,它只打印lo,确实运行了20次,在arch linux上运行内核3.15。