【Docker 内核详解】namespace 资源隔离(三):PID namespace
namespace 资源隔离(三):PID namespace
- 1.PID namespace 中的 init 进程
- 2.信号与 init 进程
- 3.挂载 proc 文件系统
- 4.unshare() 和 setns()
PID namespace 隔离非常实用,它对进程 PID 重新标号,即两个不同 namespace 下的进程可以有相同的 PID。每个 PID namespace 都有自己的计数程序。内核为所有的 PID namespace 维护了一个树状结构,最顶层的是系统初始时创建的,被称为 root namespace。它创建的新 PID namespace 被称为 child namespace(树的子节点),而原先的 PID namespace 就是新创建的 PID namespace 的 parent namespace(树的父节点)。通过这种方式,不同的 PID namespace 会形成一个层级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响。反过来,子节点却不能看到父节点 PID namespace 中的任何内容,由此产生如下结论。
- 每个
PID namespace中的第一个进程 “PID 1”,都会像传统 Linux 中的init进程一样拥有特权,起特殊作用。 - 一个
namespace中的进程,不可能通过kill或ptrace影响父节点或者兄弟节点中的进程,因为其他节点的 PID 在这个namespace中没有任何意义。 - 如果你在新的
PID namespace中重新挂载/proc文件系统,会发现其下只显示同属一个PID namespace中的其他进程。 - 在
root namespace中可以看到所有的进程,并且递归包含所有子节点中的进程。
到这里,大家可能已经联想到一种在外部监控 Docker 中运行程序的方法了,就是监控 Docker daemon 所在的 PID namespace 下的所有进程及其子进程,再进行筛选即可。
下面通过运行代码来感受一下 PID namespace 的隔离效果。修改 前一篇博客 中提到的代码,加入 PID namespace 的标识位,并把程序命名为 pid.c。
// [...]
int child_pid = clone(child_main, child_stack + STACK_SIZE,CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD, NULL);
// [...]
编译运行可以看到如下结果。
root@local:~# gcc -Wall pid.c -o pid.o && ./pid.o
程序开始:
在子进程中!
root@NewNamespace:~# echo $$
1 <<--注意此处 shell 的 PID 变成了 1
root@NewNamespace:~# exit
exit
已退出
打印 $$ 可以看到 shell 的 PID,退出后如果再次执行可以看到效果如下。
root@local:~# echo $$
17542
已经回到了正常状态。有的读者可能在子进程的 shell 中执行了 ps aux 或 top 之类的命令,发现还是可以看到所有父进程的 PID,那是因为还没有对文件系统挂载点进行隔离,ps 或 top 之类的命令调用的是真实系统下的 /proc 文件内容,看到的自然是所有的进程。所以,与其他的 namespace 不同的是,为了实现一个稳定安全的容器,PID namespace 还需要进行一些额外的工作才能确保进程运行顺利,下面将逐一介绍。
1.PID namespace 中的 init 进程
在传统的 Unix 系统中,PID 为 1 1 1 的进程是 init,地位非常特殊。它作为所有进程的父进程,维护一张进程表,不断检查进程的状态,一旦有某个子进程因为父进程错误成为了 孤儿 进程,init 就会负责收养这个子进程并最终回收资源,结束进程。所以在要实现的容器中,启动的第一个进程也需要实现类似 init 的功能,维护所有后续启动进程的运行状态。
当系统中存在树状嵌套结构的 PID namespace 时,若某个子进程成为孤儿进程,收养该子进程的责任就交给了该子进程所属的 PID namespace 中的 init 进程。
至此,可能读者已经明白了内核设计的良苦用心。PID namespace 维护这样一个树状结构,有利于系统的资源监控与回收。因此,如果确实需要在一个 Docker 容器中运行多个进程,最先启动的命令进程应该是具有资源监控与回收等管理能力的,如 bash。
2.信号与 init 进程
内核还为 PID namespace 中的 init 进程赋予了其他特权 —— 信号屏蔽。如果 init 中没有编写处理某个信号的代码逻辑,那么与 init 在同一个 PID namespace 下的进程(即使有超级权限)发送给它的该信号都会被屏蔽。这个功能的主要作用是防止 init 进程被误杀。
那么,父节点 PID namespace 中的进程发送同样的信号给子节点中的 init 进程,这会被忽略吗?父节点中的进程发送的信号,如果不是 SIGKILL(销毁进程)或 SIGSTOP(暂停进程)也会被忽略。但如果发送 SIGKILL 或 SIGSTOP,子节点的 init 会强制执行(无法通过代码捕捉进行特殊处理),也即是说父节点中的进程有权终止子节点中的进程。
一旦 init 进程被销毁,同一 PID namespace 中的其他进程也随之接收到 SIGKILL 信号而被销毁。理论上,该 PID namespace 也不复存在了。但是如果 /proc/[pid]/ns/pid 处于被挂载或者打开状态,namespace 就会被保留下来。然而,保留下来的 namespace 无法通过 setns() 或者 fork() 创建进程,所以实际上并没有什么作用。
当一个容器内存在多个进程时,容器内的 init 进程可以对信号进行捕获,当 SIGTERM 或 SIGINT 等信号到来时,对其子进程做信息保存、资源回收等处理工作。在 Docker daemon 的源码中也可以看到类似的处理方式,当结束信号来临时,结束容器进程并回收相应资源。
3.挂载 proc 文件系统
前文提到,如果在新的 PID namespace 中使用 ps 命令查看,看到的还是所有的进程,因为与 PID 直接相关的 /proc 文件系统(procfs)没有挂载到一个与原 /proc 不同的位置。如果只想看到 PID namespace 本身应该看到的进程,需要重新挂载 /proc,命令如下。
root@NewNamespace:~# mount -t proc proc /proc
root@NewNamespace:~# ps a
PID TTY STAT TIME COMMAND
1 pts/1 S 0:00 /bin/bash
12 pts/1 R+ 0:00 ps a
可以看到实际的 PID namespace 就只有两个进程在运行。
此时并没有进行 mount namespace 的隔离,所以该操作实际上已经影响了 root namespace 的文件系统。当退出新建的 PID namespace 以后,再执行 ps a 时,就会发现出错,再次执行 mount -t proc proc /proc 可以修复错误。后面还会介绍通过 mount namespace 来隔离文件系统,当我们基于 mount namespace 实现了容器 proc 文件系统隔离后,我们就能在 Docker 容器中使用 ps 等命令看到与 PID namespace 对应的进程列表。
4.unshare() 和 setns()
前一篇博客 就提到了 unshare() 和 setns() 这两个 API,在 PID namespace 中使用时,也有一些特别之处需要注意。
unshare() 允许用户在原有进程中建立命名空间进行隔离。但创建了PID namespace后,原先 unshare() 调用者进程并不进入新的 PID namespace,接下来创建的子进程才会进入新的 namespace,这个子进程也就随之成为新 namespace 中的 init 进程。
类似地,调用 setns() 创建新 PID namespace 时,调用者进程也不进入新的 PID namespace,而是随后创建的子进程进入。
为什么创建其他 namespace 时 unshare() 和 setns() 会直接进入新的 namespace,而唯独 PID namespace 例外呢?因为调用 getpid() 函数得到的 PID 是根据调用者所在的 PID namespace 而决定返回哪个 PID,进入新的 PID namespace 会导致 PID 产生变化。而对用户态的程序和库函数来说,它们都认为进程的 PID 是一个常量,PID 的变化会引起这些进程崩溃。
换句话说,一旦程序进程创建以后,那么它的 PID namespace 的关系就确定下来了,进程不会变更它们对应的 PID namespace。在 Docker 中,docker exec 会使用 setns() 函数加入已经存在的命名空间,但是最终还是会调用 clone() 函数,原因就在于此。
相关文章:
【Docker 内核详解】namespace 资源隔离(三):PID namespace
namespace 资源隔离(三):PID namespace 1.PID namespace 中的 init 进程2.信号与 init 进程3.挂载 proc 文件系统4.unshare() 和 setns() PID namespace 隔离非常实用,它对进程 PID 重新标号,即两个不同 namespace 下的…...
1600*C. Game On Leaves(博弈游戏树)
Problem - 1363C - Codeforces 解析: 我们将目标结点 x 当作树的根,显然,到当 x 的度为 1 的时候,此时行动的人胜利。 我们假设现在的情况为,只剩余三个点,再选择任意一个点,则对方获胜。但是两…...
Apache Ant的安装
介绍 Apache Ant是一个Java库和一个 命令行工具,可以用来构建Java应用。Ant提供了许多内置的任务(tasks),可以编译、组装、测试、运行Java应用。Ant也可以构建非Java应用,例如C、C应用。 Ant非常灵活,没有…...
考研:数学二例题--∞−∞和0⋅∞型极限
前言 本文只是例题,建议先参考具体如何做这类型例题。请到主文章中参考:https://blog.csdn.net/grd_java/article/details/132246630 ∞ − ∞ 和 0 ⋅ ∞ \infin - \infin 和 0\infin ∞−∞和0⋅∞ 例题 例1: lim x → ∞ x 2 x 2 −…...
C++算法:图中的最短环
题目 现有一个含 n 个顶点的 双向 图,每个顶点按从 0 到 n - 1 标记。图中的边由二维整数数组 edges 表示,其中 edges[i] [ui, vi] 表示顶点 ui 和 vi 之间存在一条边。每对顶点最多通过一条边连接,并且不存在与自身相连的顶点。 返回图中 …...
C++学习——类其实也是一种作用域
以下内容源于C语言中文网的学习与整理,非原创,如有侵权请告知删除。 其实也是一种作用域,每个类都会定义它自己的作用域。在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针或对象…...
Seata入门系列【4】undo_log、global_table、branch_table、lock_table字段及作用详解
1 客户端 1.1 undo_log 在AT模式中,需要在参与全局事务的数据库中,添加一个undo_log表,建表语句如下: SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for undo_log -- --…...
虚幻引擎:数据表格的C++常用API
1.将数据表格中的所有数据存到一个数组中 //参数1 // 错误提示 //参数2 // 存储的数组 TArray<FKeyInfoHeader*> array; KeyInfoDT->GetAllRows<FKeyInfoHeader>(TEXT("错误"),array); 2.获取表格中所有的行名称 TArray<FName>array; …...
Java日期格式化(DateFormat类和SimpleDateFormat类)
格式化日期表示将日期/时间格式转换为预先定义的日期/时间格式。例如将日期“Fri May 18 15:46:24 CST2016” 格式转换为 “2016-5-18 15:46:24 星期五”的格式。 在 java 中,可以使用 DateFormat 类和 SimpleDateFormat 类来格式化日期,下面详细介绍这两…...
centos 7 lamp owncloud
OwnCloud是一款开源的云存储软件,基于PHP的自建网盘。基本上是私人使用,没有用户注册功能,但是有用户添加功能,你可以无限制地添加用户,OwnCloud支持多个平台(windows,MAC,Android&a…...
屏幕亮度调节保护您的眼睛
官方下载地址: 安果移动 视频演示:屏幕亮度调节-保护您的眼睛_哔哩哔哩_bilibili 嗨,亲爱的用户,你是否有过这样的体验:夜晚安静的时刻,想要在抖音上看看热门的舞蹈、在快手上发现生活的 趣味、或是在哔…...
CentOS Linux下CMake二进制文件安装并使用Visual Studio调试
cmake安装——二进制安装(很简单,推荐!!) 1)下载二进制包。首先就是官网下载二进制安装包(我们是64位系统,就下载对应的包),这里。 例如:在/home/DOWNLOAD目录下执行,即下载二进制…...
ASP.net相关目录,相关配置文件和.后缀名解释
App_Data:用于存储应用程序的数据文件,例如数据库文件或其他本地文件。 App_Start:包含应用程序的启动配置文件,例如路由配置、日志配置等。 Content:存放应用程序的静态资源文件,如 CSS、JavaScript、图…...
一键批量转换,轻松将TS视频转为MP4视频,实现更广泛的播放和分享!
在享受精彩视频内容的同时,有时我们可能会面临一个问题:某些视频格式可能不太适合我们的播放设备或分享平台。特别是TS格式的视频,在一些情况下可能无法直接播放或上传。但是不用担心,因为我们为您提供了一款强大的视频剪辑工具&a…...
【Redis】使用Java客户端操作Redis
目录 引入jedis依赖连接Redis命令get/setexists/delkeysexpire/ttltype 引入jedis依赖 连接Redis 命令 get/set exists/del keys expire/ttl type...
BSPHP 未授权访问 信息泄露
漏洞描述 BSPHP 存在未授权访问 泄露用户 IP 和 账户名信息 漏洞复现 访问url: 构造payload访问: /admin/index.php?madmin&clog&atable_json&jsonget&soso_ok1&tuser_login_log&page1&limit10&bsphptime16004073…...
Learning Sample Relationship for Exposure Correction 论文阅读笔记
这是中科大发表在CVPR2023的一篇论文,提出了一个module和一个损失项,能够提高现有exposure correction网络的性能。这已经是最近第三次看到这种论文了,前两篇分别是CVPR2022的ENC(和这篇文章是同一个一作作者)和CVPR20…...
Vue项目 -- 解决Eslint导致的console报错问题
在利用vue-cli3构建的项目中引入eslint进行语法检查时,使用console.log(‘xxx’)时,控制台抛出了Unexpected console statement (no-console) 异常, 例:一使用console就提示报错 解决办法是: 在 .eslintrc.js 文件中…...
uni-app 在已有的数据对象中动态添加更多的数据对象
原数据对象 flowData: {list: [], // 数据值column: 2, // 瀑布列数columnSpace: 2 // 瀑布列宽间距 } 动态添加后的数据对象 flowData: {list: [], // 数据值column: 2, // 瀑布列数columnSpace: 2, // 瀑布列宽间距column_1: [],column_2: [] } 动态添加更多的数据对象的…...
【LeetCode】17. 电话号码的字母组合
1 问题 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:digits “23” 输出&…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
