linux之namespace解读

最近基于linux容器技术的docker很是火热,作为做云计算方面探究的程序屌当然不能错过,以前研究过 cloudfoundry自身的容器warden实现的大致过程,当时也没有把一些收获记录下来,这次借docker的势头, 重新回去研究一下基于cgroup的容器技术。

linux容器的底层原理都是基于cgroup的,docker是使用go语言来编写的,而且封装的比较高级,所以底层 一些的LXC容器更容易去接近cgroup的实现。cgroup之所以能够做到资源隔离的效果,当然和linux内核提供 的namespace机制是分不开的。

何为namespace?

我们在写c++代码的时候一般都会有这么一句话using namespace std;这样可以使我们在调用标准库函数 的时候免去函数所在的域如std::cout,这使得cout函数调用不会产生歧义,c++使用namespace来杜绝函数 或者变量带来的名称冲突问题。

而linux的namespace可以被理解为进程的命名空间划分,使得不同namespace空间下面的进程处于几乎完全 隔离的状况,它们之间是完全透明的,linux container就是把各个不同的进程组设置在不同的namespace 下来实现他们的互不干扰。我们日常所使用的机器处于全局的namespace下,所以我们并不能感觉到其存在, 而当进入到一个linux容器后,感觉在另一台机器上操作一样。

linux容器技术与传统的虚拟化技术有很大差别,一台宿主机上的各个容器更像是一个个独立的进程组,这些 进程组去共享宿主机的cpu,内存,网卡等硬件资源,所以linux container是操作系统级的虚拟化技术。

namespace的作用理清了,那么怎么去创建一个namespace呢,这就需要clone这个系统调用。

clone系统调用

通过字面判断是一个进程clone出令一个进程,而我们可以通过参数来控制这两个进程属于不同的namespace, clone函数原型:

 int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

 int (*fn)(void *)这是子进程的执行函数循环
 void *child_stack 可以通过这个参数指定子进程的运行栈
 void *arg 父进程传递给子进程的参数

clone的参数中能将父子进程放进两个namespace的就是int flags,我们来引用cloudfoundry源码中的一段 代码如下:

 long pagesize;
 void *stack;
 int flags = 0;
 pid_t pid;

 pagesize = sysconf(_SC_PAGESIZE);
 stack = alloca(pagesize);
 assert(stack != NULL);

 /* Point to top of stack (it grows down) */
 stack = stack + pagesize;

 /* Setup namespaces */
 flags |= CLONE_NEWIPC;
 flags |= CLONE_NEWNET;
 flags |= CLONE_NEWNS;
 flags |= CLONE_NEWPID;
 flags |= CLONE_NEWUTS;

 pid = clone(child_run, stack, flags, w); 

从这段代码中可以将clone这个系统调用解释的很清楚:

 CLONE_NEWIPC 新的IPC namespace,这就意味着不同namepsace下的进程不能直接通信
 CLONE_NEWNET 新的network namespace, 将各个容器的network分离
 CLONE_NEWNS  新的mount namespace,新进程的文件系统将独立
 CLONE_NEWPID 新的PID namespace,新的PID空间
 CLONE_NEWUTS 新的UTS namespace,生成新的uname返回值

前面已经说过linux容器是一个个互不干扰的进程组,通过clone出来的进程将成为容器的第一个进程,这 与普通的fork是完全不同的。



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories linux  tagged with linux