在初步的了解 docker 后,笔者期望通过理解 docker 背后的技术原理来深入的学习和使用 docker,接下来的几篇文章简单的介绍下 linux namespace 的概念以及基本用法。

namespace的概念

namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。

Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。

namespace的用途

可能绝大多数的使用者和我一样,是在使用 docker 后才开始了解 linux 的 namespace 技术的。实际上,Linux 内核实现 namespace 的一个主要目的就是实现轻量级虚拟化(容器)服务。在同一个 namespace 下的进程可以感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,认为自己置身于一个独立的系统中,从而达到隔离的目的。也就是说 linux 内核提供的 namespace 技术为 docker 等容器技术的出现和发展提供了基础条件。

我们可以从 docker 实现者的角度考虑该如何实现一个资源隔离的容器。比如是不是可以通过 chroot 命令切换根目录的挂载点,从而隔离文件系统。为了在分布式的环境下进行通信和定位,容器必须要有独立的 IP、端口和路由等,这就需要对网络进行隔离。同时容器还需要一个独立的主机名以便在网络中标识自己。接下来还需要进程间的通信、用户权限等的隔离。最后,运行在容器中的应用需要有进程号(PID),自然也需要与宿主机中的 PID 进行隔离。也就是说这六种隔离能力是实现一个容器的基础,让我们看看 linux 内核的 namespace 特性为我们提供了什么样的隔离能力:

上表中的前六种 namespace 正是实现容器必须的隔离技术,至于新近提供的 Cgroup namespace 目前还没有被 docker 采用。相信在不久的将来各种容器也会添加对 Cgroup namespace 的支持。

namespace的发展历史

Linux 在很早的版本中就实现了部分的 namespace,比如内核 2.4 就实现了 mount namespace。大多数的 namespace 支持是在内核 2.6 中完成的,比如 IPC、Network、PID、和 UTS。还有个别的 namespace 比较特殊,比如 User,从内核 2.6 就开始实现了,但在内核 3.8 中才宣布完成。同时,随着 Linux 自身的发展以及容器技术持续发展带来的需求,也会有新的 namespace 被支持,比如在内核 4.6 中就添加了 Cgroup namespace。

Linux 提供了多个 API 用来操作 namespace,它们是 clone()、setns() 和 unshare() 函数,为了确定隔离的到底是哪项 namespace,在使用这些 API 时,通常需要指定一些调用参数:CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和 CLONE_NEWCGROUP。如果要同时隔离多个 namespace,可以使用 | (按位或)组合这些参数。同时我们还可以通过 /proc 下面的一些文件来操作 namespace。下面就让让我们看看这些接口的简要用法。

查看进程所属的namespace

从版本号为 3.8 的内核开始,/proc/[pid]/ns 目录下会包含进程所属的 namespace 信息,使用下面的命令可以查看当前进程所属的 namespace 信息:

$ ll /proc/$$/ns

首先,这些 namespace 文件都是链接文件。链接文件的内容的格式为 xxx:[inode number]。其中的 xxx 为 namespace 的类型,inode number 则用来标识一个 namespace,我们也可以把它理解为 namespace 的 ID。如果两个进程的某个 namespace 文件指向同一个链接文件,说明其相关资源在同一个 namespace 中。

其次,在 /proc/[pid]/ns 里放置这些链接文件的另外一个作用是,一旦这些链接文件被打开,只要打开的文件描述符(fd)存在,那么就算该 namespace 下的所有进程都已结束,这个 namespace 也会一直存在,后续的进程还可以再加入进来。

除了打开文件的方式,我们还可以通过文件挂载的方式阻止 namespace 被删除。比如我们可以把当前进程中的 uts 挂载到 ~/uts 文件:

$ touch ~/uts$ sudo mount --bind /proc/$$/ns/uts ~/uts

使用 stat 命令检查下结果:

很神奇吧,~/uts 的 inode 和链接文件中的 inode number 是一样的,它们是同一个文件。

clone函数

我们可以通过 clone() 在创建新进程的同时创建 namespace。clone() 在 C 语言库中的声明如下:

/* Prototype for the glibc wrapper function */#define _GNU_SOURCE#include int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

