CGROUPS(7)

ИМЯ
cgroups - управляемые группы в Linux

ОПИСАНИЕ
Управляемые cgroups, обычно называемые cgroups, это свойство ядра Linux, которое позволяет группировать задачи, следить за ресурсами и ограничениями погруппно. Так как появилось несколько систем, помогающих в настройке и управлении cgroups, ядро предоставляет интерфейс работы с cgroups через псевдо-файловую систему, называемую cgroupfs. Группировка задач реализована в базовой части ядра cgroup, а слежение за ресурсами и ограничениями — в подсистемах самих ресурсов (память, ЦП и т. п.), которые можно включать в виде отдельных иерархий или подмонтировать в одну объединяющую. Каждая иерархия представляет собой отдельную точку монтирования файловой системы cgroup; включаемые в иерархию подсистемы перечисляются в параметрах монтирования. Для каждой смонтированной иерархии дерево каталогов отражает иерархию управляемой группы. Каждая управляемая группа представляется каталогом, каждый её потомок управляемой cgroups представляется дочерним каталогом. Например, /user/joe/1.session представляет управляемую группу 1.session, которая является потомком cgroup joe, которая является потомком /user. В каждом каталоге cgroup есть набор файлов, доступных на чтение и запись, через которые доступны ограничения ресурсов и другие общие свойства cgroup. В общем, ограничения cgroup иерархичны, то есть ограничения, указанные в /user/joe, не могут быть превышены в /usr/joe/1.session. Пока есть несколько исключений из этого правила, но их удаление является целью дальнейшей переработки cgroups. Также, cgroups можно монтировать без привязанной подсистеме, если они нужны только для слежения за процессами. Например, cgroup name=systemd, используемая в systemd(1) для слежения за службами и пользовательскими сеансами.

  Терминология
cgroup — это набор процессов, которые связаны с набором ограничений или параметров, определяемых через файловую систему cgroup. subsystem — компонент ядра, который изменяет поведение процессов в cgroup. Реализованы различные подсистемы, они позволяют делать разные вещи, например ограничивать количество времени ЦП и память доступную для cgroup, подсчитывать время ЦП, используемое группой и останавливать и возобновлять выполнение процессов в cgroup. Подсистемы иногда также называют контроллерами ресурсов (или просто, контроллерами). cgroups для подсистемы упорядочены в иерархию. Эта иерархия определяется посредством создания, удаления и переименования подкаталогов в файловой системе cgroup. На каждом уровне иерархии можно задать атрибуты (например, ограничения); эти атрибуты могут влиять или распространяться на дочерние cgroups и их потомков в иерархии.

  Cgroups версии 1 и версии 2
Впервые реализация cgroups появилась в Linux 2.6.24. Постепенно добавлялись различные подсистемы cgroup, позволяющие управлять различными типами ресурсов. Однако, разработка эти подсистем была, по большей части, не скоординированной, что привело к несостыковкам между подсистемами и управление иерархиями cgroup стало очень сложным (большое описание проблем можно найти в файле исходного кода ядра Documentation/cgroup-v2.txt). Вследствие проблем в первой реализации initial cgroups (cgroups версии 1), начиная с Linux 3.10 началась работа на новой независимой реализацией, учитывающей возникшие ошибки. Сначала помеченная как экспериментальная и скрытая параметром монтирования -o __DEVEL__sane_behavior новая версия (cgroups версии 2) в конце концов была добавлена в Linux 4.5. Различия между версиями описаны далее. Хотя cgroups v2 создавалась как замена cgroups v1, продолжают существовать старые системы (и по причине совместимости её не хотелось бы удалять). В настоящее время, в cgroups v2 реализованы не все контроллеры, доступные в cgroups v1. Эти две системы реализованы таким образом, что контроллеры v1 и v2 можно монтировать одновременно. То есть, например, можно не только использовать контроллеры, поддерживаемые версией 2, но и использовать контроллеры версии 1, которые не пока не поддерживаются версией 2.

  Задачи и процессы
