Cgroupv2学习

概念

cgroup 是 Linux 内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘输入输出等)。

测试进程加入Cgroup控制

  1. 创建Cgroup子系统(默认cgroup根目录都在 /sys/fs/cgroup中)

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 查看目录内容

    在这里插入图片描述

  • cgroup.procs 这个文件可读写,可以查看或者写入需要被限制cgroup的进程ID/限制ID。
  • cgroup.max.depth cgroup子目录的最大深度
  • memory.max 限制cgroup最大使用内存
  • pids.max 限制cgroup创建的最大pid数
  • cpuset.cpus 限制进程使用的cpu核心
  • memory.stat cgroup使用的内存信息
  • pids.current:显示当前cgroup中的进程个数。包括其子孙cgroup。

注意:
cgroup.controllers:当前cgroup可以限制的资源
cgroup.subtree_control: 控制了子组的cgroup可以限制的资源

  1. 设置cgroup内存限制 1MB
    在这里插入图片描述

  2. 启动测试程序

 func main() {
    time.Sleep(time.Second \* 10)
    fmt.Println("开始了")
    var lastTime time.Time
    var test = "terminating"
    fmt.Println(lastTime.Nanosecond())
    err := func() error {
    fmt.Println("err: in err")

        	return nil
        }()
        switch {
        case err != nil:
        	fmt.Println("not nil")
        case err == nil:
        	fmt.Println("nil")
        case test == "terminating":
        	fmt.Println("terminating")

        }
        fmt.Println(a.(string))
        time.Sleep(time.Minute * 3)
        //申请内存
        testString := make([]string, 0, 9999999999)
        fmt.Println(unsafe.Sizeof(testString))
        time.Sleep(time.Minute * 1000)

        fmt.Println("结束了")

    }
  1. 程序被kill掉,观察机器日志内容

机器日志内容:

Apr  1 17:11:44 localhost kernel: [1809761.449573] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
Apr  1 17:11:44 localhost kernel: [1809761.449576] [  56726]     0 56726 39913984      638    69632        0             0 test-demo
Apr  1 17:11:44 localhost kernel: [1809761.449579] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=test-demo,mems_allowed=0,oom_memcg=/test-demo,task_memcg=/test-demo,task=test-demo,pid=56726,uid=0
Apr  1 17:11:44 localhost kernel: [1809761.449594] Memory cgroup out of memory: Killed process 56726 (test-demo) total-vm:159655936kB, anon-rss:1460kB, file-rss:1092kB, shmem-rss:0kB, UID:0 pgtables:68kB oom_score_adj:0

Linux namespace 资源隔离

UTS隔离(主机名信息)

环境信息及操作
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/utsname.h>

#define STACK_SIZE (1024 * 1024)

static int child_function(void *hostname) {
    // 修改子进程的主机名
    sethostname(hostname, sizeof(hostname));
    // 输出子进程的主机名
    printf("Child process: Hostname is %s\n", (char *)hostname);
    return 0;
}