实际上,clone() 是在 C 语言库中定义的一个封装(wrapper)函数,它负责建立新进程的堆栈并且调用对编程者隐藏的 clone() 系统调用。Clone() 其实是 linux 系统调用 fork() 的一种更通用的实现方式,它可以通过 flags 来控制使用多少功能。一共有 20 多种 CLONE_ 开头的 falg(标志位) 参数用来控制 clone 进程的方方面面(比如是否与父进程共享虚拟内存等),下面我们只介绍与 namespace 相关的 4 个参数:

  • fn:指定一个由新进程执行的函数。当这个函数返回时,子进程终止。该函数返回一个整数,表示子进程的退出代码。

  • child_stack:传入子进程使用的栈空间,也就是把用户态堆栈指针赋给子进程的 esp 寄存器。调用进程(指调用 clone() 的进程)应该总是为子进程分配新的堆栈。

  • flags:表示使用哪些 CLONE_ 开头的标志位,与 namespace 相关的有CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和 CLONE_NEWCGROUP。

  • arg:指向传递给 fn() 函数的参数。

在后续的文章中,我们主要通过 clone() 函数来创建并演示各种类型的 namespace。

setns函数

通过 setns() 函数可以将当前进程加入到已有的 namespace 中。setns() 在 C 语言库中的声明如下:

#define _GNU_SOURCE#include int setns(int fd, int nstype);

和 clone() 函数一样,C 语言库中的 setns() 函数也是对 setns() 系统调用的封装:

  • fd:表示要加入 namespace 的文件描述符。它是一个指向 /proc/[pid]/ns 目录中文件的文件描述符,可以通过直接打开该目录下的链接文件或者打开一个挂载了该目录下链接文件的文件得到。

  • nstype:参数 nstype 让调用者可以检查 fd 指向的 namespace 类型是否符合实际要求。若把该参数设置为 0 表示不检查。

前面我们提到:可以通过挂载的方式把 namespace 保留下来。保留 namespace 的目的是为以后把进程加入这个 namespace 做准备。在 docker 中,使用 docker exec 命令在已经运行着的容器中执行新的命令就需要用到 setns() 函数。为了把新加入的 namespace 利用起来,还需要引入 execve() 系列的函数(笔者在 《Linux 创建子进程执行任务》一文中介绍过 execve() 系列的函数,有兴趣的同学可以前往了解),该函数可以执行用户的命令,比较常见的用法是调用 /bin/bash 并接受参数运行起一个 shell。

unshare函数和命令

通过 unshare 函数可以在原进程上进行 namespace 隔离。也就是创建并加入新的 namespace 。unshare() 在 C 语言库中的声明如下:

#define _GNU_SOURCE#include int unshare(int flags);

和前面两个函数一样,C 语言库中的 unshare() 函数也是对 unshare() 系统调用的封装。调用 unshare() 的主要作用就是:不启动新的进程就可以起到资源隔离的效果,相当于跳出原先的 namespace 进行操作。

系统还默认提供了一个叫 unshare 的命令,其实就是在调用 unshare() 系统调用。下面的 demo 使用 unshare 命令把当前进程的 user namespace 设置成了 root:

总结

namespace 是 linux 内核提供的特性,为虚拟化而生。随着 docker 的诞生引爆了容器技术,也把长期在后台默默奉献的 namespace 技术推到了大家的面前。笔者试图通过对 namespace 技术的学习和理解来加深对容器技术的认识,所以接下来会通过文章记录学习 namespace 的点点滴滴,希望能和同学们一起进步。