В cgroups v1 процессы и задачи различаются. Процесс может состоять из нескольких задач (чаще всего называемых нитями, если смотреть из пользовательского пространства). В cgroups v1 возможно независимо управлять членством cgroup для задач процесса. Так как это возможность вызывает определённые проблемы, независимое управление членством cgroup для задач была удалена в cgroups v2. В cgroups v2 допускается управление членством cgroup только для процессов (что влияет на изменение членства cgroup для всех задач процесса).

  Монтирование
Для работы подсистем cgroup они должны быть скомпилированы с ядром. Так как они видны через виртуальную файловую систему, для управления подсистемами из нужно смонтировать. Обычно для этого используется /sys/fs/cgroup. Все подсистемы можно смонтировать одной командой:
    mount -t cgroup -o all cgroup /sys/fs/cgroup (Параметр -o all можно опустить, так как по умолчанию монтируются все подсистемы, если ни одна не указана явно) Если нужно несколько отдельно монтируемых подсистем, то обычно их помещают в подкаталоги, один на подсистему. Для этого сначала требуется смонтировать tmpfs в /sys/fs/cgroup, чтобы можно было создавать подкаталоги. Например, вот так можно смонтировать cpu, memory и devices:
mount -t tmpfs -o size=100000,mode=755 cgroups /sys/fs/cgroup
for s in cpu memory devices; do
    mkdir /sys/fs/cgroup/$s
    mount -t cgroup -o $s $s /sys/fs/cgroup/$s
done
Для одновременно смонтированных подсистемах задача находится в одной cgroup. Отдельное монтирование подсистем позволяет задаче быть в cgroup /foo1 для одной подсистемы и в /foo2/foo3 для другой.

  Самодиагностика
Список скомпилированных с ядром подсистем можно посмотреть в файле /proc/cgroups. В файле /proc/pid/cgroup перечислено текущее членство cgroup задачи для каждой смонтированной иерархии.

  Создание cgroups и перемещение задач
Система начинается с единственной корневой cgroup (на иерархию), «/», которой принадлежат все задачи. Новая cgroup создаётся созданием каталога в файловой системе cgroup:
    mkdir /sys/fs/cgroup/cpu/cg1 Так создаётся новая пустая cgroup. Помещение задач в эту группу выполняется с помощью записи их PID в cgroup файл cgroup.procs или tasks (устарел):
    echo $$ > /sys/fs/cgroup/cpu/cg1/cgroup.procs Читая этот файл можно получить список процессов, находящихся сейчас в cg1. Используя файл cgroup.procs вместо файла tasks можно за раз поместить все задачи группы нитей в новую cgroup. При fork(2) новый потомок создаётся как член cgroup родителя, следуя неявной группировке иерархии процессов. Замечание: в новое версии унифицированной иерархии введено новое ограничение, из-за которого задачи могут существовать только в листовых leaf cgroups. Например, если существует cgroup /cg1/cg2, то задача может существовать в /cg1/cg2, но не в /cg1. Это устраняет имеющуюся неоднозначность в разделение ресурсов между задачами в /cg1 и её дочерних cgroups. Рекомендуемый обход ограничения: создать подкаталог с именем leaf для всех не листовых cgroup, которые должны содержать задачи и не создавать в ней дочерние cgroups. В примере выше, задачи, которые ранее попали бы в /cg1, теперь нужно поместить в /cg1/leaf. Это даёт преимущество создания явной связи между задачами в /cg1/leaf и другими потомками /cg1.

  Удаление cgroups
Удаляемая cgroup не должна содержать дочерних cgroups и задач. Если это соблюдается, то можно просто удалить соответствующий каталог. Заметим, что файлы в каталоге cgroup невозможно и ненужно удалять. Специальный файл в каждой иерархии cgroup, release_agent, можно использовать для регистрации программы обработки cgroups, которые становятся пустыми. Программа будет вызываться всякий раз, когда cgroup, помеченная для автоматического удаления, становится пустой и без потомков. В первом аргументе командной строке будет передаваться путь cgroup. Cgroup должна быть помечена для автоматического удаления посредством записи «1» в её файл notify_on_release; это значение наследуется создающимися потомками cgroups. Новое свойство cgroups v2 — файл cgroup.populated. Он содержит 0, если в cgroup нет задач или потомков, и 1 в противном случае. За его изменениями можно следить с помощью inotify(7). Это позволяет программам из пространства пользователя следить за cgroups для проверки условий автоматического удаления.

  Cgroups версии 2
