如何阻止后代线程自动进入父线程的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
相关产品推荐
相关产品推荐

