jjzjj

Linux Cgroup v1(中文翻译)(1):Control Group

aosp 2023-03-28 原文

Control Groups

.
.
原文:https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html
翻译:河东西望
.
.

Written by Paul Menage <menage@google.com> based on CPUSETS
    Original copyright statements from CPUSETS:
    Portions Copyright (C) 2004 BULL SA.
    Portions Copyright (c) 2004-2006 Silicon Graphics, Inc.
    Modified by Paul Jackson <pj@sgi.com>
    Modified by Christoph Lameter <cl@linux.com>


1 控制组

1.1 什么是控制组?

控制组(Control Group)提供一种机制把任务(和他们的子任务)集合整合/分离成有特殊行为的层次化分组。

定义:
控制组(cgroup),把任务集合跟一个或者多个子系统的参数集合进行关联。

子系统(subsystem),是一种模块,利用控制组提供的任务分组功能,以特定的方式来处理任务分组。子系统是资源控制器,调度资源或者按cgroup来设置资源限制,但是它可能是影响一组进程的任何事物,例如虚拟子系统。

分层结构(hierachy),是一组树形结构的控制组,系统中的每个任务都会处在控制组和子系统集合的其中一个。每个子系统有状态来绑定到控制组。每个分层结构都有一个控制组虚拟文件系统实例相关联。

任何时候可以有多个激活的分层结构。每个分层结构是系统中所有任务的一种分区(partition)。

用户代码可以根据cgroup虚拟文件系统中的名称创建和销毁控制组,定义和查询任务被分配给哪个控制组,枚举分配给控制组的任务PID。这些创建和分配只影响跟控制组文件系统相关的分层结构。

控制组的唯一作用就是简单的任务跟踪。它的意图是其他子系统能钩进到通用控制组上来为控制组提供新属性,例如统计或者限制控制组内的进行能访问的资源。 例如,cpusets允许把cpu和内存节点和任务进行关联。

1.2 为什么需要控制组?

linux内核提供了多种进程整合方式来做资源跟踪,像cpusets,CKRM/ResGroups,UserBeanCounters和虚拟服务命名空间(namespace)。这些需要进程和子进程分组/分割(grouping/partitioning)的基本概念。

内核控制组补丁提供了最小的关键内核实现机制。它对系统快速路径只有很小的影响,还提供了针对特定子系统的钩子,例如cpusets。

多级分层结构支持,允许任务分隔成子系统的方式是完全不同的。平行分层结构允许每个分层是自然的任务隔离,不必处理复杂的任务(几个不相关的子系统必须强制到相同的控制组树)组合。

极端情况下每个资源控制器或者子系统可能在分离的分层结构中;另一种极端情况,所有的子系统可能隶属于同一个分层。

有一个应用场景示例可能对多分层结构的理解有好处。假设一个有很多用户(学生,教授,系统任务等)的大学服务器,这个服务器的资源规划应该是下面这样子的:

CPU :          "Top cpuset"
                /       \
        CPUSet1         CPUSet2
           |               |
        (Professors)    (Students)   
        In addition (system tasks) are attached to topcpuset 
        (so that they can run anywhere) with a limit of 20%
 Memory : Professors (50%), Students (30%), system (20%)
   Disk : Professors (50%), Students (30%), system (20%)
Network : WWW browsing (20%), Network File System (60%), others (20%)
                        / \
        Professors (15%)  students (5%)

浏览器firefox/lynx算作WWW网络类,而 (k)nfsd算作NFS网络类。与此同时,取决于谁来运行它,Firefox/Lynx将共享cpu/memory类。

为了能为不同的资源分类任务(在不同的分层结构内放置资源子系统),管理员很容易就能构建脚本来接收执行通知,然后根据是谁来运行的浏览器,他可以:

# echo browser_pid > /sys/fs/cgroup/<restype>/<userclass>/tasks

在只有单个层次结构的情况下,他必须要为每个运行的浏览器创建分离的控制组,然后关联合适的网络和其他的资源类。这可能导致这种控制组数量激增。

管理员可能临时为一个学生的浏览器增加网络访问,或者给一个学生的模拟器应用增加CPU算力。

下面的方式可以直接写PIDs到资源类中:

# echo pid > /sys/fs/cgroup/network/<new_class>/tasks
(after some time)
# echo pid > /sys/fs/cgroup/network/<orig_class>/tasks

没有这种机能,管理员将不得不把控制组切分成多个分离的控制组,然后关联新的资源类型。

1.3 控制组是如何实现的?

