一直对linux内核的namespace感到困惑,今天看了一下代码才知道,原来所谓的namespace其实就是给虚拟化用的,PID namespace其实就是建立一个新的PID空间,这样内部可以使用一套新的PID,而且不会和外部冲突。这也就是说某个进程其实会有两个PID,一个空间一个。
我写了段C代码来展示这个问题。
/*
* gcc namespace.c -o ns
*
root@ubuntu-9.04# ./ns 3
Out of the container, my pid is: 15169
In the container, my pid is: 1
pid of my child is 2
cpid: 15170
Parent sleeping 20 seconds
pid of my child is 3
pid of my child is 4
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sched.h>
#include <linux/sched.h>
static int fork_child(void *arg)
{
int a = (int)arg;
int i;
pid_t pid;
printf("In the container, my pid is: %d\n", getpid());
for (i = 0; i <a; i++) {
pid = fork();
if (pid <0)
return pid;
else if (pid)
printf("pid of my child is %d\n", pid);
else if (pid == 0) {
sleep(3);
exit(0);
}
}
return 0;
}
int main(int argc, char *argv[])
{
int cpid;
void *childstack, *stack;
int flags;
int ret = 0;
int stacksize = getpagesize() * 4;
if (argc != 2) {
fprintf(stderr, "Wrong usage.\n");
return -1;
}
stack = malloc(stacksize);
if (!stack) {
perror("malloc");
return -1;
}
printf("Out of the container, my pid is: %d\n", getpid());
childstack = stack + stacksize;
flags = CLONE_NEWPID | CLONE_NEWNS;
cpid = clone(fork_child, childstack, flags, (void *)atoi(argv[1]));
printf("cpid: %d\n", cpid);
if (cpid <0) {
perror("clone");
ret = -1;
goto out;
}
fprintf(stderr, "Parent sleeping 20 seconds\n");
sleep(20);
ret = 0;
out:
free(stack);
return ret;
}
其实被namespace化的不只是PID,还有很多东西,貌似它们合起来被称为container。可以看 include/linux/nsproxy.h:
struct nsproxy {
atomic_t count;
struct uts_namespace *uts_ns;
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns;
struct net *net_ns;
};
openvz基础应该是Linux kernel namespace,进程被隔离在各自的namespace,因此额外开销很小,只能叫隔离容器,算不得虚拟机。有空要读一这个系统源码
http://openvz.org/WP/What_are_containers
http://www.cnblogs.com/lisperl/archive/2012/05/03/2480316.html
Linux Namespaces 机制提供一种资源隔离方案。PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace。每个Namespace里面的 资源对其他Namespace都是透明的。要创建新的Namespace,只需要在调用clone时指定相应的flag。 Linux Namespaces机制为实现基于容器的虚拟化技术提供了很好的基础,LXC(Linux containers)就是利用这一特性实现了 资源的隔离。不同container内的进程属于不同的Namespace,彼此透明,互不干扰。下面我们就从clone系统调用的flag出发,来介绍 各个Namespace。
当调用clone时, 设定了CLONE_NEWPID,就会创建一个新的PID Namespace,clone出来的新进程将成为Namespace里的第一个进程。一个 PID Namespace为进程提供了一个独立的PID环境,PID Namespace内的PID将从1开始,在Namespace内调用 fork,vfork或clone都将产生一个在该Namespace内独立的PID。新创建的Namespace里的第一个进程在该Namespace 内的PID将为1,就像一个独立的系统里的init进程一样。该Namespace内的孤儿进程都将以该进程为父进程,当该进程被结束时,该 Namespace内所有的进程都会被结束。PID Namespace是层次性,新创建的Namespace将会是创建该Namespace的进程属于 的Namespace的子Namespace。子Namespace中的进程对于父Namespace是可见的,一个进程将拥有不止一个PID,而是在所 在的Namespace以及所有直系祖先Namespace中都将有一个PID。系统启动时,内核将创建一个默认的PID Namespace,该 Namespace是所有以后创建的Namespace的祖先,因此系统所有的进程在该Namespace都是可见的。
当调用clone时, 设定了CLONE_NEWIPC,就会创建一个新的IPC Namespace,clone出来的进程将成为Namespace里的第一个进程。一个 IPC Namespace有一组System V IPC objects 标识符构成,这标识符有IPC相关的系统调用创建。在一个 IPC Namespace里面创建的IPC object对该Namespace内的所有进程可见,但是对其他Namespace不可见,这样就使得不 同Namespace之间的进程不能直接通信,就像是在不同的系统里一样。当一个IPC Namespace被销毁,该Namespace内的所有 IPC object会被内核自动销毁。
PID Namespace 和IPC Namespace可以组合起来一起使用,只需在调用clone时,同时指定CLONE_NEWPID和CLONE_NEWIPC,这样新创建 的Namespace既是一个独立的PID空间又是一个独立的IPC空间。不同Namespace的进程彼此不可见,也不能互相通信,这样就实现了进程间 的隔离。
当调用clone时, 设定了CLONE_NEWNS,就会创建一个新的mount Namespace。每个进程都存在于一个mount Namespace里 面,mount Namespace为进程提供了一个文件层次视图。如果不设定这个flag,子进程和父进程将共享一个mount Namespace, 其后子进程调用mount或umount将会影响到所有该Namespace内的进程。如果子进程在一个独立的mount Namespace里面,就可 以调用mount或umount建立一份新的文件层次视图。该flag配合pivot_root系统调用,可以为进程创建一个独立的目录空间。
当调用clone时, 设定了CLONE_NEWNET,就会创建一个新的Network Namespace。一个Network Namespace为进程提供了一个完全独 立的网络协议栈的视图。包括网络设备接口,IPv4和IPv6协议栈,IP路由表,防火墙规则,sockets等等。一个 Network Namespace提供了一份独立的网络环境,就跟一个独立的系统一样。一个物理设备只能存在于一个Network Namespace 中,可以从一个Namespace移动另一个Namespace中。虚拟网络设备(virtual network device)提供了一种类似管道的 抽象,可以在不同的Namespace之间建立隧道。利用虚拟化网络设备,可以建立到其他Namespace中的物理设备的桥接。当一个 Network Namespace被销毁时,物理设备会被自动移回init Network Namespace,即系统最开始的Namespace。
当调用clone时, 设定了CLONE_NEWUTS,就会创建一个新的UTS Namespace。一个UTS Namespace就是一组被uname返回的标识符。新的 UTS Namespace中的标识符通过复制调用进程所属的Namespace的标识符来初始化。Clone出来的进程可以通过相关系统调用改变这些标 识符,比如调用sethostname来改变该Namespace的hostname。这一改变对该Namespace内的所有进程可见。 CLONE_NEWUTS和CLONE_NEWNET一起使用,可以虚拟出一个有独立主机名和网络空间的环境,就跟网络上一台独立的主机一样。
以上所有 clone flag都可以一起使用,为进程提供了一个独立的运行环境。LXC正是通过在clone时设定这些flag,为进程创建一个有独立 PID,IPC,FS,Network,UTS空间的container。一个container就是一个虚拟的运行环境,对container里的进程 是透明的,它会以为自己是直接在一个系统上运行的。
一个container就像传统虚拟化技术里面的一台安装了OS的虚拟机,但是开销更小,部署更为便捷。
作者曰:Linux Namespaces机制本身就是为了实现container based virtualizaiton开发的。它提供了一套轻量级、高效率的系统资源隔离方案,远比传统的虚拟化技术开销小,不过它也不是完美的,它为内核的开发 带来了更多的复杂性,它在隔离性和容错性上跟传统的虚拟化技术比也还有差距