我们可以用以下几个步骤来实验下 UTS Namespace 功能:

创建 UTS Namespace

利用 clone() 创建一个新进程,同时创建一个 UTS 类型的 namespace,新进程中修改主机名,此时新进程可使用与全局不一样的主机名:

1
2
3
4
5
6
7
8
# hostname  # 全局系统的 hostname
k8s-test11
# ./new_uts
parent 21963 start up
parent => start a child process
parent => child process 21964 is started
# hostname  # 子进程中使用了 UTS namespace,hostname 已被改变
new_uts

执行完 new_uts 后,当前 shell 其实已经转入子进程的 shell 中,运行 uname 命令将看到 hostname 已经被更改为 new_utsnew_uts 的代码片段如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
...
static char child_stack[STACK_SIZE];

char* const child_args[] = {
    "/bin/bash",
    NULL
};

int child_main(void *args) {
    ...
    sethostname("new_uts", 9);
    execv(child_args[0], child_args);
    ...
}

int main(void) {
    ...
    int child_pid = clone(child_main, child_stack+STACK_SIZE, CLONE_NEWUTS|SIGCHLD, NULL);
    waitpid(child_pid, NULL, 0);
    ...
}

详细可参考 new_uts.c

查看 /proc 信息

查看父进程 20762 和子进程 20763 的 /proc 信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# ls -l /proc/21963/ns
total 0
lrwxrwxrwx 1 root root 0 Apr 15 12:22 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 net -> net:[4026531993]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 pid_for_children -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 uts -> uts:[4026531838]
		  
# ls -l /proc/21964/ns
total 0
lrwxrwxrwx 1 root root 0 Apr 15 12:22 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 net -> net:[4026531993]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 pid_for_children -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 15 12:22 uts -> uts:[4026532301]

/proc 上可看到,父子进程除了 uts 的 namespace 号不一样外,其他的 namespace 均一致。也就是说,父子进程除了分别使用各自的 UTS namespace 外,其他类型 namespace 仍处于同一集合中;

将新进程加入到 UTS Namespace 中

利用 setns() 将当前进程加入到已创建子进程(上文中 pid 为 21964 的进程)的 UTS namespace 中,其中 ns_exec 的代码片段如下所示:

1
2
3
4
5
6
7
8
9
...
int main(void) {
    ...
    fd = open(argv[1], O_RDONLY); // 获取 /proc 下对应进程的 namespace 文件
    setns(fd, 0);                 // 标记 0 代表是 UTS Namespace
    execvp(argv[2], &argv[2]);    // 我们将会让其执行 /bin/bash
    ...
}
...

详细代码可参考代码 ns_exec.c):

1
2
3
4
5
6
7
# hostname # 全局系统的 hostname
k8s-test11
   	 
# ./ns_exec /proc/21964/ns/uts /bin/bash    # 将启动进程加入到
process 26018 start up                      # 21964 的 uts namespace 中
# hostname
new_uts

此时我们查看 26018 的 /proc 信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# ls -l /proc/26018/ns/
total 0
lrwxrwxrwx 1 root root 0 Apr 15 12:41 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 net -> net:[4026531993]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 pid_for_children -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 15 12:41 uts -> uts:[4026532301]

可以看到 21964 和 26018 的 uts namespace 号是一样的,即这两个进程同属于一个 uts namespace。