控制组在内核中的扩展方式如下:

  • 系统中每个任务有一个引用计数指针指向css_set。
  • css_set包含引用计数指针集合,指向cgroup_subsys_state对象,每个控制组子系统一个对象。(省略了几句话)
  • 控制组分层结构文件系统能被挂载出来给用户空间进行浏览和操作。
  • 可以枚举绑定到任何控制组的任务(通过PID)。

控制组的实现需要几个简单钩子钩进内核,他们都不在性能关键路径上:

  • 在init/main.c中,系统启动时,初始化根控制组(root cgroups)和css_set.
  • 在fork和exit时,从css_set中绑定和解绑任务。

除此之外,新的文件系统类型cgroup可以被用来挂载,能够浏览和修改控制组。当挂载控制组层次结构的时候,你可以定义以逗号分隔的子系统列表来作为挂载选项。默认情况下,挂载控制组文件系统会挂载包含所有已注册子系统的层次架构。

如果已经存在一个有完全相同子系统集合的激活的层次结构,它就会重新挂载。如果没有现存的层次架构匹配,以及现有架构中的子系统正在被使用,那么挂载将会失败(失败号-EBUSY),否则,一个新的分层就被激活,跟请求的子系统关联起来。

不能绑定新的子系统到激活的控制组结构上,或者从激活的控制组结构中解绑子系统。未来可以这么做,但是会存在严重的error-recovery问题。

当控制组文件系统被卸载时,如果下面有子控制组,即使已经卸载,该控制组结构仍会保持激活;如果没有子控制组,那么控制组结构将会被停用。

没有为控制组增加新的系统调用,对控制组的所有的查询和修改的操作支持都是通过控制组文件系统。

在/proc下的每个任务都有一个新增的cgroup文件,对每个激活的分层结构来说,子系统命名和控制组名称路径都是相对控制组文件系统根路径的。

每个控制组是由控制组文件系统中的目录表示的,它包含如下的文件:

  • tasks:隶属于控制组的任务列表(以PID来表示),这个列表不是按序排列的。写入线程ID到这个文件就表示移动线程到这个控制组。
  • cgroup.procs:线程组ID,这个列表不保证按序排列或者没有重复的TGIDs,如果需要的话,用户空间应当排序或者唯一化这个列表。写线程组ID到这个文件就会移动这个组的所有线程到本控制组。
  • notify_on_release: 在exit退出时运行release agent。
  • release_agent: 用来释放通知的路径。(这个文件仅仅存在顶层控制组中)

其他的子系统,像cpusets可能会在每个控制组路目录下添加额外的文件。

新的控制组可以通过mkdir系统调用或者shell命令来创建。控制组属性,例如标签,可以通过写入该控制组目录下的文件来修改。

嵌套控制组的命名分层结构允许把大系统分割成嵌套的、动态可变的软分区(soft-partitions)。

每个任务绑定到控制组时,在fork时其子任务自动继承绑定,允许在系统中组织工作负载到相关的任务集合中。如果控制组文件系统目录允许,任务可以被重新绑定到任何控制组。

当任务从一个控制组移动到另一个控制组,他就会获得一个新的css_set指针。如果有现存的css_set就可以重用,否则就分配新的css_set。现有的css_set通过查询哈希表来定位。

要允许从控制组来访问css_sets,一个g_cgroup_link对象集合形成一个格栅(lattice);每个g_cgroup_link被链接到一个g_cgroup_links列表(省略......)

控制组中的任务集合可以通过引用该控制组的css_set来枚举。

使用linux虚拟文件系统vfs来表示控制组分层结构,最小化改动内核代码,为控制组提供了常见的权限和命名空间。

1.4 notify_on_release是做什么的?

如果使能了控制组notify_on_release标记,那么只要控制组中的最后一个任务离开(退出或者绑定到其他的控制组)并且最后的子控制组被移除,内核就会运行分层结构根目录下的release_agent文件内容中定义的命令,提供废弃的控制组的路径名(相对控制组文件系统的挂载点)。这样能自动移除废弃的控制组。

在系统启动的时候,根控制组中的notify_on_release的默认值是diabaled(0).其他的控制组在创建时候的默认值是他们的父控制组的notify_on_release的当前值。

控制组分层的release_agent路径的默认值是空。

1.5 clone_children是做什么的?

这个标签仅仅影响cpuset控制器。如果clone_children标记在控制组中被使能enbale(1),新的cpuset控制组在初始化的时候就能复制父系的配置。

