6.5 KiB
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 |
|
实验 |
|
|
|
|
|
前言
本来没想水博客的,但是与一位朋友交流后,他觉得这个工作流有点意思,建议我记录一下。 我不能保证这个工作流是最优的,但是在当时,它完美解决了我的问题
计算机系统实训题目描述
基于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提供了以下功能:
- 限制进程组可以使用的资源数量(Resource limiting)。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会发出OOM(out of memory)。
- 进程组的优先级控制(Prioritization)。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
- 记录进程组使用的资源数量(Accounting)。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间
- 进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
- 进程组控制(Control)。比如:使用freezer子系统将进程组挂起和恢复。
需要实现如下具体功能:
- CGroup内存管理的创建;
- CGroup设置内存资源限额;
- 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,结果出现报错。报错信息并不显然,这个问题卡了我一会儿的。