Closed mlycore closed 6 years ago
namespace机制即允许进程修改操作系统的资源,不在同一命名空间下的进程是不可见的,操作系统中可同时存在多个命名空间,若将系统资源划分至特定的空间,即资源仅附属于此空间内,便可在空间上对资源进行隔离,使得处于不同空间的资源透明化
故Linux Namespace是一种实现资源隔离的机制,提供了一种安全的运行环境,从而为容器的虚拟化技术提供了基础,处于不同容器内的进程拥有自己的Namespace
采用隔离机制主要是要将资源隔离,namespace提供了对PID、NET、UTS、IPC、mount、User隔离机制,主要是通过三个系统调用来实现,clone用来创建新进程并通过参数进行隔离操作,unshare即将某个子进程脱离当前的命名空间,setns用于将某进程加入到某个命名空间
首先我们创建一个父子进程,通过对比来认识以上各种隔离机制的作用,此程序是在父进程中产生一个子进程进而执行bash,以便我们查看其中的信息状态
#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");
execv(container_args[0], container_args);
return 1;
}
int main()
{
printf("Parent - start a container!\n");
int container_pid = clone(container_main, container_stack+STACK_SIZE,SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}
PID Namespace
进程的组织方式是一颗具有层次结构的动态树,来表示当前所有运行进程的引用关系,若某一进程有权限便可对进程树中另一进程进行操作如kill掉,从而导致一系列安全问题
若在系统中对某一类子树进行划分,使得进程树可进行子树的嵌套,便存在一种类似函数作用域的约定,此类子树不能对其外的任何进程进行操作,即划定新的命名空间的过程
在linux系统启动时会创建一个pid为1的特殊进程,由此为根节点产生子孙后代进程,若子进程需与其他进程进行隔离操作,便为其创建单独的命名空间,其后续子进程便以其为根节点产生一颗隔离的子树,这一过程可进行嵌套使用,从而进行新进程的隔离操作
进程树是允许嵌套使用的,那么隔离出的效果是如何的呢,处于不同命名空间下的进程互不知情,它们只能看到自己的子孙进程,而在其上的父进程可查看到它的所有子进程
如何对这么多进程进行管理呢,每个子进程树都是从其新命名空间的根节点pid为1开始编号,在子树中嵌套的子树则也是从其根节点编号,而每个子树中的节点同样的在其父命名空间中也有一个pid编号的存在,因此一个进程可拥有多个pid
int container_main(void* arg)
{
printf("Container [%5d] - inside the container!\n",getpid());
execv(container_args[0], container_args);
return 1;
}
int main()
{
printf("Parent - start a container!\n");
int container_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);
printf("Parent - container stopped!\n");
return 0;
}
进程pid可采取上述方式进行隔离操作,那如何拥有自己的网络空间,使处于不同命名空间的进程有不同的网络接口。首先将进程隔绝在自有的网络空间中需调用clone()函数时传入CLONE_NEWNET参数
通过system("ip link");可查看不同命名空间的ip状态
为使得各命名空间的网络接口可用,由处于全局命名空间的一个routing process,负责从物理网卡中接收数据并通过虚拟网口发送到子命名空间,便可实现网络资源的隔离
static int child_fn()
{
printf("New net Namespace:\n");
system("ip link");
printf("\n\n");
return 0;
}
int main()
{
printf("Original net Namespace:\n");
system("ip link");
printf("\n\n");
pid_t child_pid = clone(child_fn,child_stack+1048576, CLONE_NEWNET | SIGCHLD,NULL);
waitpid(child_pid,NULL,0);
return 0;
}
UTS Namespace
如果clone新进程时加入参数CLONE_NEWUTS,则会在新的UTS命名空间中创建进程,该空间的标识符通过复制调用进程的标识符来初始化自己,UTS命名空间隔绝了两个特殊的系统标识,如nodename与domainname,nodename即节点主机名
一个UTS Namespace即一组被uname返回的标识符,当通过命令sethostname修改子命名空间的主机名时,对其空间内的进程可见而对父进程空间无影响
static void print_nodename()
{
struct utsname utsname;
uname(&utsname);
printf("%s\n",utsname.nodename);
}
static int child_fn()
{
printf("New UTS namespace nodename:");
print_nodename();
printf("Changing nodename inside new UTS namespace\n");
sethostname("GLaDOS",6);
printf("New UTS namespace nodename: ");
print_nodename();
return 0;
}
int main()
{
printf("Original UTS Namespace nodename: ");
print_nodename();
pid_t child_pid = clone(child_fn,child_stack+1048576,CLONE_NEWUTS | SIGCHLD,NULL);
sleep(1);
printf("Original UTS Namespace nodename: ");
print_nodename();
waitpid(child_pid,NULL,0);
return 0;
}
Mount Namespace
在linux系统下存有关于所有挂载点的信息,在创建新命名空间时首先对其进行复制操作,进而在子命名空间下进行挂载修改时不会影响其父命名空间中的内容
通过参数CLONE_NEWNS传入clone()函数后,子进程会产生一个与父进程完全一致的挂载信息,当子进程命名空间内对挂载点进行任一操作变动时,对其他命名空间的进程是不可见的
int container_main(void* arg)
{
system("mount -t proc proc /proc");
execv(container_args[0], container_args);
return 1;
}
linux Cgroup
通过Namespace能够发现其机制主要是提供一个可互相隔离的环境,使之可在特定的条件下运行,而我们要对计算机的资源如cpu、mem进行控制操作,则需要另一种机制Linux CGroup
CGroup即用来控制管理一个进程组的资源分配问题,全称为linux control group,可自定义系统资源的分配限制
@lemony3650 GOOD job!
如题。