1 简介
UTS命名空间是Linux内核Namespace(命名空间)的一个子系统,主要用来完成对容器HOSTNAME和domain的隔离,同时保存内核名称、版本、以及底层体系结构类型等信息。
UTS命名空间是扁平化的结构,不同的命名空间之间没有层级关系。
Uts命名空间的用来隔离系统的这些信息,使得用户在容器中查看到的信息是当前容器的系统、版本,不同于主机的,内核通过uts_namespace对当前系统中多个容器的这些信息进行统一管理,每一个容器对应有一个自己的uts_namespace,用来隔离容器的内核名称、版本等信息,不同容器查看到的都是属于自己的信息,相互间不能查看。
2 场景分析
2.1 用户态接口
当前一个系统的uts信息可以通过uname命令来查看,uname用来获取当前机器和操作系统的相关信息,可以显示linux主机所用的操作系统的版本、硬件名称等基本信息。
其用法如下:
uname [-amnrsvpio][--help][--version]
参数:
-a或-all 详细输出所有信息,依次为内核名称,主机名,内核版本号,内核版本,硬件名,处理器类型,硬件平台类型,操作系统名称
-m或-machine 显示主机的硬件(CPU)名
-n或-nodename 显示主机在网络节点上的名称或主机名称
-r或-release 显示linux操作系统内核版本号
-s或-sysnam 显示linux内核名称
-v 显示显示操作系统是第几个 version 版本
-p 显示处理器类型或unknown
-I 显示硬件平台类型或unknown
-o 显示操作系统名
-help 获得帮助信息
-version 显示uname版本信息
这些信息在内核中都保存在一个叫new_utsname的结构体中,这个结构体中的不同字段对应着不同的信息。用户可以通过uname查看到这些信息。
Uname是glibc分装的函数。其实现是依据linux的系统调用newuname。
Uname对应内核的一个系统调用newname,这个系统调用的实现代码如下:
通过utsname获取到new_utsname结构体的值。
2.2 容器场景下的uts
容器场景下,由于实现了uts_namespace,获取当前utsname时,都是获取当前上下文进程的所在的uts命名空间中的utsname来实现的,如下:
每一个进程都有一个属于自己的uts命名空间。多个进程可共享这个命名空间 。
创建容器时,通过CLONE_UTSNS来指定一个新的命名空间。待容器中的init进程创建子进程时,子进程复制其父进程的uts命名空间。这样做到了再一个容器中的所有进程共享一个uts命名空间,从而其相应的utsname信息也是共享一份的。
3 实现原理
3.1 数据结构
Uts命名空间中主要的数据结构包括两个:uts_namespace和new_utsname。
1、uts_namespace
uts_namespace包括三部分:
kref是uts_namespace的应用计数,new_utsname是保存所有uts相关内容和信息的结构,
user_namepace是user命名空间的一个指针(这部分的使用具体参见user namespace)。
2、new_utsname
UTS命名空间里主要是一些host的信息,通过uname –a就能看到:
红色斜杠隔开的部分分别对应new_utsname结构的成员。
其中:
sysname Linux 内核名称
nodename lxc34 主机在网络节点上的名称或主机名称
release 3.4.24.09+ linux操作系统内核版本号
version #48SMP PREEMPT RT Wed Jun 26 16:57:25 CST 2013
显示操作系统是第几个 version 版本
machine x86_64 主机的硬件(CPU)名
x86_64 处理器类型
x86_64 硬件平台类型
GNU/Linux 操作系统名
domainname domain名
3.2 基本操作
1、创建UTS命名空间
创建uts_namespace的过程比较简单,主要就是分配一个uts_namespace实例的内核空间,创建一个引用计数,并增加。
2、克隆UTS命名空间
克隆一个uts_namspace首先创建一个create_uts_ns,然后将旧的命名空间的内容复制到新创建的uts_namespace中。
3、 复制UTS命名空间
如果不是CLONE_NEWUTS标志,直接返回旧的命名空间,但是增加了旧的命名空间的引用计数,否则就克隆一个UTS命名空间。
4、 uts_namespace创建的场景
前面的几个函数都是uts的使用接口函数,包括创建、clone等。那么到底在什么场景下会使用到这些具体的接口函数,这里详细说明一下。
1) 执行clone系统调用(fork)创建一个子进程时,通过指定具体的clone标识(clone_flags)来为子进程创建新的命名空间。Uts命名空间对应的clone_flag为CLONE_NEWUTS。
具体的函数调用关系如下图:
在clone系统调用(fork)处理中,在copy进程时,会做namespace的相关处理,如果指定了CLONE_NEWUTS,则会新建一个uts_namespace。否则直接沿用父进程的namespace。
2) set_ns系统调用
通过系统调用直接set_ns设置命名空间时,会调用到copy_utsname接口函数,但由于set_ns中不会指定具体的clone_flag(也不需要),因此在copy_utsname中也不会调用clone_uts的操作,而是直接返回原来的uts_namespace。
处理的逻辑如下:
可以看到,在调用create_new_namespace时,指定的flag为0,这样在copy的时候就不会执行clone_uts_ns的操作而直接返回了。
set_ns系统调用所设置的uts_namespace是这个执行进程所属的命名空间。
3) unshare系统调用
unshare系统调用允许进行调用的进程用原名称空间的拷贝替代所选资源的名称空间,用于进程在不创建一个新的进程的情况下共享它的资源,这些资源包括进程上下文、命名空间等。
Unshare系统调用通过设置不同的unshare_flags来指定是否创建新的namespace。其处理过程类似clone的中的uts_namespace的copy过程。
处理过程如下: