だから私は最近ネットワーク名前空間をいじっています。簡単なコードをまとめてビルドしたところ、非常に奇妙なことが起こっていることに気付きました。
コードは次のとおりです。
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 回実行されているように見えたり、すでにデタッチされているときに実行されているように見えることを意味します。
なぜこれが起こっているのか完全に混乱していますが、それは私のコードであるか、プログラムの実行やカーネルに関するものを見逃しているだけだと思います。
どんな助けでも大歓迎です。
ありがとう
Update1: したがって、「奇妙な」動作は、golang が OS スレッド間で goroutine をスケジュールする方法に関係しているようです。そのため、ランタイムを適切に処理する必要があります。つまり、コードの実行を 1 つの OS スレッドに固定すると、一貫した結果が得られるということです。これを行うには、次のランタイム パッケージ ステートメントを追加します。
runtime.LockOSThread()
ただし、これでも問題は解決しませんが、名前空間を理解することがすべてだと思います。私はそれをもっと調べる必要があります。
Update2: システム コールを多数実行しているときに上記の OS スレッド ロックを使用し、正しい動作の同様の「奇妙さ」を経験する理由についてもう少し詳しく説明するには、このブログ投稿を読んでください。ランタイムと go スケジューラについて説明します。これは go 1.1 用に書かれていますが、それでも非常に優れた概要を示しています。