1.6 如何使用控制组?

要启动一个将要包含在某个控制组中的新工作任务,使用cpuset控制组子系统,操作步骤如下:

1) mount -t tmpfs cgroup_root /sys/fs/cgroup
2) mkdir /sys/fs/cgroup/cpuset
3) mount -t cgroup -ocpuset cpuset /sys/fs/cgroup/cpuset
4) Create the new cgroup by doing mkdir's and write's (or echo's) in
   the /sys/fs/cgroup/cpuset virtual file system.
5) Start a task that will be the "founding father" of the new job.
6) Attach that task to the new cgroup by writing its PID to the
   /sys/fs/cgroup/cpuset tasks file for that cgroup.
7) fork, exec or clone the job tasks from this founding father task.

举个例子,下面的命令序列将会创建一个名称为“Charlie”的控制组,仅仅包含CPU2和3,内存节点1,在控制组中启动一个子shell ‘sh’:

mount -t tmpfs cgroup_root /sys/fs/cgroup
mkdir /sys/fs/cgroup/cpuset
mount -t cgroup cpuset -ocpuset /sys/fs/cgroup/cpuset
cd /sys/fs/cgroup/cpuset
mkdir Charlie
cd Charlie
/bin/echo 2-3 > cpuset.cpus
/bin/echo 1 > cpuset.mems
/bin/echo $$ > tasks
sh
# The subshell 'sh' is now running in cgroup Charlie
# The next line should display '/Charlie'
cat /proc/self/cgroup

2 应用示例和语法

2.1 基本用法

创建、修改和使用控制组可以通过控制组虚拟文件系统来完成。

要挂载一个所有子系统都可用的控制组分层,输入命令:

# mount -t cgroup xxx /sys/fs/cgroup

内核代码解读不了“xxx”,但是它会出现在/proc/mounts中,因此它就可以是你想用的有用的身份字符串。

注意:没有用户提前输入的话,一些子系统不能工作。例如,如果cpusets被使能,用户必须为每个已经创建但是还没使用的控制组写入数据到cpu和mem文件中。

正如1.2章节所述,我们为什么需要控制组?你应该为每个你想要控制的资源或者资源组创建不同的控制组分层。因此,你可以挂载在/sys/fs/cgroup中tmpfs,然后为每个控制组资源或者资源组创建目录:

# mount -t tmpfs cgroup_root /sys/fs/cgroup
# mkdir /sys/fs/cgroup/rg1

要挂载只有cpuset和memory子系统的控制组分层,输入如下命令:

# mount -t cgroup -o cpuset,memory hier1 /sys/fs/cgroup/rg1

重新挂载控制组当前是支持的,但是不推荐使用。重新挂载允许改变子系统和release_agent。重新绑定几乎没有什么用,它只在分层为空并且release_agent本身应当被常规fsnotify替换的时候才会生效。重新挂载在未来会被移除。

要定义分层的release_agent:

# mount -t cgroup -o cpuset,release_agent="/sbin/cpuset_release_agent" xxx /sys/fs/cgroup/rg1

注意,如果多次定义release_agent,将会返回失败。

注意,子系统集合的变更当前被支持,仅限于由单个(根)控制组组成的分层。能够随时从现存的控制组分层绑定/解绑子系统,未来会考虑支持实现。

然后在/sys/fs/cgroup/rg1下,你能找到系统中的控制组树。例如/sys/fs/cgroup/rg1也可以是容纳整个系统的控制组。

如果你想要更改release_agent的值:

# echo "/sbin/new_release_agent" > /sys/fs/cgroup/rg1/release_agent

它也可以在重新挂载时更改。

如果你想在/sys/fs/cgroup/rg1下创建新的控制组:

# cd /sys/fs/cgroup/rg1
# mkdir my_cgroup

现在你想要用这个控制组来做些什么的话:

# cd my_cgroup

在这个目录下,你可以找到几个文件:

# ls
cgroup.procs notify_on_release tasks
(plus whatever files added by the attached subsystems)

现在绑定你的当前shell到这个控制组:

# /bin/echo $$ > tasks

你也能在你的控制组内部创建控制组,在这个目录下使用mkdir:

# mkdir my_sub_cs

要移除控制组,只要使用rmdir就可以:

# rmdir my_sub_cs

如果控制组正在使用中(内部有控制组,或者有进程绑定绑定,或者其他子系统相关的引用保持激活状态),这操作就会失败。

2.2 绑定进程

# /bin/echo PID > tasks

