关于cgroup:长文解析作为容器底层技术的半壁江山-cgroup如何突破并发创建瓶颈

简介: io_uring 作为一种新型高性能异步编程框架,代表着 Linux 内核将来的方向,以后仍处于疾速倒退中。阿里云联结 InfoQ 发动《io_uring 介绍及利用实际》的技术公开课,围绕 OpenAnolis 龙蜥社区 Anolis OS 8 全方位解析高性能存储场景。 写在后面cgroup 作为容器底层技术的半壁江山,很多文章曾经介绍并总结得很好了,对于 cgroup 是什么、有什么用以及一些相干概念,这些内容并不是本文的重点所以也将不再赘述。 情谊揭示:以下内容默认读者曾经初步理解 task、cgroup、subsys、hierarchy 是什么及它们之间的关系。 咱们为啥关注 cgroup 管制立体性能?云原生目前是云计算畛域的重点倒退方向,其中的函数计算场景中,函数执行的速度是重要的性能指标,要求可能疾速、高并发地创立和销毁实例。在此场景下的隔离个性广泛都会波及到大量 cgroup 的相干操作,而现有的cgroup框架设计并发性很差,或者在设计之初并未思考到大规模的管制立体操作(例如:创立和销毁)。而随着技术的演进,大规模的管制立体操作场景逐步增多,也促使咱们开始更加关注管制立体的性能。 本文的论述是基于4.19版本的内核源代码,旨在剖析cgroup提供给用户的接口背地的实现原理,并基于实现原理给出一些用户态应用cgroup的倡议,最初在文章的结尾分享了一些内核态优化的思路。 原理剖析图一 图二 以上两张图,是4.19版本的内核中cgroup中最次要的几个数据结构之间的连贯关系和cgroup层次结构的连贯关系。 cgroup:字面意思 cgroup_root:hierarchy cgroup_subsys: 子系统,缩写的变量个别为ss cgroup_subsys_state: 当指向某个subsys时,代表该subsys在某个cgroup中一个实体 css_set、cset_cgrp_link:用于建设task_struct和cgroup之间多对多的关系 这些数据结构形象之后是这张图: 图三 其实也很好了解,实质上cgroup框架要解决的是:一个cgroup管哪些task,一个task归哪些cgroup管的问题,在实现上可通过cset作为中介来建设这层关系。相比于task和cgroup直连,这种做法能够简化简单的关系。这是因为在理论应用的场景中,task根本都以组为单位进行治理,对某一组task的资源管控计划都大概率是统一的。 对于cgroup的各类操作围绕着这三类实体开展: 创立:在图二所示的树形构造中减少一个叶节点绑定:实质上是迁徙,子过程被fork进去时连贯父过程指向的cset,绑定即是从一个cset(如果不再有task指向则删除)迁徙到了另一个cset(如果指向的是新的cgroup汇合则新创建)删除:在图二所示的树形构造中删除一个不论控任何task的叶节点对于cgroup的各类操作的访问控制也围绕这三类实体的开展: task: cgroup_threadgroup_rwsem锁cset: css_set_lock锁cgroup: cgroup_mutex锁具体的这三类锁有什么作用,将在优化思路里进行剖析。 优化计划问题出在哪? 问题在于三个锁上:cgroup_mutex、cgroup_threadgroup_rwsem、css_set_lock。 cgroup_mutex爱护cgroup的整个层级构造。cgroup的层级构造是一个森林,咱们须要用这个一个锁来爱护整个森林。扭转层级构造比方常见的mount、mkdir、rmdir就不用多说了,必定是须要持有这个锁的;除此之外对cgroup的任何一个其余的操作也须要持有这个锁,比方attach task、以及其余的读或写cgroup提供的接口。同时,因为rmdir的操作是随时都有可能产生的,任何操作都须要与rmdir都互斥。 css_set_lock爱护和css_set相干的所有操作。任意过程随时都有可能exit,导致某个css_set开释,从而影响css_set的哈希表。除此之外,对cgroup的绝大多数的操作也会波及到css_set_lock,这是因为对cgroup的绝大多数的操作(创立除外)都会引起css_set的变动。 cgroup_threadgroup_rwsem爱护和cgroup相干的线程组操作,事实中随时都有可能的fork和exit操作导致线程组发生变化。这里用读写锁的起因是,过程本身的行为可能包含扭转线程组的组成和持有读锁,这是能够并行的;当过程attach的时候,须要一个稳固的线程组视图,此时如果过程在fork或者exit的话会导致线程组的扭转,而attach又是能够以线程组为单位的,不可并行。这里用读写锁并不是说是真的在读什么或写什么,只是恰好合乎读者并行,写者需与其余写者互斥这个个性而已。也就是说,fork、exec、exit之间能够并行,相似于读者;attach与其余的都互斥,相似于写者。 这三个锁会受到过程fork和exit的影响,并且也会导致对cgroup的任何操作之间简直不可并行。笔者在对cgroup进行深刻的钻研前,感觉是最开始的设计者偷懒,应用如此大粒度的锁,直到把cgroup的框架摸索明确后才发现,临界区就是有这么大,各种会异步产生的事件都须要操作这些数据,所以这些锁被设计成这样也很正当。 这里试着对问题进行形象,思考一下问题的实质在哪。 对于cgroup_mutex,问题实质是树形(节点是cgroup)构造的并发拜访。 对于css_set_lock,问题其实是二部图(一边是css_set,一边是cgroup)构造的并发拜访。 对于cgroup_threadgroup_rwsem,问题其实是汇合(线程组作为汇合的元素)构造的并发拜访。 问题的定义曾经分明了,怎么解决呢?以我目前的能力,我没法解。 是的,剖析了这么多给的论断是此题无解,或者说临时无解,能够有的解法也会对cgroup的框架造成刮骨疗毒式的改变。这背地的危险、稳定性的影响、投入产出比的痛能不能接受的住,我给不出一个确定的论断。如果读者有什么想法,欢送在留言区提出,一起交换。 尽管治标难治,但治本还是能够有点想法的。 用户态优化:缩小cgroup操作 这个计划很好了解,提前把cgroup创立和配置好,等须要用的时候间接取就行。这个计划成果极好,几乎是降维打击。这里贴一下试验数据,这里的测试模仿袋鼠容器启动时的创立与读写—— 这个计划达到了90%以上的优化率,将原本须要创立配置后attach过程最初删除的状况变成了只须要attach,工作量少了,天然也就变快了。 但这个计划存在一些弊病。一方面,池子里不必的cgroup对于零碎来讲仍然是可见的,须要进行治理,因而会存在肯定的负载;另一方面是数据残留问题,并不是所有的subsys都提供相似于clear的操作接口,如果对监控数据有要求的话cgroup就是用一次就废,须要对池子进行补充,减少管制逻辑的同时又呈现了竞争,成果会打折扣。最初便是须要明确cgroup的层次结构,毕竟要提前创立和配置,如果对运行时的层次结构无奈掌控的话,池子都没法建设。 缩小cgroup数量systemd在默认状况下会把大多数subsys都挂在独立的一个hierarchy下,如果业务的过程都须要受同一些subsys管控的话,能够把这些subsys都挂载在同一个hierarchy下,比方把cpu、memory、blkio挂载在一起。 这时候可能有同学要问了,本来在cpu、memory、blkio下各创立一个cgroup,和在cpu_memory_blkio下创立一个cgroup能有多少区别?该有的逻辑都得有,一个都跑不了,最多就是少了几个cgroup本身这个构造体,能有多少区别? 这里要回归到最开始的场景,cgroup的问题出在场景是高并发,而实质上各类操作却是串行的。咱们晓得,掂量性能有次要的两个维度:吞吐和提早。cgroup实质的串行无奈间接进步吞吐,各个subsys独立在hierarchy下等于是被拆解成子工作,反而进步了提早。 上面是测试数据: 内核态优化 ...

August 11, 2021 · 1 min · jiezi