int main() {
    char *child_stack;
    //定义子进程内的UTS命名空间名称
    char hostname1[] = "child-hostname";

    // 为子进程分配堆栈空间
    child_stack = malloc(STACK_SIZE);
    if (child_stack == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    // 创建子进程,在子进程中修改主机名
    pid_t pid = clone(child_function, child_stack + STACK_SIZE, CLONE_NEWUTS | SIGCHLD, hostname1);
    if (pid == -1) {
        perror("clone");
        exit(EXIT_FAILURE);
    }

    // 等待子进程退出
    waitpid(pid, NULL, 0);

    // 获取父进程的主机名并打印
    struct utsname uts;
    if (uname(&uts) == -1) {
        perror("uname");
        exit(EXIT_FAILURE);
    }

    printf("Parent process: Hostname is %s\n", uts.nodename);

    return 0;
}
执行
//编译执行
gcc -o uts uts.c  -pthread
./uts
结果验证
Child process: Hostname is child-hostname
Parent process: Hostname is vm-3-23-centos

IPC隔离(Linux的进程通信方式(信号量,共享内存,消息队列))

环境信息及操作
  1. 两个相同机器的shell终端
    在这里插入图片描述

  2. 在第二个终端使用 unshare -i ,加入到新的IPC命名空间
    在这里插入图片描述

  3. 在第二个终端中,创建IPC信号量,分别查看两个终端的ipc信息,可以看到此时两个终端的IPC信息是互相隔离的
    在这里插入图片描述

结果验证

把第一个终端的ipc加入到第二个终端中,然后验证结果,以及对其它uts等信息的隔离

在这里插入图片描述

使用到的linux工具和知识
  1. nsenter(可以进入不同的linux 命名空间): https://man7.org/linux/man-pages/man1/nsenter.1.html
  2. ipcmk(用于创建进程通信的信号量): https://wker.com/linux-command/ipcmk.html
  3. ipcs(用于查看进程通信方式的列表信息): https://wker.com/linux-command/ipcs.html
  4. unshare(让进程进入到新的命名空间): https://man7.org/linux/man-pages/man1/unshare.1.html
  5. linux进程间通信:https://www.cnblogs.com/huansky/p/13170125.html
  6. 参考文档: https://www.testerfans.com/archives/linux-namespace-ipc

Mount隔离(挂载点信息的隔离)

  1. 创建挂载目录 /mnt/shell-1和 /mnt/shell-2,并把目录制作镜像文件
    在这里插入图片描述

  2. 第一个终端中,确认挂载的mount namespace,把1.iso挂载到/mnt/shell-1上面
    在这里插入图片描述

  3. 第二个终端加入到新的mnt namespace,查看挂载信息,可以看到实现了挂载点的拷贝
    在这里插入图片描述

  4. 在第二个终端,卸载1.iso,挂载2.iso查看效果,看下与第一个终端的差异(在默认情况下 mnt namespace 会拷贝挂载信息)
    在这里插入图片描述

  5. 第一个终端的挂载信息
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果验证

通过上面的操作可以看到,两个终端的的挂载信息是互不影响的情况

使用到的linux知识和工具
  1. mkisofs(把目录生成镜像文件工具):https://www.runoob.com/linux/linux-comm-mkisofs.html
  2. 参考文章:https://www.testerfans.com/archives/linux-namespace-mount

Pid隔离(进程ID信息的隔离)

环境准备及操作

两个终端,其中一个终端加入到新的pid命名空间,可以看到子进程的PID号是 1
在这里插入图片描述
mount-proc是因为默认不同的mnt namespace挂载信息是共享的,/proc目录也是挂载的一部分,所以mount-proc可以解决共享挂载问题,让子进程的proc直接就是新的信息

Pid的嵌套
  1. 两个终端,一个终端使用三次 unshare --uts --mount-proc --pid --fork /bin/bash 不断创建子进程和pidnamespace等信息

  2. 另一个终端通过pstree -p pid查看第二个终端的pid,查看pid关系
    在这里插入图片描述

  3. 通过nsenter进入到子PIDnamespace中,查看进程关系,nsenter --pid --mount -t 854889 /bin/bash
    在这里插入图片描述

  4. 继续往深处进入, nsenter --pid --mount -t 854957 /bin/bash
    在这里插入图片描述

  5. 查看最后一个,nsenter --pid --mount -t 854957 /bin/bash
    在这里插入图片描述

为什么在pstree中没有看到 nsenter进入的PID呢?

在这里插入图片描述

可以看到,我们当前进程59和1号进程的PPID(父进程)都为0。但PID为59进程并不属于当前 PID namespace 中 init 进程的子进程,所以不会在pstree中显示。这也是我们嵌套PID namespace和最外层PID namespace不同的地方:子PID namespace可以有多个PPID为0的进程。

使用到的linux知识和工具
  1. pstree 查看进程树状结构
  2. nsenter 进入指定进程的namespace
  3. unshare 使进程进入新的namespace
  4. 参考文章:https://www.testerfans.com/archives/linux-namespace-pid

Network隔离(IP地址,协议,网卡等信息的隔离)

环境信息及操作
  1. 创建network namespace ip netns add liuchong-net,每创建一个netns,/var/run/netns/目录下面,会出现netns名称的文件,进入创建的netns,查看基本信息,可以看到 netns成功隔离了
  2. 使用 ip link set lo up 可以启动lo网卡,分配出一个127.0.0.1,可以ping通

在这里插入图片描述

多个netns进行通信
  1. 创建网桥
    在这里插入图片描述
    在这里插入图片描述

  2. 创建veth网卡对,并加入到liuchong-test netns中
    在这里插入图片描述

  3. 分配ip,启动网卡
    在这里插入图片描述

  4. veth网卡对另外一段绑定到网桥上
    在这里插入图片描述

  5. 配置另外一个netns
    在这里插入图片描述

结果验证

在这里插入图片描述

需要提醒的是 当只有两个veth互联,可以不创建网桥,直接创建veth网卡对分别放入到netns中,设置基本信息启动,可以直接互联。

使用到的linux知识和工具
  1. ip-netns(创建删除networkNamespace):https://man7.org/linux/man-pages/man8/ip-netns.8.html
  2. ip-link(对网卡的基本操作):https://man7.org/linux/man-pages/man8/ip-link.8.html
  3. iptables(linux防火墙):https://man7.org/linux/man-pages/man8/iptables.8.html

结语

通过这篇文章有没有解决大家的疑惑呢,下次再见,拜拜

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