这几年,Docker容器和其它资源虚拟化平台(比如Mesos)研究非常多,各种应用技术层出不穷,但是要想使用好容器,其底层实现技术原理必须要掌握清楚。其中,无论是LXC、Mesos还是Docker容器,都依赖于Linux内核底层技术namespace和cgroup,本文以简要方式演示如何使用namespace和cgroup来实现进程的资源隔离和限制。
1. 什么是Namespace和Cgroups
在Linux中,敲击man帮助手册命令,
- man namespaces
- man cgroups
可以获取到namespace和cgoups的相关文档定义,
A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes. One use of namespaces is to implement containers……
Control cgroups, usually referred to as cgroups, are a Linux kernel feature which provides for grouping of tasks and resource tracking and limitations for those groups……
从上述定义,可以知道namespace/cgroup是可以控制指定进程对系统资源的支配调度,系统资源包括网络、进程间通信、进程空间、文件系统及其用户权限等。换句话说,namespace/cgroup对系统资源做了一层抽象,让系统资源对进程部分可见,在同一个namespace中的进程,可看到系统资源是一样的。
Linux提供Namespace/Cgroup功能,主要是为了实现Linux容器技术。
2. 启动一个进程
在进入演示步骤前,我们先了解下Linux如何启动一个进程。
在Linux中有三种方法来启动一个子进程,分别为
1) clone:从父进程中创建子进程,和fork不一样之处在于,可以更加灵活的指定子进程的运行上下文,也具有更为强大的进程调度功能。man namespaces
2) fork:该方法创造的子进程是几乎是父进程的完整副本(进程ID\内存锁等除外)。子进程和父进程各自独立执行,先后顺序不定。
3) vfork:该方法创建的子进程和父进程共享物理空间,包括堆栈,而且和fork不一样之处是,其会阻塞父进程的执行。换句话说,vfork保证子进程在父进程之前执行完毕。
演示2-1
创建一个代码文件clone.c
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#define STACK_SIZE (1024*1024)
static char child_stack[STACK_SIZE];
int child_main(void * args) {
printf("in child process, pid=%d\n", getpid());
printf("quit child process...\n");
return EXIT_SUCCESS;
}
int main(){
printf("start...\n");
printf("in parent process, pid=%d\n", getpid());
int child_pid = clone(child_main, child_stack + STACK_SIZE, SIGCHLD, NULL);
waitpid(child_pid, NULL, 0);
printf("quit...\n");
return EXIT_SUCCESS;
}
编译命令gcc clone.c -o clone.o
运行可执行文件clone.o,有如下console输出结果,
start...
in parent process, pid=4673
in child process, pid=4674
quit child process...
quit...
可以看到一个子进程(ID=4674)通过父进程启动,然后先后退出。
演示2-2
创建一个代码文件fork.c
#include
#include
#include
#include
int main(void) {
int count = 1;
int child;
child = fork();
if(child < 0) {
perror("fork error : ");
} else if(child == 0) {
++count;
printf("in child process, pid=%d, count=%d (%p)\n", getpid(), count, &count);
} else {
++count;
printf("in parent process, pid=%d, count=%d (%p)\n", getpid(), count, &count);
}
printf("pid=%d quit now...\n", getpid());
return EXIT_SUCCESS;
}
编译命令gcc fork.c -o fork.o
运行可执行文件fork.o,有如下console输出结果,
in parent process, pid=4253, count