From e4c08e4b0aa49ec629020047c88f43664bda0ba2 Mon Sep 17 00:00:00 2001 From: li-chx Date: Sat, 30 Aug 2025 22:45:37 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=B7=BB=E5=8A=A0=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0=20CGroup=20=E5=AE=9E=E9=AA=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=20=E8=AE=B0=E5=BD=95=E4=B8=80=E6=AC=A1=E6=9C=89?= =?UTF-8?q?=E8=B6=A3=E7=9A=84=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CGroup 实验记录 记录一次有趣的工作流.md | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 2025-08/CGroup 实验记录 记录一次有趣的工作流.md diff --git a/2025-08/CGroup 实验记录 记录一次有趣的工作流.md b/2025-08/CGroup 实验记录 记录一次有趣的工作流.md new file mode 100644 index 0000000..7250e81 --- /dev/null +++ b/2025-08/CGroup 实验记录 记录一次有趣的工作流.md @@ -0,0 +1,106 @@ +--- +{ + title: "CGroup 实验记录 记录一次有趣的工作流", + description: "本文介绍了基于CGroup技术实现内存管理的开发过程。作者使用Vue3和.NET Core构建前后端,并通过C++封装libcgroup库操作CGroup。开发中采用WSL编译动态库并部署到Docker容器,使用CLion调试C++代码。解决了glibc版本兼容和FILENAME_MAX跨系统差异等问题,形成了一套容器化跨平台调试工作流。", + draft: false, + type: "article", + created_at: "2025-08-30T16:48:57+08:00", + published_at: "2025-08-30T22:41:18+08:00", + updated_at: [ "2025-08-30T22:41:18+08:00" ], + category: '实验', + tags: [ "后端", "Docker" ], + tech_stack: [ "Docker","C#", "C++" ], + tech_stack_percent: [ 70, 60, 40], + tech_stack_icon_names: [ "mdi:docker","mdi:language-csharp", "simple-icons:cplusplus" ], + tech_stack_theme_colors: [ "#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使用上限,一旦进程组使用的内存达到限额再申请内存,就会发出OOM(out 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启动调试时 +```mermaid +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 +```mermaid +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,结果出现报错。报错信息并不显然,这个问题卡了我一会儿的。 +