当前位置: 首页 > news >正文

【Docker 内核详解】namespace 资源隔离(一):进行 namespace API 操作的 4 种方式

namespace 资源隔离(一):进行 namespace API 操作的 4 种方式

  • 1.通过 clone() 在创建新进程的同时创建 namespace
  • 2.查看 /proc/[pid]/ns 文件
  • 3.通过 setns() 加入一个已经存在的 namespace
  • 4.通过 unshare() 在原先进程上进行 namespace 隔离
  • 5.fork() 系统调用(拓展)

当谈论 Docker 时,常常会聊到 Docker 的实现方式。很多开发者都知道,Docker 容器本质上是宿主机上的进程(容器所在的运行环境统一称为宿主机)。Docker 通过 namespace 实现了 资源隔离,通过 cgroups 实现了 资源限制,通过写时复制机制( copy-on-write)实现了 高效的文件操作。但当更进一步深入 namespacecgroups 等技术细节时,大部分开发者都会感到茫然无措。所以在这里,希望先带领大家走进 Linux 内核,了解 namespacecgroups 的技术细节。

Docker 大热之后,热衷技术的开发者就会思考,想要实现一个资源隔离的容器,应该从哪些方面下手?也许第一反应就是 chroot 命令,这条命令给用户最直观的感受就是在使用后根目录 / 的挂载点切换了,即 文件系统 被隔离了。接着,为了在分布式的环境下进行通信和定位,容器必然要有独立的 IP、端口、路由等,自然就联想到了 网络 的隔离。同时,容器还需要一个独立的 主机名 以便在网络中标识自己。有了网络,自然离不开通信,也就想到了 进程间通信 需要隔离。开发者可能也已经想到了权限的问题,对用户和用户组的隔离就实现了 用户权限 的隔离。最后,运行在容器中的应用需要有进程号(PID),自然也需要与宿主机中的 PID 进行隔离。

由此,基本上完成了一个容器所需要做的 6 项隔离,Linux 内核中提供了这 6 种 namespace 隔离的系统调用,如下表所示。当然,真正的容器还需要处理许多其他工作。

namespace系统调用参数隔离内容
UTSCLONE_ NEWUTS主机名与域名
IPCCLONE_NEWIPC信号量、消息队列和共享内存
PIDCLONE_NEWPID进程编号
NetworkCLONE_NEWNET网络设备、网络栈、端口等
MountCLONE_NEWNS挂载点(文件系统)
UserCLONE_NEWUSER用户和用户组

实际上,Linux 内核实现 namespace 的一个主要目的,就是实现轻量级虚拟化(容器)服务。在同一个 namespace 下的进程可以感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,仿佛自己置身于一个独立的系统环境中,以达到独立和隔离的目的。

需要说明的是,本文所讨论的 namespace 实现针对的均是 Linux 内核 3.8 3.8 3.8 及以后的版本(user namespace 在内核 3.8 3.8 3.8 版本以后才支持)。接下来,将首先介绍使用 namespace 的 API,然后对这 6 种 namespace 进行逐一讲解,并通过程序让读者切身感受隔离效果。

namespace 的 API 包括 clone()setns() 以及unshare(),还有 /proc 下的部分文件。为了确定隔离的到底是哪 6 项 namespace,在使用这些 API 时,通常需要指定以下 6 个参数中的一个或多个,通过 |(位或)操作来实现。从上表可知,这 6 个参数分别是 CLONE_NEWIPCCLONE_NEWNSCLONE_NEWNETCLONE_NEWPIDCLONE_NEWUSERCLONE_NEWUTS

1.通过 clone() 在创建新进程的同时创建 namespace

使用 clone() 来创建一个独立 namespace 的进程,是最常见的做法,也是 Docker 使用 namespace 最基本的方法,它的调用方式如下。

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

clone() 实际上是 Linux 系统调用 fork() 的一种更通用的实现方式,它可以通过 flags 来控制使用多少功能。一共有 20 多种 CLONE_*flag(标志位)参数用来控制 clone 进程的方方面面(如是否与父进程共享虚拟内存等),下面挑选与 namespace 相关的 4 个参数进行说明。

  • child func 传入子进程运行的程序主函数。
  • child stack 传入子进程使用的栈空间。
  • flags 表示使用哪些 CLONE_* 标志位,与 namespace 相关的主要包括 CLONE_NEWIPCCLONE_NEWNSCLONE_NEWNETCLONE_NEWPIDCLONE_NEWUSERCLONE_NEWUTS
  • args 则可用于传入用户参数。