В cgroups v2 все смонтированные контроллеры располагаются в единой унифицированной иерархии. Хотя (различные) контроллеры могут одновременно монтироваться в иерархиях v1 и v2, невозможно одновременное монтирование одного контроллера в обеих иерархиях v1 и v2. Новое поведение в cgroups v2 вкратце:

1. Задачи только в узлах-листьях
    За исключением корневой cgroup, задачи могут располагаться только в узлах-листьях. Это позволяет не решать как делить ресурсы между задачами, которые являются членами cgroup A и задачами в дочерних cgroups A.

2. Требуется указать активные cgroups
    В унифицированной иерархии добавилось два файла, cgroup.controllers и cgroup.subtree_control. Когда создаётся cgroup A/b, её файл cgroup.controllers содержит список контроллеров, которые активны в её родителе, A. Это список доступных контроллеров для этой cgroup. Контроллеры не активны, пока они будут включены в файле cgroup.subtree_control посредством записи списка имён контроллеров (через пробел), где каждое имя начинается с «+» (включить) или «-» (выключить). Если контроллер freezer не включён в /A/B, то его нельзя включить в /A/B/C.

3. Нет файлов «tasks» или «cgroup.clone_children»

4. Нет уведомления от cgroup
    Новый файл cgroup.populated в каждой cgroup содержит «0», если cgroup пуста, и «1», если нет. Таким образом, можно отслеживать, когда cgroup становится (не)пустой. Он заменяет исходный механизм уведомления-при-освобождении. Все изменения можно найти в файле исходного кода ядра Documentation/cgroups/unified-hierarchy.

  Подсистемы cgroups версии 1
Все подсистемы cgroups версии 1 управляются параметрами настройки ядра (список далее). Также, включение свойства cgroups управляется параметром настройки ядра CONFIG_CGROUPS.

cpu (начиная с Linux 2.6.24; CONFIG_CGROUP_SCHED)
    Cgroups может гарантироваться минимальное количество «общих ЦП» в занятой системе. Это значение не ограничивает использование ЦП cgroup, если ЦП не заняты. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/scheduler/sched-bwc.txt.

cpuacct (начиная с Linux 2.6.24; CONFIG_CGROUP_CPUACCT)
    Включает учёт использования ЦП группами задач. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/cpuacct.txt.

cpuset (начиная с Linux 2.6.24; CONFIG_CPUSETS)
    Эту cgroup можно использовать для привязки задач cgroup к указанному набору ЦП и узлов NUMA. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/cpusets.txt.

memory (начиная с Linux 2.6.25; CONFIG_MEMCG)
    Контроллер памяти поддерживает учёт и ограничение памяти процесса, памяти ядра и подкачки, используемой cgroups. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/memory.txt.

devices (начиная с Linux 2.6.26; CONFIG_CGROUP_DEVICE)
    Позволяет контролировать какие задачи могут создавать (mknod) устройства, а также открывать их на чтение или запись. Правила можно задавать в виде чёрных и белых списков. Учитывает иерархия, поэтому новые правила не должны нарушать существующие у cgroups назначения или предков. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/devices.txt.

freezer (начиная с Linux 2.6.28; CONFIG_CGROUP_FREEZER)
    freezer cgroup может приостанавливать и возобновлять работу всех задач в cgroup. Заморозка cgroup /A также влияет на её потомков, например, задачи в /A/B тоже приостанавливаются. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/freezer-subsystem.txt.

net_cls (начиная с Linux 2.6.29; CONFIG_CGROUP_NET_CLASSID)
    Помещает classid, задаваемые для cgroup, в сетевые пакеты, создаваемые cgroup. Эти classid затем можно использовать в правилах межсетевого экрана, а также для ограничения трафика с помощью tc(8). Применяется только к пакетам, выходящим из cgroup, и не применяется к входящему трафику cgroup. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/net_cls.txt.

