You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止后代线程自动进入父线程的Linux cgroup?

解决Linux cgroup中线程自动继承父cgroup的问题

cgroup v1 解决方案

cgroup v1里线程默认会继承父线程的cgroup,要避免这种自动继承,可从以下几个方向处理:

1. 创建线程后立即迁移回目标cgroup

在pthread_create创建子线程后,在新线程的入口函数开头,马上把自己迁移到预设的cgroup(比如父线程原本的cgroup,或是你指定的默认cgroup)。

具体操作步骤:

  • 父线程创建子线程前,先读取/proc/self/cgroup文件,提取cpu子系统对应的cgroup路径并保存。
  • 将这个路径作为参数传递给子线程。
  • 子线程启动后,获取自身tid,把tid写入目标cgroup的tasks文件,完成迁移。

示例代码片段:

// 父线程保存当前cpu cgroup路径
char parent_cgroup[256];
FILE *f = fopen("/proc/self/cgroup", "r");
if (f) {
    char line[256];
    while (fgets(line, sizeof(line), f)) {
        if (strstr(line, "cpu:")) {
            char *path = strchr(line, ':') + 1;
            strncpy(parent_cgroup, path, sizeof(parent_cgroup)-1);
            parent_cgroup[strcspn(parent_cgroup, "\n")] = '\0';
            break;
        }
    }
    fclose(f);
}

// 创建线程并传递父cgroup路径
pthread_t tid;
pthread_create(&tid, NULL, thread_func, parent_cgroup);

// 子线程入口函数
void *thread_func(void *arg) {
    char *parent_cgroup = (char*)arg;
    char tasks_path[512];
    snprintf(tasks_path, sizeof(tasks_path), "/sys/fs/cgroup/cpu%s/tasks", parent_cgroup);
    
    pid_t tid = syscall(SYS_gettid);
    FILE *f = fopen(tasks_path, "w");
    if (f) {
        fprintf(f, "%d\n", tid);
        fclose(f);
    }
    // 后续业务逻辑
    return NULL;
}

2. 用CLONE_NEWCGROUP标志创建线程(需特权)

如果进程拥有CAP_SYS_ADMIN权限,可在创建线程时添加CLONE_NEWCGROUP克隆标志,让新线程进入独立的cgroup命名空间,不会继承父线程的cgroup。之后再手动将其加入目标cgroup即可。

注意:此方法需要特权,且会创建新的cgroup命名空间,适合对隔离性有严格要求的场景。

示例代码片段:

pthread_attr_t attr;
pthread_attr_init(&attr);

// 添加CLONE_NEWCGROUP克隆标志
int flags;
pthread_attr_getclone(&attr, &flags);
flags |= CLONE_NEWCGROUP;
pthread_attr_setclone(&attr, flags);

// 创建线程
pthread_t tid;
pthread_create(&tid, &attr, thread_func, NULL);

pthread_attr_destroy(&attr);

cgroup v2 解决方案

cgroup v2对线程的控制更灵活,默认仍会继承父cgroup,但可通过以下方式规避:

1. 子线程启动后立即迁移

和v1思路类似,子线程启动后马上迁移到目标cgroup。v2的cgroup接口更统一,所有子系统共用同一路径,操作更简单:

示例代码片段:

// 父线程保存当前cgroup路径
char parent_cgroup[256];
FILE *f = fopen("/proc/self/cgroup", "r");
if (f) {
    fgets(parent_cgroup, sizeof(parent_cgroup), f);
    parent_cgroup[strcspn(parent_cgroup, "\n")] = '\0';
    fclose(f);
}

// 子线程入口函数
void *thread_func(void *arg) {
    char *parent_cgroup = (char*)arg;
    char procs_path[512];
    snprintf(procs_path, sizeof(procs_path), "/sys/fs/cgroup%s/cgroup.procs", parent_cgroup);
    
    pid_t tid = syscall(SYS_gettid);
    FILE *f = fopen(procs_path, "w");
    if (f) {
        fprintf(f, "%d\n", tid);
        fclose(f);
    }
    // 后续业务逻辑
    return NULL;
}

2. 使用CLONE_NEWCGROUP创建独立命名空间

和v1特权方案一致,拥有CAP_SYS_ADMIN权限的进程,可通过添加CLONE_NEWCGROUP标志让子线程进入独立cgroup命名空间,再手动分配cgroup。

额外注意事项

  • 线程池场景下,建议在初始化阶段就把所有工作线程统一迁移到指定的默认cgroup,避免后续任务线程自动继承问题。
  • 操作cgroup文件时需确保进程有对应的读写权限,通常需要root权限,或是给cgroup目录设置合适的权限。

内容的提问来源于stack exchange,提问作者JenyaKh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:35:13