execve系统调用_Linux操作系统中的namespace是个什么鬼相关推荐

  1. execve系统调用_Linux系统编程——进程替换:exec 函数族

    在 Windows 平台下,我们可以通过双击运行可执行程序,让这个可执行程序成为一个进程:而在 Linux 平台,我们可以通过 ./ 运行,让一个可执行程序成为一个进程. 但是,如果我们本来就运行着一 ...

  2. 操作系统系统调用_操作系统中的系统调用

    操作系统系统调用 系统调用简介 (Introduction to System calls) The interface between the operating system and the us ...

  3. execve系统调用_Linux下的sys_execve系统调用

    传统Int 0x80系统调用 系统调用号:EAX 参数:EBX.ECX.EDX.ESI.EDI.EBP 返回值:EAX 具体功能号在 unistd_32.h 文件中 64位系统调用syscall 系统 ...

  4. centos sudo不能运行_Linux操作系统中sudo的使用和sudoers配置

    sudo 是linux下常用的允许普通用户使用超级用户权限的工具; 允许系统管理员让普通用户执行一些或者全部的root命令,如halt,reboot,su等等.这样不仅减少了root用户的登陆和管理时 ...

  5. execve系统调用,Linux内核中的sys_execve()系统调用可以同时接收绝对路径还是相对路径?...

    Shall sys_execve() in kernel level code receive absolute or relative path for the filename parameter ...

  6. execve系统调用_《操作系统》知识结构

    作者:陈凌灏 单位:西安电子科技大学 注:转载请注明出处~~ 写在前面:<操作系统>这门课几乎可以说是计算机专业最重要的课程了.本文主要是在学习完这门课后,参照TANENBAUM的< ...

  7. 如何从文件名字符串中获取文件扩展名_Linux操作系统:文件系统的功能和命名...

    文件命名 文件是抽象机制,提供在磁盘上存放信息和以后从中读出的方法.用户不必了解信息如何存放.存放在何处.磁盘如何实际工作等细节.抽象机制最重要的特性就是"按名"管理对象.用户对文 ...

  8. 笔记:Linux系统调用在文件中的分布情况

    一.Linux 3.1.10版本的系统调用在文件中的分布情况如下所示: arch/alpha/kernel/osf_sys.c:SYSCALL_DEFINE2(osf_getdomainname, c ...

  9. 操作系统中的名词解释

    1.说明现在操作系统的功能主要有哪些? 计算机系统的资源可分为设备资源和信息资源两大类.操作系统位于底层硬件与用户之间. 进程管理,其工作主要是进程调度,在单用户单任务的情况下,处理器仅为一个用户的一 ...

最新文章

  1. 洛谷P4316 绿豆蛙的归宿(期望)
  2. IBM发布未来五年五大科技预测
  3. 【Android View基础】View中几个容易混淆的距离及坐标量
  4. 索尼:声控将在VR中发挥大作用
  5. 云服务器转租赁协议,关于云服务器的租赁协议
  6. 火狐web开发清楚缓存_如何使用Firefox的Web开发工具
  7. 所有主流浏览器都支持HTML的视频格式5,所有主流浏览器都支持HTML 5吗?
  8. Android camera (12)---camera ap在特殊的应用场景下额外使用一套独立的camera tuning参数
  9. 冷饭新炒:理解JDK中UUID的底层实现
  10. web前端是什么?如何能成为一名合格的前端开发工程师?
  11. [UI]实用案例--Shape绘制实用圆圈
  12. Java 8中的::(双冒号)运算符
  13. CentOS 6 上安装 pip、setuptools
  14. axure轮播图怎么设置循环轮播_Axure教程:广告图片自动轮播+点击切换
  15. CentOS查看和修改PATH环境变量的方法 (转)
  16. web 前端面试题50道
  17. 什么是IDS和IPS
  18. [rock]Life ’s A Struggle -宋岳庭(转)
  19. 小米pro15拆机_小米笔记本Pro 15增强版拆解:重新定义高质低价
  20. Linux基础、vim、find命令等

热门文章

  1. 1、eclipse 使用git提交项目至github进行项目托管
  2. 女程序员在GitHub提交PR更易被拒?开源世界“她”力量
  3. Compose 1.0 即将发布,你准备好了吗?
  4. 毕业后想拿大厂offer?你得完整拥有这些计算机知识体系!
  5. 只加两行代码,为什么用了整整两天时间?
  6. 新一代国际视频编解码标准正式出炉,腾讯超100项提案获标准采纳
  7. 小心这 10 个云计算错误!
  8. 苹果官方将首次参与天猫 618 促销活动;淘宝回应用户账号被禁用980年;Julia 1.5.0 beta1 发布 | 极客头条...
  9. 数据说话!UCloud「硬刚」腾讯云,高性能 AMD 云主机哪家强?
  10. 你技术很强但薪资不涨?这门技术远比你想的更重要!