2.查看 /proc/[pid]/ns 文件

3.8 3.8 3.8 版本的内核开始,用户就可以在 /proc/[pid]/ns 文件下看到指向不同 namespace 号的文件,效果如下所示,形如 [4026531839] 者即为 namespace 号。

$ ls -l /proc/$$/ns         <<--$$是shell中表示当前运行的进程ID号
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 user->user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 uts -> uts:[4026531838]

如果两个进程指向的 namespace 编号相同,就说明它们在同一个 namespace 下,否则便在不同 namespace 里面。/proc/[pid]/ns 里设置这些 link 的另外一个作用是,一旦上述 link 文件被打开,只要打开的 文件描述符fd)存在,那么就算该 namespace 下的所有进程都已经结束,这个 namespace 也会一直存在,后续进程也可以再加入进来。在 Docker 中,通过文件描述符定位和加入一个存在的 namespace 是最基本的方式。

另外,把 /proc/[pid]/ns 目录文件使用 --bind 方式挂载起来可以起到同样的作用,命令如下:

# touch ~/uts
# mount --bind /proc/27514/ns/uts ~/uts

为了方便起见,后面的讲解中会使用这个 ~/uts 文件来代替 /proc/27514/ns/uts

注意:如果大家看到 ns 下的内容与本节所述不符,那可能是因为使用了 3.8 3.8 3.8 以前版本的内核。如在内核版本 2.6 2.6 2.6 中,该目录下存在的只有 ipcnetuts,并且以硬链接方式存在。

3.通过 setns() 加入一个已经存在的 namespace

上文提到,在进程都结束的情况下,也可以通过挂载的形式把 namespace 保留下来,保留 namespace 的目的是为以后有进程加入做准备。在 Docker 中,使用 docker exec 命令在已经运行着的容器中执行一个新的命令,就需要用到该方法。通过 setns() 系统调用,进程从原先的 namespace 加入某个已经存在的 namespace,使用方法如下。通常为了不影响进程的调用者,也为了使新加入的 pid namespace 生效,会在 setns() 函数执行后使用 clone() 创建子进程继续执行命令,让原先的进程结束运行。

int setns(int fd, int nstype);
  • 参数 fd 表示要加入 namespace 的文件描述符。上文提到,它是一个指向 /proc/[pid]/ns 目录的文件描述符,可以通过直接打开该目录下的链接或者打开一个挂载了该目录下链接的文件得到。
  • 参数 nstype 让调用者可以检查 fd 指向的 namespace 类型是否符合实际要求。该参数为 0 表示不检查。

为了把新加入的 namespace 利用起来,需要引入 execve() 系列函数,该函数可以执行用户命令,最常用的就是调用 /bin/bash 并接受参数,运行起一个 shell,用法如下。

fd = open(argv[1], O_RDONLY);    /* 获取 namespace 文件描述符 */
setns(fd, 0);                    /* 加入新的 namespace */
execvp(argv[2], &argv[2]);       /* 执行程序 */

假设编译后的程序名称为 setns-test

# ./setns-test ~/uts /bin/bash      # ~/uts 是绑定的 /proc/27514/ns/uts

至此,就可以在新加入的 namespace 中执行 shell 命令了,下文会多次使用这种方式来演示隔离的效果。

4.通过 unshare() 在原先进程上进行 namespace 隔离

最后要说明的系统调用是 unshare(),它与 clone() 很像,不同的是,unshare() 运行在原先的进程上,不需要启动一个新进程。

int unshare(int flags);

调用 unshare() 的主要作用就是,不启动新进程就可以起到隔离的效果,相当于跳出原先的 namespace 进行操作。这样,就可以在原进程进行一些需要隔离的操作。Linux 中自带的 unshare 命令,就是通过 unshare() 系统调用实现的。Docker 目前并没有使用这个系统调用,这里不做展开,读者可以自行查阅资料学习该命令的知识。

5.fork() 系统调用(拓展)

系统调用函数 fork() 并不属于 namespace 的 API,这部分内容属于延伸阅读,如果读者已经对 fork() 有足够多的了解,可以忽略该部分。

