ИМЯ cgroups - управляемые группы в Linux
ОПИСАНИЕ Управляемые cgroup-ы, обычно называемые cgroups, это свойство ядра Linux, которое позволяет объединять процессы в иерархические группы, и в этих группах отслеживать и ограничивать разные типы ресурсов. Ядро предоставляет интерфейс работы с cgroup-ами через псевдо-файловую систему, называемую cgroupfs. Группировка реализована в базовой части ядра cgroup, а слежение за ресурсами и ограничениями — в подсистемах самих ресурсов (память, ЦП и т. п.).
Терминология cgroup — это набор процессов, которые связаны с набором ограничений или параметров, определяемых через файловую систему cgroup.
subsystem — компонент ядра, который изменяет поведение процессов в cgroup-у. Реализованы различные подсистемы, они позволяют делать разные вещи, например ограничивать количество времени ЦП и память доступную для cgroup-ы, подсчитывать время ЦП, используемое группой и останавливать и возобновлять выполнение процессов в cgroup-е. Подсистемы иногда также называют контроллерами ресурсов (или просто, контроллерами).
Для контроллера cgroup-ы упорядочены в иерархию. Иерархия определяется посредством создания, удаления и переименования подкаталогов в файловой системе cgroup. На каждом уровне иерархии можно задать атрибуты (например, ограничения). Если атрибуты назначены, то ограничение, контроль и учёт, предоставляемый cgroup-ами, обычно, распространяется в иерархии по всем нижестоящим элементам. То есть, например, ограничение, заданное на cgroup на высшем уровне иерархии не может быть превышено в дочерних cgroup-ах.
Cgroups версии 1 и 2 Впервые реализация cgroup появилась в Linux 2.6.24. Постепенно добавлялись различные контроллеры cgroup, позволяющие управлять различными типами ресурсов. Однако, разработка этих контроллеров была, по большей части, не скоординированной, что привело к несогласованности между контроллерами, и управление иерархиями cgroup стало очень сложным (расширенное описание проблем можно найти в файле исходного кода ядра Documentation/cgroup-v2.txt).
Вследствие проблем в первой реализации cgroup (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 v2.
Cgroups версии 1 В cgroups v1 каждый контроллер можно смонтировать в отдельную файловую систему cgroup, которая представляет собой собственную иерархию процессов в системе. Также есть набор файлов, доступных на чтение и запись, через которые доступны ограничения ресурсов и другие общие свойства cgroup.
Также, в cgroups v1 можно монтировать cgroups без привязанного контроллера, если они нужны только для слежения за процессами (смотрите описание отправки уведомлений ниже). Например, cgroup name=systemd используется systemd(1) для слежения за службами и пользовательскими сеансами.
Задачи (нити) и процессы В cgroups v1 процессы и задачи различаются. Процесс может состоять из нескольких задач (чаще всего называемых нитями, если смотреть из пользовательского пространства, и так они будут называться далее в этой справочной странице). В cgroups v1 возможно независимо управлять членством cgroup для нитей процесса. Так как это возможность вызывает определённые проблемы, независимое управление членством cgroup для нитей было удалено в cgroups v2. В cgroups v2 допускается управление членством cgroup только для процессов (что влияет на изменение членства cgroup для всех нитей процесса).
Монтирование контроллеров v1 Для использования cgroups требуется собрать ядро с параметром CONFIG_CGROUP. Также с каждым контроллером v1 связан параметр настройки, который должен быть задан, если нужно работать с этим контроллером.
Чтобы использовать контроллер a v1, его нужно смонтировать в файловую систему cgroup. Обычно для этого используют файловую систему tmpfs(5), смонтированную в /sys/fs/cgroup. Таким образом, можно смонтировать контроллер cpu следующим образом:
mount -t cgroup -o cpu none /sys/fs/cgroup/cpu
Можно смонтировать несколько контроллеров вместе в одной иерархии. Например, так контроллеры cpu и cpuacct одновременно монтируются в одной иерархии:
mount -t cgroup -o cpu,cpuacct none /sys/fs/cgroup/cpu,cpuacct
Для одновременно смонтированных контроллеров процесс находится в одной cgroup всех одновременно смонтированных контроллеров. Отдельно смонтированные контроллеры позволяют процессу находиться в cgroup /foo1 одного контроллера и в /foo2/foo3 другого.
Можно смонтировать все контроллеры v1 вместе в одной иерархии:
mount -t cgroup -o all cgroup /sys/fs/cgroup
(Параметр -o all можно опустить, так как по умолчанию монтируются все контроллеры, если ни один не указан явно)
Невозможно смонтировать один и тот же контроллер в несколько иерархий cgroup. Например, невозможно смонтировать контроллеры cpu и cpuacct в одну иерархию и смонтировать только контроллер cpu в другую. Возможно создать несколько точек монтирования с полностью одинаковым набором одновременно смонтированных контроллеров. Однако в этом случае получается только несколько точек монтирования, представляющих одну иерархию.
Заметим, что на многих системах контроллеры v1 автоматически монтируется в /sys/fs/cgroup; в частности, такие точки монтирования автоматически создаёт systemd(1). заняты. Дополнительную информацию смотрите в Documentation/scheduler/sched-design-CFS.txt.
В Linux 3.2 в этот контроллер было добавлено управление «полосы пропускания» ЦП. Если ядро собрано с параметром CONFIG_CFS_BANDWIDTH, то внутри каждого диапазона планирования (определяемого через файл в каталоге cgroup) процессам в 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.
ограничением скорости обмена с устройством.
Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v1/blkio-controller.txt.
perf_event (начиная с Linux 2.6.39; CONFIG_CGROUP_PERF) Этот контроллер позволяет выполнять слежение perf за набором процессов, сгруппированных в cgroup.
Дополнительную информацию смотрите в файле исходного кода ядра tools/perf/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.
Создание cgroups и перемещение процессов Первоначально, в файловой системе cgroup содержится только корневая cgroup, «/», которой принадлежат все процессы. Новая cgroup создаётся посредством создания каталога в файловой системе cgroup:
mkdir /sys/fs/cgroup/cpu/cg1
Данная команда создаёт новую пустую cgroup.
Помещение процесса в эту cgroup выполняется с помощью записи его PID в файл cgroup cgroup.procs:
echo $$ > /sys/fs/cgroup/cpu/cg1/cgroup.procs
В этот файл единовременно должен записываться только один PID.
Запись в файл cgroup.procs значения 0 приводит к помещению в соответствующую cgroup записывающего процесса.
При записи PID в cgroup.procs в новую cgroup одновременно перемещаются все нити процесса.
Внутри иерархии процесс может быть членом только одной cgroup. Запись PID процесса в файл cgroup.procs автоматически удаляет его из cgroup, в которой он числился до этого.
Удаление cgroups Удаляемая cgroup не должна содержать дочерних cgroups и процессов (не зомби). Если это соблюдается, то можно просто удалить соответствующий каталог. Заметим, что файлы в каталоге cgroup невозможно и ненужно удалять.
Выпуск уведомлений cgroups v1 Для определения того, как ядро выполняет уведомления об опустевших cgroup, можно использовать два файла. Cgroup считается пустой, если не содержит дочерних cgroup и процессов.
Специальный файл в корневом каталоге каждой иерархии cgroup, release_agent, можно использовать для регистрации программы, которая будет вызываться всякий раз, когда cgroup в иерархии становится пустой. При вызове программы release_agent в единственной аргументе командной строки передаётся путь (относительно точки монтирования cgroup) только что опустевшей cgroup. Программа release_agent может удалить удалить каталог cgroup или, возможно, добавить в него процесс.
По умолчанию файл release_agent пуст, то есть агент освобождения не вызывается.
Будет ли программа release_agent вызываться для определённой ставшей пустой cgroup, задаётся значением файла notify_on_release в каталоге, соответствующем cgroup. Если этот файл содержит значение 0, то программа release_agent не вызывается. Если он содержит 1, то программа release_agent вызывается. По умолчанию в этом файле содержится 0 для корневой cgroup. В момент, когда создаётся новая cgroup, значение в этом файле наследуется из соответствующего файла родительской cgroup.
Cgroups версии 2 В cgroups v2 все смонтированные контроллеры располагаются в единой унифицированной иерархии. Хотя (различные) контроллеры могут одновременно монтироваться в иерархиях v1 и v2, невозможно одновременное монтирование одного контроллера в обеих иерархиях v1 и v2.
Далее приведено краткое описание новых правил поведения cgroups v2, и в некоторых случаях, расширено в последующих подразделах.
1. Cgroups v2 предоставляет унифицированную иерархию всех смонтированных контроллеров.
2. «Внутренние» процессы запрещены. За исключением корневой cgroup, процессы могут располагаться только в крайних узлах (cgroup, которая не содержит дочерних cgroup).
3. Требуется указывать активные cgroup-ы через файлы cgroup.controllers и cgroup.subtree_control.
4. Удалён файл tasks. Также удалён файл cgroup.clone_children, использовавшийся контроллером cpuset.
5. Улучшенный механизм уведомлений о пустых cgroup доступен через файл cgroup.events.
Дополнительную информацию смотрите в файле исходного кода ядра Documentation/cgroup-v2.txt.
Унифицированная иерархия cgroups v2 В cgroups v1, способность монтировать различные контроллеры в разные иерархии Контроллер cgroup v2 доступен только, если он уже не смонтирован в иерархию cgroup v1. Или, говоря иначе, невозможно использовать один и тот же контроллер сразу в иерархии v1 и унифицированной иерархии v2.
Правило cgroups v2 «нет внутренним процессам» За исключением корневой cgroup, процессы могут располагаться только в крайних узлах (cgroup, которая не содержит дочерних cgroup). Из-за этого не нужно решать как делить ресурсы между процессами, которые являются членами cgroup A и процессами в дочерних cgroup-ах A.
Например, если существует cgroup /cg1/cg2, то процесс может располагаться в /cg1/cg2, но не в /cg1. Это решает проблему с неясностью в cgroups v1 в плане разделения ресурсов между процессами в /cg1 и её дочерних cgroup-ах. Рекомендуемый подход в cgroups v2 — создать подкаталог leaf для всех конечных cgroup, в котором будут содержаться только процессы и отсутствовать дочерние cgroup-ы. То есть процессы, которые раньше находились в /cg1 теперь должны помещаться в /cg1/leaf. Преимуществом этого является явное указание родства между процессами в /cg1/leaf и в других потомках /cg1.
Управление поддеревом cgroups v2 При создании cgroup A/b её файл cgroup.controllers содержит список контроллеров, активных в её родителе, A. Данный список контроллеров доступен для этой cgroup. Контроллеры не активны до тех пор, пока не будут разрешены в файле cgroup.subtree_control посредством записи списка имён контроллеров через пробел; каждое имя начинается с «+» (включён) или «-» (выключен). Если контроллер freezer не включён в /A/B, то он не может быть включён и в /A/B/C.
Файл cgroup.events в cgroups v2 В cgroups v2 появился новый механизм получения уведомления при появлении пустой cgroup. Файлы cgroups v1 release_agent и notify_on_release удалены и заменены новым, более общим файлом cgroup.events. В этом файле содержится пара ключ-значение (пары разделяются символом новой строки, ключ и значение разделяется пробелами), которые определяют события или состояние cgroup. В настоящее время доступен только один ключ, populated, который имеет значение 0, означающее, что эта cgroup (и её потомки) не содержат процессов (не зомби), или 1, означающее, что cgroup содержит процессы.
За файлом cgroup.events можно установить наблюдение и получить уведомление, когда cgroup изменяет состояние с пустой на непустую и наоборот. При наблюдении за файлом с помощью inotify(7) во время перехода генерируются события IN_MODIFY, а при наблюдении с помощью poll(2) во время перехода генерируются события POLLPRI.
Механизм cgroups v2 notify_on_release имеет, по крайней мере, два преимущества над механизмом cgroups v1 release_agent. Во-первых, уведомление менее требовательно, так как один процесс может следить за несколькими файлами cgroup.events. Для сравнения, механизм cgroups v1 требует создания процесса для каждого уведомления. Во-вторых, уведомление может бы поручено процессу, который находится внутри контейнера, связанного с только что созданной пустой cgroup.
Файлы в /proc /proc/cgroups (начиная с Linux 2.6.24) В этом файле содержится информация о контроллерах, с которыми было собрано ядро. Пример содержимого файла (переформатирован для читабельности):
#subsys_name hierarchy num_cgroups enabled cpuset 4 1 1 cpu 8 1 1 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, членом которой является процесс, существует одна запись, состоящая из трёх полей через двоеточие:
hierarchy-ID:controller-list:cgroup-path
Пример:
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).
|