Пространство имен Linux Network неожиданное поведение
Так что я недавно поиграл с сетевыми пространствами имен. Я собрал простой код, собрал его и заметил нечто очень странное.
Код выглядит следующим образом:
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 распределяет программы между потоками ОС. Поэтому вам нужно убедиться, что вы хорошо справляетесь со временем выполнения. Под этим я подразумеваю, что если вы заблокируете выполнение кода в одном потоке ОС, вы получите согласованные результаты. Вы можете сделать это, добавив следующий оператор пакета времени выполнения:
runtime.LockOSThread()
Однако это все еще не решает мою проблему, но теперь я думаю, что все сводится к пониманию пространств имен. Мне нужно больше посмотреть на это.
Обновление 2: чтобы дать вам немного больше смысла в том, почему вы должны использовать вышеупомянутую блокировку потока ОС при запуске группы системных вызовов и испытывать подобную "странность" правильного поведения, прочитайте эту статью в блоге. Описывает время выполнения и идут планировщики. Он был написан для Go 1.1, но он все еще дает очень хороший обзор.