操作系统级虚拟化
kvm、xen等虚拟化技术允许各个虚拟机拥有自己独立的操作系统。与kvm、xen等虚拟化技术不同,所谓操作系统级虚拟化,也被称作容器化,是操作系统自身的一个特性,它允许多个相互隔离的用户空间实例的存在。这些用户空间实例也被称作为容器。普通的进程可以看到计算机的所有资源而容器中的进程只能看到分配给该容器的资源。通俗来讲,操作系统级虚拟化将操作系统所管理的计算机资源,包括进程、文件、设备、网络等分组,然后交给不同的容器使用。容器中运行的进程只能看到分配给该容器的资源。从而达到隔离与虚拟化的目的。
实现操作系统虚拟化需要用到namespace及cgroups技术。
命名空间(namespace)
在编程语言中,引入命名空间的概念是为了重用变量名或者服务例程名。在不同的命名空间中使用同一个变量名而不会产生冲突。linux系统引入命名空间也有类似的作用。例如,在没有操作系统级虚拟化的linux系统中,用户态进程从1开始编号(pid)。引入操作系统虚拟化之后,不同容器有着不同的pid命名空间,每个容器中的进程都可以从1开始编号而不产生冲突。
目前,linux中的命名空间有6种类型,分别对应操作系统管理的6种资源:
挂载点(mount point) clone_newns
进程(pid) clone_newpid
网络(net) clone_newnet
进程间通信(ipc) clone_newipc
主机名(uts) clone_newuts
用户(uid) clonw_newuser
将来还会引入时间、设备等对应的namespace.
linux 2.4.19版本引入了第一个命名空间——挂载点,因为那时还没有其他类型的命名空间,所以clone系统调用中引入的flag就叫做clone_newns
与命名空间相关的三个系统调用(system calls)
下面3个系统调用用来操作命名空间:
clone() —— 用来创建新的进程及新的命名空间,新的进程会被放到新的命名空间中
unshare() —— 创建新的命名空间但并不创建新的子进程,之后创建的子进程会被放到新创建的命名空间中去
setns() —— 将进程加入到已经存在的命名空间中
注意:这3个系统调用都不会改变调用进程(calling process)的pid命名空间,而是会影响其子进程的pid命名空间
命名空间本身并没用名字(囧),不同的命名空间用不同的inode号来标识,这也符合linux用文件一统天下的惯例。可以在proc文件系统中查看一个进程所属的命名空间,例如,查看pid为4123的进程所属的命名空间:
kelvin@desktop:~$ls -l /proc/4123/ns/
总用量0
lrwxrwxrwx1kelvin kelvin012月2616:28cgroup -> cgroup:[4026531835]
lrwxrwxrwx1kelvin kelvin012月2616:28ipc -> ipc:[4026531839]
lrwxrwxrwx1kelvin kelvin012月2616:28mnt -> mnt:[4026531840]
lrwxrwxrwx1kelvin kelvin012月2616:28net -> net:[4026531963]
lrwxrwxrwx1kelvin kelvin012月2616:28pid -> pid:[4026531836]
lrwxrwxrwx1kelvin kelvin012月2616:28user -> user:[4026531837]
lrwxrwxrwx1kelvin kelvin012月2616:28uts -> uts:[4026531838]
下面的代码演示了如何利用上述3个系统调用来操作进程的命名空间:
#define _gnu_source
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define stack_size (10 * 1024 * 1024)
charchild_stack[stack_size];
intchild_main(void* args){
pid_t child_pid = getpid();
printf(i'm child process and my pid is %d \n,child_pid);
// 子进程会被放到clone系统调用新创建的pid命名空间中, 所以其pid应该为1
sleep(300);
// 命名空间中的所有进程退出后该命名空间的inode将会被删除, 为后续操作保留它
return0;
}
intmain(){
/* clone */
pid_t child_pid = clone(child_main,child_stack + stack_size,\
clone_newpid | sigchld,null);
if(child_pid < 0){
perror(clone failed);
}
/* unshare */
intret = unshare(clone_newpid);// 父进程调用unshare, 创建了一个新的命名空间,
//但不会创建子进程. 之后再创建的子进程将会被加入到新的命名空间中
if(ret < 0){
perror(unshare failed);
}
intfpid = fork();
if(fpid < 0){
perror(fork error);
}elseif(fpid == 0){
printf(i am child process. my pid is %d\n,getpid());
// fork后的子进程会被加入到unshare创建的命名空间中, 所以pid应该为1
exit(0);
}else{
}
waitpid(fpid,null,0);
/* setns */
charpath[80] = ;
sprintf(path,/proc/%d/ns/pid,child_pid);
intfd = open(path,o_rdonly);
if(fd == -1)
perror(open error);
if(setns(fd,0) == -1)
// setns并不会改变当前进程的命名空间, 而是会设置之后创建的子进程的命名空间
perror(setns error);
close(fd);
intnpid = fork();
if(npid > group0/tasks
# cat group0/tasks
20553
7. 限制该组的进程只能运行在编号为2的cpu上
# echo 2 > group0/cpuset.cpus
# cat group0/cpuset.cpus
2
8. 查看pid为20553的进程所运行的cpu编号
# ps -elo ruser,lwp,psr,args | grep 20553 | grep -v grep
root 20553 2bash run.sh
上面的例子简单的展示了如何使用控制组。控制组通过文件和目录来操作,文件系统又是树形结构,因此如果不对cgroups的使用做一些限制的话,配置会变得异常复杂和混乱。因此,在新版的cgroups中做了一些限制。
小结
本文简要介绍了操作系统虚拟化的概念,以及实现操作系统虚拟化的技术——命名空间及控制组。并通过两个简单的例子演示了命名空间及控制组的使用方法。
Visa收购Plaid 计划失败 拟将欧洲业务规模扩增一倍
高速图像压缩芯片“雅芯-天图”成功应用
大彩M系列精简核心板结构串口屏发布,提供IO/USB扩展口二次开发
2021第八届海峡两岸(南京)新型显示产业高峰论坛成功举办
共筑开源安全 | 软通动力作为发起单位加入“开放原子开源基金会开源安全委员会”
简要介绍了操作系统虚拟化的概念,以及实现操作系统虚拟化的技术
输出的脉宽调制集液晶/LED驱动器
LPC21o4处理器结构及特性介绍
我们正在进入一个数据存储的新时代
盘点机器学习推动运输与物流行业变革的几种方式
时刻继电器的两种延时方法
TI新推出QFN封装蓝牙无线连结系列产品
华为荣耀V9怎么样?荣耀V9、华为P10和P10Plus大PK,看看谁能笑到最后!
ARM发力物联网 收购两家公司
基于Zynq的嵌入式系统教学改革背景与措施
PFC控制器CS1501的主要特性及典型应用电路
图像采集卡的工作原理
MIMO和分布式天线系统(DAS)技术简述
iPad 2初期销量或高于一代
射频PCB布局中的波导腔设计