【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 进程退出并终止,新的命名空间也会被销毁。
    • 新命名空间中的所有挂载和文件都会被销毁,因为命名空间的生命周期与执行该命令的进程相关。当进程退出时,整个命名空间也会被清理,从而删除其中的所有文件。
相关推荐
Tlzns2 分钟前
Linux网络——UDP的运用
linux·网络·udp
码农土豆8 分钟前
PaddlePaddle飞桨Linux系统Docker版安装
linux·docker·paddlepaddle
Hacker_xingchen13 分钟前
天融信Linux系统安全问题
linux·运维·系统安全
丘狸尾14 分钟前
[cisco 模拟器] ftp服务器配置
android·运维·服务器
黑客老陈22 分钟前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
张暮笛23 分钟前
抓取手机HCI日志
linux
大猫和小黄25 分钟前
Windows、CentOS环境下搭建自己的版本管理资料库:GitBlit
linux·服务器·windows·git
Joyner201829 分钟前
【Linux】ubuntu通过远程命令行启动桌面应用
linux·服务器·ubuntu
ghostwritten32 分钟前
Linux Swap: 深入解析 mkswap, mkfs.swap, 和 swapon
linux·运维·服务器
我是唐青枫34 分钟前
Linux xargs 命令使用教程
linux·运维·服务器