blkio (начиная с Linux 2.6.33; CONFIG_BLK_CGROUP)
    blkio cgroup контролирует и ограничивает доступ к заданным блочным устройствам, применяет управление вводом-выводом посредством пропусков (throttling) и ограничения сверху листовых узлов и и промежуточных узлов в иерархии хранилища. Доступно две стратегии. Первая: пропорционально взвешенное повременное разделение диска, реализованная посредством CFQ. Влияет на листовые узлы с помощью CFQ. Вторая: стратегия пропусков, которая задаётся верхним ограничением скорости обмена с устройством. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/blkio-controller.txt.

perf_event (начиная с Linux 2.6.39; CONFIG_CGROUP_PERF)
    Этот контроллер позволяет выполнять слежение perf за набором процессов, сгруппированных в cgroup. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/perf-record.txt.

net_prio (начиная с Linux 3.3; CONFIG_CGROUP_NET_PRIO)
    Позволяет для cgroups задавать свой приоритет на каждый интерфейс. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/net_prio.txt.

hugetlb (начиная с Linux 3.5; CONFIG_CGROUP_HUGETLB)
    Поддерживает ограничение cgroups на использование огромных страниц. Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/hugetlb.txt.

pids (начиная с Linux 4.3; CONFIG_CGROUP_PIDS)
    Этот контроллер позволяет ограничивать количество процессов, которые могут быть созданы в cgroup (и её потомках). Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/pids.txt.

  Файлы в /proc

/proc/cgroups (начиная с Linux 2.6.24)
    В этом файле содержится информация о доступных в системе контроллерах. Пример содержимого файла (переформатирован для читабельности):
    #subsys_name     hierarchy        num_cgroups      enabled
    cpuset           4                1                1
    cpu              8                1                1
    cpuacct          8                1                1
    blkio            6                1                1
    memory           3                1                1
    devices          10               84               1
    freezer          7                1                1
    net_cls          9                1                1
    perf_event       5                1                1
    net_prio         9                1                1
    hugetlb          0                1                0
    pids             2                1                1
    Поля файла, слева направо:

    1.  Имя контроллера.

    2.  Уникальный ID иерархии cgroup, на которой смонтирован контроллер. Если к одной иерархии привязано несколько контроллеров cgroups v1, то для каждого в этом поле будет показан одинаковый ID иерархии. Значение поля равно 0, если:

        1.  контроллер не смонтирован на иерархию cgroups v1;

        2.  контроллер привязан к унифицированной иерархии cgroups v2; или

        c)  контроллер отключён (смотрите ниже).

    3.  Количество контролируемых групп в этой иерархии, использующих этот контроллер.

    4.  В этом поле содержится значение 1, если этот контроллер включён, или 0, если он выключен (с помощью параметра cgroup_disable командной строки загрузки ядра).

/proc/[pid]/cgroup (начиная с Linux 2.6.24)
    Этот файл описывает управляемые группы, которым принадлежит процесс с соответствующим PID. Отображаемая информация отличается для иерархий cgroups версии 1 и 2. Для каждой иерархии cgroup, членом которой является процесс, существует одна запись, состоящая из трёх полей через двоеточие:
         ID иерархии:список подсистем:путь cgroup Пример:
        5:cpuacct,cpu,cpuset:/daemons

    Поля, разделяемые двоеточием, слева направо:

    1.  Для иерархии cgroups версии 1 это поле содержит уникальный ID номер иерархии, который может совпадать с ID иерархии в /proc/cgroups. Для иерархии cgroups версии 2 это поле содержит значение 0.

    2.  Для иерархии cgroups версии 1 это поле содержит список подсистем, привязанных к иерархии, перечисленных через запятую. Для иерархии cgroups версии 2 это поле пусто.

    3.  Это поле содержит путь управляемой группы в иерархии, которой принадлежит процесс. Путь является относительным точки монтирования иерархии.

ОШИБКИ
Следующие ошибки могут возникать при mount(2):

EBUSY
    При монтировании файловой системы cgroup версии 1 не указан параметр name= (для монтирования именованной иерархии) или имя контроллера (или all).

СМОТРИТЕ ТАКЖЕ
prlimit(1), systemd(1), clone(2), ioprio_set(2), perf_event_open(2), setrlimit(2), cgroup_namespaces(7), cpuset(7), namespaces(7), sched(7), user_namespaces(7)