当前位置: 首页 > news >正文

cgroup机制

目录
  • 主要机制
  • 测试功能
    • 测试1
    • 测试2
    • 测试3

主要机制

测试功能

测试1

用户态测试-task1是主进程,先创建mygroup组,将task1的pid号加入到mygroup/cgroup.procs中,用task1调用system启动子进程task2,task2再调用system启动它的子进程task3,最后观察mygroup/cgroup.procs,可以发现task2和task3的pid被自动加入其中,证明了在用户态cgroup机制的可行性

/*---------------------------task1.c------------------------------*/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>#define CGROUP_PATH "/sys/fs/cgroup/mygroup"int add_pid_to_cgroup(pid_t pid) {char path[256];snprintf(path, sizeof(path), "%s/cgroup.procs", CGROUP_PATH);FILE *fp = fopen(path, "w");if (!fp) {perror("Failed to open cgroup.procs");return -1;}fprintf(fp, "%d", pid);fclose(fp);return 0;
}int main(){pid_t pid = getpid();printf("task1 PID: %d\n", pid);// 把主PID加入cgroupif (add_pid_to_cgroup(pid) != 0) {fprintf(stderr, "Failed to add main pid to cgroup\n");return -1;}sleep(5);printf("执行task2\n");system("./task2"); //system()自带同步阻塞机制,不用担心task2和task1进程会争抢cpureturn 0;
}/*------------------------------------task2.c---------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>int main(){pid_t pid = getpid();printf("task2 PID: %d\n", pid);sleep(5);printf("执行task3\n");system("./task3");//system(task2);
}/*-----------------------------------------------task3.c-------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>int main(){pid_t pid = getpid();printf("task3 PID: %d\n", pid);sleep(5);printf("结束\n");//system(task2);
}
/*----------------------------------------monitor.c----------------------------------*/
//用于监控/sys/fs/cgroup/mygroup/cgroup.procs#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>#define CGROUP_PROCS_PATH "/sys/fs/cgroup/mygroup/cgroup.procs"void print_cgroup_procs() {FILE *fp = fopen(CGROUP_PROCS_PATH, "r");if (!fp) {perror("Failed to open cgroup.procs");return;}printf("=== cgroup.procs snapshot ===\n");int pid;while (fscanf(fp, "%d", &pid) == 1) {printf("PID in cgroup: %d\n", pid);}fclose(fp);printf("=============================\n\n");
}int main() {printf("Monitoring %s\n", CGROUP_PROCS_PATH);while (1) {print_cgroup_procs();sleep(1);  // 每秒打印一次}return 0;
}

测试结果,taks2和task3自动继承到mygroup中,pid自动写入了文件/sys/fs/cgroup/mygroup/cgroup.procs

测试2

用户态测试-task1是主进程,先创建mygroup组,将task1的pid号加入到mygroup/cgroup.procs中,然后在线程中启动task2,观察mygroup/cgroup.procs,其中并没有task2的pid号,因为线程启动的进程pid和之前task1的是同一个,如果观察mygroup/cgroup.threads是有task2的启动记录的

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>#define CGROUP_PATH "/sys/fs/cgroup/mygroup"/* ---------- 把 task2 的代码改造成线程函数 ---------- */
void* task2_thread(void* arg)
{pid_t pid = getpid();        // 注意:线程里 PID 与主线程相同printf("task2 thread running, PID: %d  TID: %ld\n",pid, (long)pthread_self());sleep(20);return NULL;
}
/* -------------------------------------------------- */int add_pid_to_cgroup(pid_t pid)
{char path[256];snprintf(path, sizeof(path), "%s/cgroup.procs", CGROUP_PATH);FILE *fp = fopen(path, "w");if (!fp) {perror("Failed to open cgroup.procs");return -1;}fprintf(fp, "%d", pid);fclose(fp);return 0;
}int main(void)
{pid_t pid = getpid();printf("task1 PID: %d\n", pid);if (add_pid_to_cgroup(pid) != 0) {fprintf(stderr, "Failed to add main pid to cgroup\n");return EXIT_FAILURE;}sleep(5);printf("创建线程执行 task2 逻辑...\n");pthread_t tid;if (pthread_create(&tid, NULL, task2_thread, NULL) != 0) {perror("pthread_create");return EXIT_FAILURE;}pthread_join(tid, NULL);   // 主线程等待 task2 线程结束return 0;
}

测试结果:如果是主进程用线程启动一个任务,这个新任务的pid和主进程的pid是一样的,所以procs文件中没有新的pid号,但是threads文件中会有新任务的tid号

测试3

测试步骤:

  • 1.用户态下,手动创建目录/sys/fs/cgroup/mygroup/ 因为尝试了程序里面调用api创建mygroup,失败了,好多api是v1层级下使用的,也有好多函数没有导出符号给我们使用
  • 2.用cgroup_attach_task将主进程加入到mygroup,并且打印自身pid以及找出自身所在的cgroup的相对路径
  • 3.然后打印procs里面的内容
  • 4.kthread打印自身所在cgroup的相对路径
  • 5.kthread调用cgroup_attach_task将自己加入主进程组中,再次打印相对路径
  • 6.再次打印procs里面的内容

// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/cgroup.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kernel_read_file.h>#define BUF_LEN 256
#define NEW_CGROUP_PATH "mygroup"   /* 相对于根,不含前导 / *//* ---------- 回显 cgroup.procs ---------- */static void show_procs(void)
{struct file *filp;loff_t pos = 0;char buf[256];ssize_t ret;filp = filp_open("/sys/fs/cgroup/mygroup/cgroup.procs",O_RDONLY, 0);if (IS_ERR(filp)) {pr_err("open cgroup.procs failed: %ld\n", PTR_ERR(filp));return;}ret = kernel_read(filp, buf, sizeof(buf) - 1, &pos);if (ret >= 0) {buf[ret] = '\0';pr_info("cgroup.procs: %s", buf);}filp_close(filp, NULL);
}/* ---------- 子线程 ---------- */
static int child_fn(void *arg)
{char *buf;struct task_struct *parent = arg;buf = kmalloc(BUF_LEN, GFP_KERNEL);if (!buf)return -ENOMEM;cgroup_path(current->cgroups->dfl_cgrp, buf, BUF_LEN);pr_info("child[%d] (before): %s\n", current->pid, buf);show_procs();if (!cgroup_attach_task(parent->cgroups->dfl_cgrp, current, false))pr_info("child[%d] moved\n", current->pid);cgroup_path(current->cgroups->dfl_cgrp, buf, BUF_LEN);pr_info("child[%d] (after): %s\n", current->pid, buf);show_procs();kfree(buf);return 0;
}/* ---------- 初始化 ---------- */
static int __init whoami_init(void)
{struct cgroup *cgrp;char *buf;/* 1. 用户态必须提前建好 /sys/fs/cgroup/mygroup */cgrp = cgroup_get_from_path(NEW_CGROUP_PATH);if (IS_ERR(cgrp)) {pr_err("cannot find %s (%ld)\n", NEW_CGROUP_PATH, PTR_ERR(cgrp));return PTR_ERR(cgrp);}/* 2. 迁移主进程 */if (!cgroup_attach_task(cgrp, current, false))pr_info("parent[%d] moved to %s\n", current->pid, NEW_CGROUP_PATH);cgroup_put(cgrp);buf = kmalloc(BUF_LEN, GFP_KERNEL);if (buf) {cgroup_path(current->cgroups->dfl_cgrp, buf, BUF_LEN);pr_info("parent[%d] now in: %s\n", current->pid, buf);show_procs();kfree(buf);}/* 3. 启动子线程 */kthread_run(child_fn, current, "whoami_child");return 0;
}static void __exit whoami_exit(void)
{pr_info("module unloaded\n");
}module_init(whoami_init);
module_exit(whoami_exit);
MODULE_LICENSE("GPL");

将编译好的模块加入到内核中,测试结果如下:

sudo dmesg | tail[ 2875.672622] parent[4779] moved to mygroup
[ 2875.672629] parent[4779] now in: /mygroup
[ 2875.672670] cgroup.procs: 4779
[ 2875.672890] child[4780] (before): /
[ 2875.672910] cgroup.procs: 4779
[ 2875.672934] child[4780] moved
[ 2875.672935] child[4780] (after): /mygroup
[ 2875.672948] cgroup.procs: 47794780

可以看到,一开始procs里面只有parent进程的pid,后面在把child拉进mygroup之前,child默认加入的cgroup组是根目录"/sys/fs/cgroup/",在使用cgroup_attach_task函数将child拉进mygroup之后,procs里面变成了parent和child的两个进程pid

http://www.vanclimg.com/news/266.html

相关文章:

  • ls | tee 1.txt 如何拿到ls的返回值$?
  • 深入浅出:Clang中的控制流完整性(CFI)技术解析
  • 工业互联网甄选联盟会员组织正式成立,合作共赢
  • VK16K33AQ QNF28小体积封装大电流LED驱动电子烟LED屏显方案
  • HelloWorld
  • 颠覆性应用指南:EtherCAT转PROFINET网关的工业场景核爆方案大全
  • 如何将 Markdown格式文章快速发布到微信公众号.240516
  • Maven 镜像配置文件 maven-settings.xml
  • 图论
  • 开源能源管理系统:数字化时代能源安全与效能提升的核心引擎
  • 四.分支语句的简单应用
  • 使用AnythingLLM本地化投喂文件,简单三步快速本地化部署DeepSeek满血版看这篇!.250304
  • 循环for、while
  • 最小斯坦纳树
  • 浏览器跨标签页通信
  • 以太坊开发指南:SendTransaction vs CallContract 的区别与错误处理实践 - 若
  • Ntpdate系统时间同步
  • oracle 自增id
  • 接地气的软件开发流程.240618
  • 接地气的代码版本管理流程.240617
  • sersync同步
  • deepseek本地部署硬件资源对比表.250303
  • 【API接口】最新可用手机号归属地查询接口
  • NFS安装配置
  • Git代码分支管理模型TBD++ Flow.240520
  • deepseek-chat和deepseek-reasoner的区别.250305
  • grain和crops的区别
  • 【macOS】Homebrew更换国内镜像源(2025.7更新)
  • 第二十三天
  • SqlSugar的无实体(匿名)插入、更新、删除、查询以及多库和跨库查询 - microsoft