【Linux】资源隔离机制 — 命名空间(Namespace)详解

文章目录

  • 一、什么是Namespace
    • [1.1 Namespace 的作用与原理](#1.1 Namespace 的作用与原理)
    • [1.2 命名空间的操作](#1.2 命名空间的操作)
    • [1.3 常见命名空间](#1.3 常见命名空间)
    • [1.4 如何隔离两个容器中的进程](#1.4 如何隔离两个容器中的进程)
  • 二、相关命令
    • [2.1 dd 命令](#2.1 dd 命令)
    • [2.2 mkfs 命令](#2.2 mkfs 命令)
    • [2.3 df 命令](#2.3 df 命令)
    • [2.4 mount 命令](#2.4 mount 命令)
    • [2.5 unshare 命令](#2.5 unshare 命令)
  • 三、实际操作
    • [3.1 PID 隔离](#3.1 PID 隔离)
    • [3.2 Mount 隔离](#3.2 Mount 隔离)

一、什么是Namespace

Namespace(命名空间) 是 Linux 内核提供的一种资源隔离机制

  • 它允许将系统资源分隔成多个虚拟的"空间",每个命名空间内的进程只能访问该命名空间下的资源,而不能访问其他命名空间中的资源。

  • 通过 namespace,不同的进程可以在同一个操作系统内共享硬件资源,但又能感知到各自独立的环境。具体来说,namespace 可以将一个或多个进程的资源隔离到同一个命名空间中,确保这些进程只能看到和操作该命名空间内的资源。


1.1 Namespace 的作用与原理

Linux namespaces对全局系统资源的一种封装与隔离 。处于不同 namespace 的进程拥有独立的全局系统资源。修改某个 namespace 中的资源(如网络、PID、文件系统等)只会影响该命名空间中的进程,不会影响其他命名空间中的进程。

例如:

  • 一个进程可能在一个独立的网络命名空间中运行,无法看到其他命名空间的网络接口和路由。
  • 另一个进程可能在不同的用户命名空间中运行,从而拥有不同的用户ID和权限。

1.2 命名空间的操作

Linux 提供了几个系统调用来操作命名空间,常用的 API 包括:

  • clone():创建新进程并指定要使用的命名空间。
  • setns():使当前进程加入指定的命名空间。
  • unshare():将当前进程从现有的命名空间中分离出来,并创建新的命名空间。

在使用这些 API 时,通常需要指定一些参数来选择隔离的资源类型。

例如,可以通过按位或 | 组合多个命名空间类型来同时隔离多个资源。


1.3 常见命名空间

下面是一些常见的命名空间及其对应的系统调用参数和功能:

命名空间 系统调用参数 被隔离的全局系统资源 引入内核版本
UTS CLONE_NEWUTS 主机名和域名 2.6.19
IPC CLONE_NEWIPC 信号量、消息队列和共享内存(进程间通信) 2.6.19
PID CLONE_NEWPID 进程编号 2.6.24
Network CLONE_NEWNET 网络设备、网络栈、端口等 2.6.29
Mount CLONE_NEWNS 文件系统挂载点 2.4.19
User CLONE_NEWUSER 用户和用户组 3.8

下面是上面的命名空间容器下的隔离效果:

  1. UTS:每个容器可以拥有独立的主机名和域名,容器内的进程只看到自己的 hostname,互不干扰。
  2. IPC:同一个 IPC 命名空间内的进程可以相互通信,而不同的 IPC 命名空间中的进程则无法通信。
  3. PID:每个 PID 命名空间内的进程有独立的进程编号,因此每个容器可以有一个 PID 为 1 的 root 进程。
  4. Network:每个容器拥有独立的网络环境,包括网络设备、IP 地址、路由表、端口号等。容器内的网络与主机或其他容器的网络互相隔离。
  5. Mount:每个容器拥有独立的文件系统层次结构,容器内的进程只能看到自己的文件系统挂载点。
  6. User:每个容器内的进程可以拥有独立的用户和组 ID,与主机或其他容器的用户和组 ID 隔离。

1.4 如何隔离两个容器中的进程

在容器化环境中,如果我们要隔离容器 A 和容器 B 中的进程,需要从以下几个方面进行资源隔离:

  1. PID 隔离

    • 容器内的进程需要拥有独立的 PID(进程编号)。通过 PID 命名空间,容器 A 和容器 B 内的进程将有独立的 PID 系统,彼此无法相互识别或干扰。
  2. IPC 隔离

    • 容器 A 和容器 B 中的进程不能共享信号量、消息队列或共享内存等 IPC 资源。通过 IPC 命名空间,每个容器内的进程只能与同一容器中的进程进行通信,而无法访问其他容器的 IPC 资源。
  3. Mount 隔离

    • 容器内的文件系统应该是独立的,这样容器 A 的进程不能访问容器 B 的文件系统。通过 Mount 命名空间,每个容器的文件系统挂载点是独立的,不同容器之间的文件系统层次结构互不干扰。
  4. 网络隔离

    • 容器 A 和容器 B 的网络应该是独立的,容器 A 无法访问容器 B 的网络接口、端口或网络资源。通过 Network 命名空间,每个容器可以拥有独立的网络设备、IP 地址、路由表等资源。
  5. 主机名隔离

    • 每个容器应该有自己的主机名和域名,这样容器 A 内的进程无法读取容器 B 的主机名。通过 UTS 命名空间,每个容器可以有独立的主机名和域名。

二、相关命令

2.1 dd 命令

Linux 下 dd 命令用于读取、转换并输出数据。

dd 可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出;

  • 语法
bash 复制代码
dd OPTION
  • 参数 / 选项:
参数 说明
if=文件名 输入文件名,默认为标准输入(指定源文件)。
of=文件名 输出文件名,默认为标准输出(指定目标文件)。
ibs=bytes 一次读取 bytes 个字节,即指定输入块的大小。
obs=bytes 一次输出 bytes 个字节,即指定输出块的大小。
bs=bytes 同时设置读取和输出的块大小为 bytes 个字节。
cbs=bytes 一次转换 bytes 个字节,即指定转换缓冲区的大小。
skip=blocks 从输入文件开头跳过 blocks 个块后再开始复制。
seek=blocks 从输出文件开头跳过 blocks 个块后再开始复制。
count=blocks 仅复制 blocks 个块,块大小由 ibs 指定。
conv=<关键字> 文件转换方式,支持以下关键字:
ascii:将 EBCDIC 转换为 ASCII
ebcdic:将 ASCII 转换为 EBCDIC
ibm:将 ASCII 转换为 IBM 格式的 EBCDIC
block:将每行转换为长度为 cbs 的块,短于 cbs 的部分用空格填充
unblock:使每行的长度为 cbs,短于 cbs 的部分用空格填充
lcase:将大写字符转换为小写字符
ucase:将小写字符转换为大写字符
swap:交换输入中的每对字节
noerror:出错时不停止继续执行
notrunc:不截断输出文件
sync:将每个输入块填充到 ibs 字节,不足部分用 NUL 字符补齐
--help 显示帮助信息
--version 显示版本信息
  • 案例

    bash 复制代码
    # 生成一个大小为 80MB 的空白镜像文件 image.img,内容是 0 字节。
    dd if=/dev/zero of=image.img bs=8k count=10240
    # 将 testfile 文件中的所有英文字母转换为大写,然后转成为 testfile_1 文件
    dd if=testfile_2 of=testfile_1 conv=ucase

2.2 mkfs 命令

mkfs 命令 用于 在设备上创建 Linux 文件系统,即格式化,比如我们使用 U 盘的时候可以格式化。

  • 语法
bash 复制代码
mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
  • 参数解释
bash 复制代码
-t fstype:指定要建立何种文件系统;如 ext3, ext4filesys :指定要创建的文件系统对应的设备文件名;
blocks:指定文件系统的磁盘块数。
-V : 详细显示模式
fs-options:传递给具体的文件系统的参数
  • 案例

    bash 复制代码
    # 将 sda6 分区格式化为 ext4 格式
    mkfs -t ext4 /dev/sda6
    # 格式化镜像文件为 ext4
    mkfs -t ext4 ./image.img

2.3 df 命令

Linux df(disk free) 命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况

  • 语法

    bash 复制代码
    df [OPTION]... [FILE]...
  • 参数

  • -a, --all:包含所有的具有 0 Blocks 的文件系统

  • -h, --human-readable:使用人类可读的格式(预设值是不加这个选项的...)

  • -H, --si:很像 -h, 但是用 1000 为单位而不是用 1024

  • -t, --type=TYPE:限制列出文件系统的 TYPE

  • -T, --print-type:显示文件系统的形式

  • 案例

    bash 复制代码
    # 查看磁盘使用情况
    df -h
    # 查看磁盘的系统类型
    df -Th

2.4 mount 命令

mount 命令用于加载文件系统到指定的加载点 。也常用于挂载光盘,使我们可以访问光盘中的数据,因为将光盘插入光驱中后,Linux 并不会自动挂载,必须使用 mount 命令来手动完成挂载。

Linux 系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的,可以自由组合(通过挂载)不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。

挂载的实质是为磁盘添加入口(挂载点)。

  • 语法

    bash 复制代码
    mount [-l]
    mount [-t vfstype] [-o options] device dir
  • 参数

    • -l:显示已加载的文件系统列表;
    • -t: 加载文件系统类型支持常见系统类型的ext3 / ext4 / iso9660 / tmpfs / xfs 等,大部分情况可以不指定, mount 可以自己识别;
    • -o [options] 主要用来描述设备或档案的挂接方式。
      • loop:用来把一个文件当成硬盘分区挂接上系统
      • ro:采用只读方式挂接设备
      • rw:采用读写方式挂接设备
      • device: 要挂接(mount)的设备。
    • dir: 挂载点的目录
  • 案例

    bash 复制代码
    # 将 /dev/hda1 挂在 /mnt 之下。
    mount /dev/hda1 /mnt
    # 将镜像挂载到/mnt/testext4 下面,需要确保挂载点(目录)存在
    mkdir -p /mnt/testext4
    mount ./fdimage.img /mnt/testext4

2.5 unshare 命令

unshare 命令用于启动一个新的进程并与当前进程分离不同的命名空间

  • 语法
bash 复制代码
unshare [options] program [arguments]
  • 参数
参数 含义
-i, --ipc 不共享 IPC 空间
-m, --mount 不共享 Mount 空间
-n, --net 不共享 Net 空间
-p, --pid 不共享 PID 空间
-u, --uts 不共享 UTS 空间
-U, --user 不共享用户
-V, --version 查看版本信息
--fork 执行 unshare 的进程 fork 一个新的子进程,在子进程里执行传入的参数
--mount-proc 执行子进程前,将 proc 文件系统优先挂载过去
  • 案例

    bash 复制代码
    # 启动一个完全隔离的进程,既有独立的网络、进程 ID 和挂载空间
    unshare --net --pid --mount /bin/bash

三、实际操作

3.1 PID 隔离

  1. 首先在主机 shell 上执行 ps -ef 命令,可以查看进程列表,启动进程PID 为1的是init进程;

  2. 我们另开一个shell,新建一个PID的Namespace,执行下面的命令,进行进程隔离:

    bash 复制代码
    sudo unshare --fork --pid --mount-proc /bin/bash
    • --fork :新建了一个 bash 进程,如果不创建新进程,新的命名空间会将 unshare 的 PID 作为新的空间的父进程。但这个 unshare 进程并不在新的命名空间中,就会报错:Cannot allocate memory

    • --pid:表示进程通过 PID 隔离,而其他命名空间并没有隔离。

    • --mount-proc :在 Linux 下,每个进程都有一个对应的 /proc/PID 目录,该目录包含了大量有关当前进程的信息。对于一个 PID 命名空间而言,/proc 目录只包含当前命名空间及其所有子命名空间中的进程信息。

      创建一个新的 PID 命名空间后,如果想让子进程中的 topps 等依赖 /proc 文件系统的命令正常工作,需要挂载 /proc 文件系统。文件系统隔离是由 mount 命名空间管理的,因此 Linux 提供了 --mount-proc 选项来解决该问题。如果不带这个选项,我们仍然会看到宿主机的进程信息,而不是当前命名空间中的进程信息。

  3. 执行 ps -ef 查看进程信息,可以观察到进程空间内的内容已经更改,启动进程变成了bash进程

  1. 执行exit命令退出命名空间

    bash 复制代码
    exit

3.2 Mount 隔离

  1. 打开第一个 shell 窗口 A,执行命令, df -h ,查看主机默认命名空间的磁盘挂载情况

  2. 打开新的 shell 窗口 B,执行 Mount 隔离命令:

    bash 复制代码
    unshare --mount --fork /bin/bash
    mkdir -p /data/tmpmount
  3. 在窗口B 上 添加磁盘挂载:

    bash 复制代码
    dd if=/dev/zero of=image.img bs=8k count=10240
    mkfs -t ext4 ./image.img
    mount ./image.img /data/tmpmount
  4. 在窗口B挂载的磁盘 添加文件:

    bash 复制代码
    echo "Hello world!" > /data/tmpmount/hello.txt
  5. 使用 df -h 命令查看窗口 B 中的磁盘挂载信息:

  1. 分别查看磁盘A、B的文件信息:

    • 磁盘A:

    • 磁盘B:

  2. 再次查看窗口A的磁盘挂在信息,输出结果与第一次一致,根据磁盘挂载信息与文件信息,可以看出磁盘B中的文件在磁盘A中不存在,即成功实现了文件隔离。

  3. 窗口B通过 exit 命令进行退出

    bash 复制代码
    exit
  4. 关于文件的生命周期:

    • 如果窗口B在新的命名空间中创建了文件,它们的生命周期将由该命名空间控制。
    • 当执行 exit 命令时,窗口B的 Bash 进程退出并终止,新的命名空间也会被销毁。
    • 新命名空间中的所有挂载和文件都会被销毁,因为命名空间的生命周期与执行该命令的进程相关。当进程退出时,整个命名空间也会被清理,从而删除其中的所有文件。
相关推荐
眠修25 分钟前
Kuberrnetes 服务发布
linux·运维·服务器
BAOYUCompany2 小时前
暴雨服务器成功中标华中科技大学集成电路学院服务器采购项目
运维·服务器
yanwushu3 小时前
10分钟搭建 PHP 开发环境教程
php·laravel
鳄鱼皮坡3 小时前
仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器
运维·服务器
即将头秃的程序媛3 小时前
centos 7.9安装tomcat,并实现开机自启
linux·运维·centos
fangeqin3 小时前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
小Mie不吃饭4 小时前
FastAPI 小白教程:从入门级到实战(源码教程)
运维·服务器
爱奥尼欧5 小时前
【Linux 系统】基础IO——Linux中对文件的理解
linux·服务器·microsoft
戒不掉的伤怀5 小时前
【Navicat 连接MySQL时出现错误1251:客户端不支持服务器请求的身份验证协议;请考虑升级MySQL客户端】
服务器·数据库·mysql
超喜欢下雨天5 小时前
服务器安装 ros2时遇到底层库依赖冲突的问题
linux·运维·服务器·ros2