TL;DR 最近想看 docker
相关的实现,自然涉及底层 namespace
, 所以边做实验边看源码,感兴趣的先看耗子叔的文章
测试案例
uts namespace
主要是用来隔离主机名,但不只这一个功能。先上小例子,也是来自耗子叔
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mount.h>
/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};
int container_main(void* arg)
{
printf("Container - inside the container!\n");
sethostname("my-container",10); /* 设置hostname */
/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
}
int main()
{
printf("Parent - start a container!\n");
/* 调用clone函数,其中传出一个函数,还有一个栈空间的(为什么传尾指针,因为栈是反着的) */
int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD|CLONE_NEWUTS, NULL);
/* 等待子进程结束 */
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}
和上一篇测试 mount ns
不同,本次 flags 只设置了 CLONE_NEWUTS
, 然后在子进程中调用 sethostname
设置容器的主机名。
root@iZhp36ik63t96xhzjh00ujZ:~# gcc uts.c && ./a.out
Parent - start a container!
Container - inside the container!
root@my-contain:~# pwd
/root
root@my-contain:~# exit
exit
Parent - container stopped!
root@iZhp36ik63t96xhzjh00ujZ:~#
root@iZhp36ik63t96xhzjh00ujZ:~#
root@iZhp36ik63t96xhzjh00ujZ:~# pwd
/root
编绎并运行,可以看到容器内主机名己变,退出后宿主机主机名并没有改变。
创建 uts namespace
struct ns_common {
atomic_long_t stashed;
const struct proc_ns_operations *ops;
unsigned int inum;
};
struct new_utsname {
char sysname[__NEW_UTS_LEN + 1];
char nodename[__NEW_UTS_LEN + 1];
char release[__NEW_UTS_LEN + 1];
char version[__NEW_UTS_LEN + 1];
char machine[__NEW_UTS_LEN + 1];
char domainname[__NEW_UTS_LEN + 1];
};
struct uts_namespace {
struct kref kref; // 引用计数
struct new_utsname name;
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct ns_common ns;
} __randomize_layout;
核心结构体是 uts_namespace
,创建的过程和前文讲的 clone
调用相关,来自 copy_utsname
. 另外可以看到 new_utsname
构构体内容是 uts ns
隔离的所有内容,不仅有主机名。
struct uts_namespace *copy_utsname(unsigned long flags,
struct user_namespace *user_ns, struct uts_namespace *old_ns)
{
struct uts_namespace *new_ns;
BUG_ON(!old_ns);
get_uts_ns(old_ns);
if (!(flags & CLONE_NEWUTS))
return old_ns;
new_ns = clone_uts_ns(user_ns, old_ns);
put_uts_ns(old_ns);
return new_ns;
}
如果没有参数 CLONE_NEWUTS
, 那就使用旧的 uts, 否则调用 clone_uts_ns
复制一份。
static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
struct uts_namespace *old_ns)
{
struct uts_namespace *ns;
struct ucounts *ucounts;
int err;
err = -ENOSPC;
ucounts = inc_uts_namespaces(user_ns);
if (!ucounts)
goto fail;
err = -ENOMEM;
ns = create_uts_ns();
if (!ns)
goto fail_dec;
err = ns_alloc_inum(&ns->ns);
if (err)
goto fail_free;
ns->ucounts = ucounts;
ns->ns.ops = &utsns_operations;
down_read(&uts_sem);
memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
ns->user_ns = get_user_ns(user_ns);
up_read(&uts_sem);
return ns;
......
}
这块代码更简单,主要是设置 uts ns 的回调函数结构体 utsns_operations
,然后再将 old_ns.new_utsname
复制给新的 uts_ns
,此段函数就完成了。
设置主机名
当容器启动后,uts namespace
己经隔离了,此时再设置主机名会与宿主机互不影响。先看下 hostname
系统调用
~# strace hostname
......
uname({sysname="Linux", nodename="my-contain", ...}) = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
write(1, "my-contain\n", 11my-contain
) = 11
exit_group(0) = ?
+++ exited with 0 +++
省去无关信息,原来 hostname
是调用 uname
来获取的主机名等所有信息
SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
{
struct old_utsname tmp;
if (!name)
return -EFAULT;
down_read(&uts_sem);
memcpy(&tmp, utsname(), sizeof(tmp));
up_read(&uts_sem);
if (copy_to_user(name, &tmp, sizeof(tmp)))
return -EFAULT;
if (override_release(name->release, sizeof(name->release)))
return -EFAULT;
if (override_architecture(name))
return -EFAULT;
return 0;
}
static inline struct new_utsname *utsname(void)
{
return ¤t->nsproxy->uts_ns->name;
}
代码也非常简单,通过 utsname
获取当前进程的 uts namespace
, 然后把 utsname
结构体复制一份到用户空间。后面两个 override 忽略,为了兼容老的版本。其中 old_utsname
和 new_utsname
虽然是两个结构体,但是字段长度大小是一样的,只是为了去掉 hardcode 重新定义一个。
小结
现在看 uts ns
是最简单的一个,比较容易理解。
网友评论