注意,这里是PID而不是PIDs,一次只能绑定一个任务。如果你有几个任务,只能一个个的绑定:

# /bin/echo PID1 > tasks
# /bin/echo PID2 > tasks
        ...
# /bin/echo PIDn > tasks

你也可以通过写入0来绑定当前的shell任务:

# echo 0 > tasks

你也可以使用cgroup.procs文件来代替tasks文件,一次性移除线程组中的所有任务。写入线程组中任何的任务PID到cgroup.procs中,线程组中的所有任务将会被绑定到该控制组。写入0到cgroup.procs中就会移动当前写任务的线程组中的所有任务。

注意:因为每个任务总是某个已挂载分层下的控制组的成员,要从当前控制组移除任务,你必须移动它到新的控制组(可能是根控制组),就是通过写入新控制组的tasks文件的方式。

注意:由于受到一些控制组子系统的强制限制,移动进程到另外的控制组可能会失败。

2.3 按名字挂载分层

当挂载控制组分层时传递name=选项,就会以给定的名字来关联分层。这么做是有用的,对于挂载一个已存在(pre-existing)的分层时,为了按名引用而不是按激活子系统的集合引用。每个分层或者是无名的或者是有一个唯一的名字。

名字应当匹配 [w.-]+

当传递name=选项给新的分层时,你需要手动定义子系统;当你给一个子系统命名一个名字,挂载所有子系统而什么都没有定义,这种行为是不被支持的。

子系统的名字作为分层的一部分会出现在/proc/mounts和/proc//cgroups中。

3 Kernel API

省略原文大概90行!我个人并不关注这一部分,所以没有翻译!!!

4 扩展属性用法

控制组文件系统支持它的目录和文件中扩展属性的特定类型,当前支持的类型是:

Trusted (XATTR_TRUSTED)
Security (XATTR_SECURITY)

他们都需要设置CAP_SYS_ADMIN功能。

跟在tmpfs中一样,控制组文件系统中的扩展属性使用内核内存来存储,建议保持最小使用。这就是为什么用户定义的扩展属性不支持的原因,因为任何用户都能这么做并且没有大小限制。

这个功能当前的已知用户是SELINUX,用来限制控制组在容器中和systemd的使用,以便对诸如控制组(systemd为每个服务创建的控制组)中的主PID这样的meta数据进行分类。

5 答疑

Q: 为什么要使用'/bin/echo'?  
A: bash内嵌的echo命令不会检查对write()调用的错误,如果你在控制组文件系统中使用它,你将不知道命令是否执行成功还是失败。  

Q: 当我绑定很多进程时,只有第一行被真正绑定?  
A: 每次对write()的调用只能返回一个错误,所以你应该就放一个PID。 

