BlogArticles/2025-08/CGroup 实验记录 记录一次有趣的工作流.md

6.5 KiB
Raw Blame History

title description draft type created_at published_at updated_at category tags tech_stack tech_stack_percent tech_stack_icon_names tech_stack_theme_colors
CGroup 实验记录 记录一次有趣的工作流 本文介绍了基于CGroup技术实现内存管理的开发过程。作者使用Vue3和.NET Core构建前后端并通过C++封装libcgroup库操作CGroup。开发中采用WSL编译动态库并部署到Docker容器使用CLion调试C++代码。解决了glibc版本兼容和FILENAME_MAX跨系统差异等问题形成了一套容器化跨平台调试工作流。 false article 2025-08-30T16:48:57+08:00 2025-08-30T22:41:18+08:00
2025-08-30T22:41:18+08:00
实验
后端
Docker
Docker
C#
C++
70
60
40
mdi:docker
mdi:language-csharp
simple-icons:cplusplus
#1c90ed
#a179dc
#00599c

前言

本来没想水博客的,但是与一位朋友交流后,他觉得这个工作流有点意思,建议我记录一下。 我不能保证这个工作流是最优的,但是在当时,它完美解决了我的问题

计算机系统实训题目描述

基于CGroup技术的内存管理

CGroup介绍

CGroup是Control Groups的缩写是OpenEuler内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物力资源(如cpu、memory、i/o等)的机制。CGroups是LXC为实现虚拟化所使用的资源管理手段。

CGroup功能及组成

CGroup是将任意进程进行分组化管理的OpenEuler内核功能。CGroup提供将进程进行分组化管理的功能和接口的基础结构I/O或内存的分配控制等具体的资源管理功能通过这个功能实现。这些具体的资源管理功能称为CGroup子系统或控制器。CGroup子系统有控制内存的Memory控制器、控制进程调度的CPU控制器等。运行中的内核可以使用的Cgroup子系统由/proc/cgroup来确认。 CGroup提供了一个CGroup虚拟文件系统作为进行分组管理和各子系统设置的用户接口。要使用CGroup必须挂载CGroup文件系统。这时通过挂载选项指定使用哪个子系统。Cgroups提供了以下功能

  1. 限制进程组可以使用的资源数量Resource limiting。比如memory子系统可以为进程组设定一个memory使用上限一旦进程组使用的内存达到限额再申请内存就会发出OOMout of memory
  2. 进程组的优先级控制Prioritization。比如可以使用cpu子系统为某个进程组分配特定cpu share。
  3. 记录进程组使用的资源数量Accounting。比如可以使用cpuacct子系统记录某个进程组使用的cpu时间
  4. 进程组隔离Isolation。比如使用ns子系统可以使不同的进程组使用不同的namespace以达到隔离的目的不同的进程组有各自的进程、网络、文件系统挂载空间。
  5. 进程组控制Control。比如使用freezer子系统将进程组挂起和恢复。

需要实现如下具体功能:

  1. CGroup内存管理的创建
  2. CGroup设置内存资源限额
  3. CGroup触发控制。

技术栈

由于技术不限,为了获得相对较好的成绩,同时兼顾开发速度体验,我选择了以下技术栈:

  • 前端Vue3 + TypeScript + Vite + Pinia + TDesign 没太多难点 后续文章不提
  • 后端:.Net Core API
  • 自构建链接库C++ Dynamic Link Library —— CGroupEncapsulation
  • 中间件Docker
  • 虚拟机WSL

工作流

想法与问题

CGroup 调用

CGroup 的调用不是大问题。 退一步,可以使用纯粹文件操作 + exec运行命令行工具但这样显然是不够优雅的。 我选择的方案是:使用 libcgroup 这个 C 语言库使用syscall而非调用系统命令行工具或直接进行文件操作这样做的好处是能够获得更完善的错误处理机制。 通过C#的P/Invoke可以较为方便的调用C/C++动态链接库但是也没那么方便所以我又添加了一个中间层封装一次libcgroup。

Docker 与调试问题

CGroup是Linux特有的我要么直接使用wsl要么使用Docker。 而CGroup也算比较关键的系统功能直接在wsl中运行可能会出现问题所以选择使用Docker。 使用Docker也会引入问题关键是动态链接库的构建和调试。我的选择是在wsl中构建动态链接库然后将动态链接库复制到Container中使用。 在该方法下是无法通过VS进行跨语言调试的我的解决方案是使用Clion连接到Docker Container编译CGroupEncapsulation项目然后使用main函数进行调试。 综上对于CGroupEncapsulation这个项目需要两种项目文件。对于VS将读取它的 CGroupEncapsulation.vcxproj 项目文件对于Clion将读取 CMakeLists.txt 项目文件。 这个工作流唯一的问题是重启VS会造成Container销毁Clion需要重联除此之外没有什么问题。

具体工作流

对于后端当VS启动调试时

stateDiagram-v2
    [*] --> WSL
    [*] --> BAC
    
    state WSL {
        state "CGroupEncapsulation Compile" as CC
        [*] --> CC
        CC --> [*]
    }
    state "Backend API Compile" as BAC
    WSL --> Docker : Copy dynamic link library
    BAC --> Docker : Copy backend API DLL
    state Docker {
        state ".Net Core API Debug" as API
        [*] --> API
        API --> [*]
    }
    Docker --> [*]

如果想要调试CGroupEncapsulation这个动态链接库则需要在Clion中连接到Docker Container 但是使用main文件运行 而不是附加到DLL

stateDiagram-v2
    [*] --> Docker
    state Docker {
        state "CGroupEncapsulation Debug" as CCD
        state "CGroupEncapsulation Compile as Runnable" as CCR
        [*] --> CCR
        CCR --> CCD : Run main
        CCD --> [*]
    }
    Docker --> [*]

如果能够在VS中附加到Docker的DLL同时能断点就更好了但是目前还没找到方法。我相信是有方法的但当时时间比较紧平衡好时间和技术也是编程的关键。

遇到的问题

WSL编译后的DLL执行时报错

结果是Docker的glibc版本过低调整编译选项降低到C++17后正常

controller_data 下 FILENAME_MAX 宏在不同系统中定义不同

FILENAME_MAX 在Windows中的值为260而在Linux中为4096之前在用VS查阅源代码时看到的值是260因此使用C# P/Invoke调用时传入的数组长度为260结果出现报错。报错信息并不显然这个问题卡了我一会儿的。