当程序调用 fork() 函数时,系统会创建新的进程,为其分配资源,例如存储数据和代码的空间,然后把原来进程的所有值都复制到新进程中,只有少量数值与原来的进程值不同,相当于复制了本身。那么程序的后续代码逻辑要如何区分自己是新进程还是父进程呢?

fork() 的神奇之处在于它仅仅被调用一次,却能够返回两次(父进程与子进程各返回一次),通过返回值的不同就可以区分父进程与子进程。它可能有以下 3 种不同的返回值:

  • 在父进程中,fork() 返回新创建子进程的进程 ID;
  • 在子进程中,fork() 返回 0;
  • 如果出现错误,fork() 返回一个负值。

下面给出一段实例代码,命名为 fork_example.c

#include <unistd.h>
#include <stdio.h>
int main (){pid_t fpid; // fpid 表示 fork 函数返回的值int count=0;fpid=fork();if (fpid < 0) printf("error in fork!");else if (fpid == 0) {printf("I am child. Process id is %d\n",getpid());}else {printf("i am parent. Process id is %d\n",getpid());}	return 0;
}

编译并执行,结果如下。

[root@local:~#] gcc -Wall fork_example.c && ./a.out
I am parent. Process id is 28365
I am child. Process id is 28366

代码执行过程中,在语句 fpid=fork() 之前,只有一个进程在执行这段代码,在这条语句之后,就变成父进程和子进程同时执行了。这两个进程几乎完全相同,将要执行的下一条语句都是 if (fpid < 0),同时 fpid=fork() 的返回值会依据所属进程返回不同的值。

使用 fork() 后,父进程有义务监控子进程的运行状态,并在子进程退出后自己才能正常退出,否则子进程就会成为 “孤儿” 进程。


后续将根据 Docker 内部对 namespace 资源隔离使用的方式分别对 6 种 namespace 进行详细的解析。

相关文章:

【Docker 内核详解】namespace 资源隔离(一):进行 namespace API 操作的 4 种方式

namespace 资源隔离&#xff08;一&#xff09;&#xff1a;进行 namespace API 操作的 4 种方式 1.通过 clone() 在创建新进程的同时创建 namespace2.查看 /proc/[pid]/ns 文件3.通过 setns() 加入一个已经存在的 namespace4.通过 unshare() 在原先进程上进行 namespace 隔离5…...

【技术研究】环境可控型原子力显微镜超高真空度精密控制解决方案

摘要&#xff1a;针对原子力显微镜对真空度和气氛环境精密控制要求&#xff0c;本文提出了精密控制解决方案。解决方案基于闭环动态平衡法&#xff0c;在低真空控制时采用恒定进气流量并调节排气流量的方法&#xff0c;在高真空和超高真空控制时则采用恒定排气流量并调节进气流…...

【Vuex+ElementUI】Vuex中取值存值以及异步加载的使用

一、导言 1、引言 Vuex是一个用于Vue.js应用程序的状态管理模式和库。它建立在Vue.js的响应式系统之上&#xff0c;提供了一种集中管理应用程序状态的方式。使用Vuex&#xff0c;您可以将应用程序的状态存储在一个单一的位置&#xff08;即“存储”&#xff09;中&#xff0c;…...

python经典百题之简单加密数据

题目:某个公司采用公用电话传递数据&#xff0c;数据是四位的整数&#xff0c;在传递过程中是加密的&#xff0c;加密规则如下&#xff1a; 每位数字都加上5,然后用和除以10的余数代替该数字&#xff0c;再将第一位和第四位交换&#xff0c;第二位和第三位交换 程序分析 对于…...

登陆认证权限控制(1)——从session到token认证的变迁 session的问题分析 + CSRF攻击的认识

前言 登陆认证&#xff0c;权限控制是一个系统必不可少的部分&#xff0c;一个开放访问的系统能否在上线后稳定持续运行其实很大程度上取决于登陆认证和权限控制措施是否到位&#xff0c;不然可能系统刚刚上线就会夭折。 本篇博客回溯登陆认证的变迁历史&#xff0c;阐述sess…...

单点接地、多点接地、混合接地

有三种基本的信号接地方式:浮地、单点接地、多点接地。 浮地&#xff1a;目的是使电路或设备与公共地线可能引起环流的公共导线隔离起来&#xff0c;浮地还使不同电位的电路之间配合变得容易。缺点&#xff1a;容易出现静电积累引起强烈的静电放电。折中方案&#xff1a;接入泄…...

【C++初阶(一)】学习前言 命名空间与IO流

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…...

flask vue跨域问题

问题&#xff1a; 调试时候跨域访问报&#xff1a; Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response. 解决办法&#xff1a; 安装flask_cros from flask_cors import CORS CORS(app) app.after_request def a…...

stm32(二十)IAP升级优化(双缓存,可恢复)

这次主要对STM32F103/Keil和LPC2478/IAR加了一个IAP在线升级功能&#xff0c; 主要记录一下自己的思路&#xff0c;无代码&#xff0c;实在是代码感觉没啥写的&#xff0c;都是一些网上很多流传的东西。 1、开发环境 Keilstm32f103JLINK 2、程序思路 在升级中&#xff0c;必…...

HDLbits:Exams/ece241 2013 q4

本题是一个实际的应用问题&#xff0c;一个水库&#xff0c;有三个传感器S1、S2、S3提供输入&#xff0c;经过控制电路&#xff0c;四个输出给到四个流量阀。也就是说&#xff0c;本题想让我们根据水位去控制流量阀。 问题的关键在于把什么抽象成state&#xff0c;答案是&…...

什么是React的虚拟DOM(Virtual DOM)?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

Response Status Code 301、302

目录 Information Django redirect Influence Information HTTP状态码301、302和304分别表示以下情况&#xff1a; codeinformation301&#xff08;Moved Permanently&#xff09; 永久重定向。当请求的资源已经被永久地移动到了一个新的URI时&#xff0c;服务器会返回这个…...

import { ref, onMounted, reactive } from ‘vue‘

ref, onMounted, reactive 用于创建和操作响应式数据、生命周期钩子。 1.ref 用来创建一个响应式的引用&#xff08;Reactive Reference&#xff09;的函数&#xff0c;主要用于创建基本数据类型&#xff08;如数字、字符串等&#xff09;的响应式数据。 通过 ref 创建的变…...

【TB作品】基于MSP430G2553单片机的超声波测距与报警系统,原理图,PCB

功能&#xff1a; 1 超声波测距显示 2 按键设置报警上下限 3 蜂鸣器报警 原理图&#xff1a; PCB样式&#xff1a; 实物&#xff1a; 代码&#xff1a; https://github.com/xddun/blog_code_search...

npm install报错

在命令提示符窗口下载npm&#xff0c;报错如下&#xff1a; $npm install报错信息如下&#xff1a; npm WARN old lockfile npm WARN old lockfile The package-lock.json file was created with an old version of npm, npm WARN old lockfile so supplemental metadata must…...

Flutter自定义model实体类

在某些场景下&#xff0c;我们可能需要自定义Flutter model实体类&#xff0c;来创建更加结构化和有组织的代码&#xff0c;提高代码的可重用性&#xff0c;并增强Flutter应用程序的整体可维护性。 自定义小部件:在创建自己的小部件时&#xff0c;可能需要定义自定义数据类型来…...

java项目实现不停服更新的4种方案(InsCode AI 创作助手)

文章目录 1. Blue-Green 部署2. 滚动更新3. 使用负载均衡器4. 灰度发布 在软件开发和维护中&#xff0c;不停机更新是确保应用程序持续可用的关键任务之一。以下是四种常见的不停机更新策略及其示例&#xff1a; 1. Blue-Green 部署 概念&#xff1a; Blue-Green 部署是一种部…...

7.1 yolov5优化模型时,自动标注xml数据

yolov5优化模型时&#xff0c;一般需要继续标注一些检测错误的图片&#xff0c;将其标为xml数据。以下是根据训练好的模型自动标注xml数据的python代码&#xff1a; 注意&#xff1a;代码中包含了本人的yolov5的测试过程&#xff0c;测试过程可以自己根据yolov5的测试文件自行…...

开发者职场“生存状态”大调研报告分析 - 第一版

听人劝、吃饱饭,奉劝各位小伙伴,不要订阅该文所属专栏。 作者:不渴望力量的哈士奇(哈哥),十余年工作经验, 跨域学习者,从事过全栈研发、产品经理等工作,现任研发部门 CTO 。荣誉:2022年度博客之星Top4、博客专家认证、全栈领域优质创作者、新星计划导师,“星荐官共赢计…...

在MySQL中使用!=还能走索引吗?

在MySQL中使用!还能走索引吗&#xff1f; 一般情况下&#xff0c;我们会在一个索引上较多的使用等值查询或者范围查询&#xff0c;此时索引大多可以帮助我们极快的查询出我们需要的数据。 那当我们在where条件中对索引列使用!查询&#xff0c;索引还能发挥他的作用吗&#xf…...

【算法题】2897. 对数组执行操作使平方和最大

题目&#xff1a; 给你一个下标从 0 开始的整数数组 nums 和一个 正 整数 k 。 你可以对数组执行以下操作 任意次 &#xff1a; 选择两个互不相同的下标 i 和 j &#xff0c;同时 将 nums[i] 更新为 (nums[i] AND nums[j]) 且将 nums[j] 更新为 (nums[i] OR nums[j]) &#…...

2023年中国划船机产量、销量及市场规模分析[图]

划船机是一种健身器材&#xff0c;它模拟了划船的运动&#xff0c;可以锻炼身体的肌肉力量和协调性。划船机通常由座椅、把手、脚踏板和传动装置组成&#xff0c;使用者可以通过拉动把手来模拟划船的动作&#xff0c;从而达到锻炼身体的目的。 划船机产业链 资料来源&#xff…...

Kafka和RabbitMQ的对比

Rabbitmq比kafka可靠&#xff0c;kafka更适合IO高吞吐的处理&#xff0c;比如ELK日志收集 Kafka和RabbitMq一样是通用意图消息代理&#xff0c;他们都是以分布式部署为目的。但是他们对消息语义模型的定义的假设是非常不同的。 a) 以下场景比较适合使用Kafka。如果有大量的事…...

ffmpeg从一个视频中提取音频

ffmpeg -i ~/video/video.mp4 -vn -acodec copy ~/video/audioFile.m4a 从video.mp4中提取音频到文件audioFile.m4a中 查看提取的音频文件 ffprobe ~/video/audioFile.m4a...

CCF CSP题解:坐标变换(其一)(202309-1)

链接 OJ链接&#xff1a;传送门 AC代码 #include <iostream>using namespace std;int n, m;int dx 0, dy 0;int main() {cin >> n >> m;for (int i 0; i < n; i) {int x, y;cin >> x >> y;dx x;dy y;}for (int i 0; i < m; i) {i…...

跳表C语言

【C语言】算法学习跳表_c语言跳表-CSDN博客 leetcode原题&#xff0c;代码如下 #define MAX(a, b) ((a) > (b) ? (a) : (b)) const int MAX_LEVEL 32; const int P_FACTOR RAND_MAX >> 2;typedef struct SkiplistNode {int val;int maxLevel;struct SkiplistNode…...

【JavaEE】_tomcat的安装与简单使用

目录 1. 安装tomcat 1.1 下载tomcat并解压缩 1.2 启动tomcat 1.3 访问tomcat欢迎页面 2. tomcat简单使用&#xff1a;部署前端代码 3. 基于tomcat的网站后端开发 tomcat是一个HTTP服务器&#xff0c;HTTP协议就是HTTP客户端与HTTP服务器之间通信使用的协议。 其中HTTP客…...

React 状态管理 - Context API 前世今生(上)旧版v16.3前

目录 扩展学习资料 Context api before React v16.3 Context 实战使用-Context Context VS Props Context Props Context的缺陷 New Context API 的实践 扩展学习资料 名称 链接 备注 new context api https://reactjs.org/docs/context.html 英文 old context …...

微服务、SOA 和 API 之间的区别

在软件开发中&#xff0c;组织的投资方式发生了重大转变&#xff0c;部署了面向架构的方法。这一切都始于 SOA&#xff0c;然后转变为我们称之为微服务的东西。添加到其中的是另一个概念&#xff0c;指定为 API。 在过去的几年里&#xff0c;SOA 和微服务仍然是讨论的话题。随…...

python打印正反直角三角形

我们用while循环&#xff0c;第一行打印一颗星&#xff0c;第二行打印两颗星&#xff0c;依次循环到五颗 我们写while循环时&#xff0c;先定义一个变量&#xff0c;然后在循环中增加值 i0 while < 5:j0while j <i:print(*,end\t)j1print() # 换行i1我们还可以打印反…...