有关Linux Cgroup v1(中文翻译)(1):Control Group的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. 亚特兰蒂斯的回声(中文版): chatGPT 的杰作 - 2

    英文版英文链接关注公众号在“亚特兰蒂斯的回声”中踏上一段难忘的冒险之旅,深入未知的海洋深处。足智多谋的考古学家AriaSeaborne偶然发现了一件古代神器,揭示了一张通往失落之城亚特兰蒂斯的隐藏地图。在她神秘的导师内森·兰登教授的指导和勇敢的冒险家亚历克斯·默瑟的帮助下,阿丽亚开始了一段危险的旅程,以揭开这座传说中城市的真相。他们的冒险之旅带领他们穿越险恶的大海、神秘的岛屿和充满陷阱和谜语的致命迷宫。随着Aria潜在的魔法能力的觉醒,她被睿智勇敢的QueenNeria的幻象所指引,她让她为即将到来的挑战做好准备。三人组揭开亚特兰蒂斯令人惊叹的隐藏文明,并了解到邪恶的巫师马拉卡勋爵试图利用其古

  3. ruby - Google-api-ruby-client 翻译 API 示例 - 2

    很高兴看到google代码:google-api-ruby-client项目,因为这对我来说意味着Ruby人员可以使用GoogleAPI-s来完善代码。虽然我现在很困惑,因为给出的唯一示例使用Buzz,并且根据我的实验,Google翻译(v2)api的行为必须与google-api-ruby-client中的Buzz完全不同。.我对“Explorer”演示示例很感兴趣——但据我所知,它并不是一个探索器。它所做的只是调用一个Buzz服务,然后浏览它已经知道的关于Buzz服务的事情。对我来说,Explorer应该让您“发现”所公开的服务和方法/功能,而不一定已经知道它们。我很想听听使用这个

  4. ruby-on-rails - 如果特定语言环境中缺少翻译,如何配置 i18n 以使用 en 语言环境? - 2

    如果特定语言环境中缺少翻译,如何配置i18n以使用en语言环境翻译?当前已插入翻译缺失消息。我正在使用RoR3.1。 最佳答案 找到相似的question这里是答案:#application.rb#railswillfallbacktoconfig.i18n.default_localetranslationconfig.i18n.fallbacks=true#railswillfallbacktoen,nomatterwhatissetasconfig.i18n.default_localeconfig.i18n.fallback

  5. ruby-on-rails - 如何使用 globalize 和 rails 4 以一种形式显示所有翻译字段 - 2

    在使用rails4和https://github.com/globalize/globalize的情况下,我应该如何为我的模型编写表单?用于翻译。我想以一种形式显示所有翻译,如下例所示。我在这里找到了解决方案https://github.com/rilla/batch_translations但我不知道如何实现它。这个“批量翻译”是一个gem还是什么?以及如何安装它。EditingpostEnglish(defaultlocale)SpanishtranslationFrenchtranslation 最佳答案 批处理翻译gem很旧

  6. python - 用于从 Python 到 Ruby 查找集合的所有分区的翻译函数 - 2

    我有以下python函数来递归查找集合的所有分区:defpartitions(set_):ifnotset_:yield[]returnforiinxrange(2**len(set_)/2):parts=[set(),set()]foriteminset_:parts[i&1].add(item)i>>=1forbinpartitions(parts[1]):yield[parts[0]]+bforpinpartitions(["a","b","c","d"]):print(p)有人可以帮我把它翻译成ruby​​吗?这是我目前所拥有的:defpartitions(set)ifnots

  7. 「想体验ChatGPT中文聊天?」那快进来,你用不上算我输 - 2

    ♥️作者:白日参商🤵‍♂️个人主页:白日参商主页♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!!🎈🎈加油!加油!加油!加油🎈欢迎评论💬点赞👍🏻收藏📂加关注+!「想体验ChatGPT中文聊天?」那快进来,你用不上算我输项目场景:项目条件一、那就开始吧1、安装ChatGPT-Desktop2、OpenAPI设置二、使用实例恭喜你!!!配置成功了!!!API和URL都是博主免费提供给大家的!!!恭喜你!!!配置成功了!!!API和URL都是博主免费提供给大家的!!!🎈🎈加油!加油!加油!加油🎈欢迎评论💬点赞👍🏻收藏📂加关注+!项目场景:近几个月可以说ChatGPT是火得一

  8. 对于体育新闻中文文本关键字提取有哪些关键字提取算法及其步骤 - 2

    对于体育新闻中文文本的关键字提取,常用的算法包括TF-IDF、TextRank和LDA等。它们的基本步骤如下:1.TF-IDF算法: -将文本进行分词和词性标注处理。-统计每个词在文本中的词频(TF)。-计算每个词在整个语料库中出现的文档频率(DF)和逆文档频率(IDF)。-计算每个词的TF-IDF值,并按照值的大小进行排序,选择排名前几的词作为关键字。2.TextRank算法:-将文本进行分词和词性标注处理。-将分词结果转化成图模型,每个词语为节点,根据词语之间的共现关系建立边。-对图模型进行迭代计算,计算每个节点的PageRank值,表示该节点的重要性。-选择排名前几的节点作为关键字。3.

  9. ruby-on-rails - Rails_Admin - 如何更改 Post View 中文本字段的大小 - 2

    RailsAdmin中Post的“正文”输入区域的默认高度非常小。我想弄清楚如何增加高度。有什么建议么?config.modelPostdolabel'Blog'weight0editdofield:userfield:titlefield:body_formatfield:bodydo(somethinghere?)end 最佳答案 configure:descriptiondohtml_attributesrows:20,cols:50end 关于ruby-on-rails-Rail

  10. ruby - I18n.t 翻译缺少默认值 Nil - 2

    所以我知道如果我在读取yaml文件时遇到“翻译缺失:”如何返回默认值。some=I18n.t("something.something_else",default:"value")但是如果我希望默认值为nil,我该如何以Ruby的方式做到这一点呢?我知道我可以正则表达式并匹配变量some中的“translationmissing:”,如果它匹配,我会将它分配给nil。但我想做的是拥有some=I18n.t("something.something_else",default:nil)但它只是返回了我缺少的翻译。有谁知道好的方法吗? 最佳答案

随机推荐