Linux namespace

linux下命名空间是一种环境隔离的方法,基于内核态,通过命名空间可以将一些进程抽象到不同的容器中,使得各个容器彼此隔离,但是隔离之后相互间能否存在一些共享,就看你创建namespace的选项了

目前namespaces官方有如下种类:

 Mount namespaces	CLONE_NEWNS
 UTS namespaces	 	CLONE_NEWUTS
 IPC namespaces	 	CLONE_NEWIPC
 PID namespaces	 	CLONE_NEWPID
 Network namespaces	CLONE_NEWNET
 User namespaces	CLONE_NEWUSER

namespace的层次关系,也可以通过父namespace和子namespace来说明,比如对于PID namespace:
1: 父namespace有pid为1,2,3,4,5这五个进程,衍生出两个子namespace,A和B
2: pid为2和3的进程分给了子namespace A,pid为5的进程分给了子namespace B
3:namespace的作用是相互隔离,也就是此时A和B是互不知情
4:关键在A里,pid可就不一定是2和3了,说不定就是1和2了,同理在B里,pid可能就是1了,为什么一定要带个1呢,pid为1是init进程,人家牛逼,linux系统的管家,既然都隔离成单独的容器,理应先分别创建一个pid为1的进程,也就是说这里父namespace里的pid为2和3两个进程分到A里,进程pid变成了1和2,pid为5的进程分到B里,进程pid变成了1
5:父namespace是知道两个子namespace的存在的,虽然它们两互相不清楚,所以它们两都会映射到父namespace,A的两个进程映射父namespace的2和3,B的一个进程映射父namespace的5,始终记住namespace仅仅是环境隔离的抽象,进程依旧是5个

下面是网上的一个小例子,修改了几个地方

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>

#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];

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

int container(){
    printf("Container pid: %d\n", getpid());
    sethostname("container", 10);
    execv(container_args[0], container_args);
    printf("Something wrong!\n");
    return 1;
}

void main(){
    printf("Parent %d: start a container\n", getpid());
    int container_pid;
    container_pid = clone(container, container_stack + STACK_SIZE,
        CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL);
    waitpid(container_pid, NULL, 0);
    printf("Parent: container stopped!\n");
}

记得第一行一定要,否则就回不识别clone的标志位CLONE_NEWPID,而这个标志位才是创建新的namespace,这里clone创建一个新的进程,并且是新的namespace,就像一个新的操作系统一样,同时创建pid,当然是从1开始,所以container函数里的printf打印的就是新进程的pid,等下瞄瞄是多少,除此之外,新的进程里还重新设置了hostname;main函数里printf打印的就是原主进程的pid

root@2015-vm1:/home/lihui# cc hello.c 
root@2015-vm1:/home/lihui# ./a.out 
Parent 6775: start a container
Container pid: 1
root@container:/home/lihui# 

父进程就不看了,子进程在新的容器里pid变成了1,真的相当于重新创建了一个操作系统一样,同时可以看到hostname也变成了container了
但是随便试验一下,两个namespace的隔离性,top命令却还能够看到所有进程的信息,原因是执行这个命令回去读/proc文件系统,所以只要将这个文件系统也给隔离了应该就行了,这里就需要用到另一种namespace的标志位,然后子进程里重新mount一次

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>

#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];

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

int container(){
    printf("Container pid: %d\n", getpid());
    sethostname("container", 10);
    system("mount -t proc proc /proc");
    execv(container_args[0], container_args);
    printf("Something wrong!\n");
    return 1;
}

void main(){
    printf("Parent %d: start a container\n", getpid());
    int container_pid;
    container_pid = clone(container, container_stack + STACK_SIZE,
        CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD | CLONE_NEWNS, NULL);
    waitpid(container_pid, NULL, 0);
    printf("Parent: container stopped!\n");
}

clone多加了一个CLONE_NEWNS标志位,然后在子进程里重新mount一次,这样,就对文件系统/proc进行了隔离,然后子进程容器里查看top结果

root@2015-vm1:/home/lihui# cc hello.c 
root@2015-vm1:/home/lihui# ./a.out 
Parent 7141: start a container
Container pid: 1
root@container:/home/lihui# top

Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   1003252 total,   405100 used,   598152 free,    25236 buffers
KiB Swap:        0 total,        0 used,        0 free.   211868 cached Mem

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                        
     1 root      20   0   19792   3436   3120 S  0.0  0.3   0:00.00 bash                                             
    13 root      20   0   23544   2860   2508 R  0.0  0.3   0:00.00 top  

 

这样就只看到了top和execv这两个进程了

发表评论