Что «сломано» в семантике наследования cpuset cgroup в ядре Linux?

Чтобы процитировать объявление Systemd 2013 года о новом интерфейсе группы управления (с добавлением акцента):

Обратите внимание, что количество атрибутов cgroup, доступных в настоящее время как свойства объекта, ограничено. Это будет расширено позже, так как их интерфейсы ядра очищены. Например, cpuset или freezer в настоящее время вообще не доступны из-за нарушенной семантики наследования логики ядра. Кроме того, перенос единиц в другой слайс во время выполнения не поддерживается (т. Е. Изменение свойства Slice = для запущенных единиц), поскольку в ядре в настоящее время отсутствуют перемещения атомарного поддерева cgroup.

Итак, что же не так в семантике наследования логики ядра для cpuset (и как эта поломка не применяется к другим controllerам cgroup, таким как cpu )?

На сайте RedHat есть статья, в которой дается непроверенное решение о том, как использовать cpusets cgroup в RHEL 7, несмотря на отсутствие поддержки в качестве простых в управлении свойств системных модhive … но это даже хорошая идея? Выделенная цитата выше относится к теме.

Иными словами, какие «ошибки» (ловушки) могут применяться к использованию cgroup v1 cpuset, на которые здесь ссылаются?


Я начинаю вознаграждение за это.

Возможные источники информации для ответа на этот вопрос (в произвольном порядке) include:

  1. документация cgroup v1;
  2. исходный код ядра;
  3. результаты теста;
  4. реальный опыт.

Одно из возможных значений жирной линии в приведенной выше цитате может заключаться в том, что когда новый процесс разветвляется, он не остается в той же группе cpuset, что и его родительский элемент, или что он находится в той же группе, но в каком-то «неисполненном» состоянии. в результате он может работать на другом процессоре, чем позволяет cgroup. Тем не менее, это чистое предположение с моей стороны, и мне нужен окончательный ответ.

3 Solutions collect form web for “Что «сломано» в семантике наследования cpuset cgroup в ядре Linux?”

Я не достаточно разбираюсь в cgroups, чтобы дать однозначный ответ (и у меня определенно нет опыта работы с cgroups, начиная с 2013 года!), Но на ванильной Ubuntu 16.04 cgroups v1, похоже, действует вместе:

Я разработал небольшой тест, который заставляет разветвляться как другого пользователя, используя дочерний sudo /bin/bash отключенный с & – флаг -H – дополнительная паранойя, заставляющая sudo выполняться в домашней среде root.

 cat < (whoami) /proc/self/cgroup >me.cgroup && \ sudo -H /bin/bash -c 'cat < (whoami) /proc/self/cgroup >you.cgroup' & \ sleep 2 && diff me.cgroup you.cgroup 

Это дает:

 1c1 < admlocal --- > root 

Для справки, это структура монтирования cgroup в моей системе:

 $ mount | grep group tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755) cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event) lxcfs on /var/lib/lxcfs type fuse.lxcfs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other) $ 

По крайней мере, одна определенная и нерешенная проблема с cpusets задокументирована в трекере ошибок ядра здесь:

Ошибка 42789 – cpuset cgroup: когда процессор отключается, он удаляется из cpuset.cpus всех cgroup, но когда он подключается, он восстанавливается только в корне cpuset.cpus

Чтобы процитировать один комментарий из заявки (я добавляю гиперссылки к фактическим коммитам и удаляю адрес электронной почты IBM в случае спам-ботов):

Об этом независимо сообщил Prashanth Nageshappa … и зафиксировал в коммите 8f2f748b0656257153bcf0941df8d6060acc5ca6 , но впоследствии Линус отменил коммит 4293f20c19f44ca66e5ac836b411d25e14b9f185 . По его словам, исправление вызвало регресс в других местах.

Фикс фиксации (который позже был отменен) хорошо описывает проблему:

В настоящее время во время горячего подключения ЦП обратные вызовы cpuset изменяют процессорные наборы для отображения состояния системы, и эта обработка является асимметричной. То есть при отключении ЦП этот ЦП удаляется из всех процессоров. Однако, когда он возвращается в оперативный режим, он возвращается только в корневой процессор.

Это приводит к серьезной проблеме во время приостановки / возобновления. Во время приостановки мы отключаем все не-загрузочные процессоры, а во время возобновления их онлайн возвращаем. Это означает, что после возобновления работы все процессоры (кроме корневого процессора) будут ограничены одним процессором (загрузочным процессором). Но весь смысл приостановки / возобновления состоит в том, чтобы восстановить систему до состояния, максимально приближенного к тому, которое было до приостановки.


Та же проблема асимметричного горячего подключения описана с дальнейшим пониманием того, как это связано с наследованием, в:

Ошибка 188101 – планирование процессов в процессоре cgroup не работает должным образом.

Цитируя этот билет:

Когда процессорный набор контейнера (docker / lxc оба используют базовую группу) становится пустым (из-за hotplug / hotunplug), тогда процессы, выполняющиеся в этом контейнере, могут быть запланированы на любом процессоре в процессоре его ближайшего непустого предка.

Но когда процессор из запущенного контейнера (docker / lxc) становится непустым из пустого состояния (добавление процессора в пустой процессор) путем обновления процессора из запущенного контейнера (с помощью метода echo), процессы, запущенные в этом контейнере все еще использует тот же процессор, что и у его ближайшего непустого предка.


Хотя могут быть другие проблемы с cpuset, вышеприведенного достаточно, чтобы понять и понять утверждение, что systemd не раскрывает и не использует cpuset «из-за нарушенной семантики наследования логики ядра».

Из этих двух отчетов об ошибках процессоры не только не добавляются обратно в набор процессоров после возобновления, но даже когда они (добавляются вручную), процессы в этой группе все равно будут работать на процессорах, потенциально запрещенных процессором.


Я нашел сообщение от Леннарта Поэттеринга, которое прямо подтверждает это как причину (выделено жирным шрифтом):

В среду, 2016-08-03 в 16:56 +0200, Леннарт Поэттеринг написал:

В среду, 03.08.16 14:46, доктор Вернер Финк (werner at suse.de) написал:

проблема с v228 (и я думаю, что это также позже AFAICS из журналов текущего git), что повторяет события горячего подключения процессора (в автономном режиме / онлайн). Основная причина в том, что cpuset.cpus не восстанавливается механически. Обратите внимание, что libvirt не может сделать это, поскольку это не разрешено.

Это ограничение интерфейса ядра cpuset, и это одна из причин, по которой мы сейчас вообще не выставляем cpusets в systemd. К счастью, есть альтернатива cpusets, которая представляет собой средства управления сродством ЦП, предоставляемые через CPUAffinity = в systemd, которые делают то же самое, но имеют меньше семантики.

Мы хотели бы поддерживать процессоры непосредственно в systemd, но мы не делаем этого, пока интерфейсы ядра такие же, как и они. Например, процессоры полностью сбрасываются, когда система проходит цикл приостановки / возобновления.

Что «сломано» в семантике наследования cpuset cgroup в ядре Linux?

«Обратите внимание, что количество атрибутов cgroup, доступных в настоящее время как свойства объекта, ограничено. Это будет расширено позже, так как их интерфейсы ядра очищены. Например, cpuset или freezer в настоящее время вообще не доступны из-за нарушенной семантики наследования Логика ядра . Кроме того, перенос единиц в другой слайс во время выполнения не поддерживается (т. е. изменение свойства Slice = для запущенных единиц), поскольку в ядре в настоящее время отсутствуют перемещения атомарного поддерева cgroup. ”

Итак, что же не так в семантике наследования логики ядра для cpuset (и как эта поломка не применяется к другим controllerам cgroup, таким как cpu)?

Выделенная цитата выше относится к теме. Иными словами, какие «ошибки» (ловушки) могут применяться к использованию cgroup v1 cpuset, на которые здесь ссылаются?

Очень краткий ответ: код плохо обрабатывается несколькими процессами, разные процессы используют и бесплатные PID возвращают их в пул до того, как PID их детей прекратят работу, оставляя восходящий stream, чтобы полагать, что дочерние PID активны, поэтому пропустите этот PID, но этот PID не должен был быть переиздан прежде, чем убить детей. Короче говоря, плохие замки.

Сервисы, области и fragmentы могут создаваться администратором свободно или динамически программами. Это может помешать установке слайсов по умолчанию, установленной операционной системой во время запуска.

С Cgroups процесс и все его дочерние элементы извлекают ресурсы из содержащей группы.

И многое другое … приводит к длинному ответу …

Ряд людей выразили свои опасения:

  1. « Контрольные группы Linux – это не рабочие места » (2016) Джонатана де Бойна Полларда:

    Ядро операционной системы, которое обеспечивает абстракцию «задания», обеспечивает способ отмены / уничтожения всей «работы». Посмотрите, например, механизм Win32 TerminateJobObject () .

    Когда systemd завершает все процессы в cgroup, он не выдает ни одного системного вызова “terminate job”. Там нет такой вещи. Вместо этого он зацикливается на коде режима приложения, многократно сканируя все идентификаторы процессов в cgroup (перечитывая файл, полный чисел PID) и отправляя сигналы новым процессам, которых он раньше не видел. Есть несколько проблем с этим.

    • systemd может работать медленнее, чем то, что происходит в дочерних процессах внутри группы процессов, что приводит к тому, что сигналы завершения отправляются совершенно неправильному процессу: тот, который случайно использовал один и тот же идентификатор процесса между systemd, читающей файл списка процессов cgroup и это на самом деле доходит до отправки сигналов в список процессов. …

    • Программа, которая генерирует новые процессы достаточно быстро в пределах cgroup, может поддерживать системное rotation в течение длительного времени, теоретически, до тех пор, пока преобладает подходящая «погода», так как на каждой итерации цикла будет еще один процесс, который нужно убить. Обратите внимание, что это не обязательно должна быть вилочная бомба. Нужно только разветвиться, чтобы systemd видел по крайней мере еще один новый идентификатор процесса в cgroup при каждом запуске цикла.

    • systemd хранит идентификаторы процессов, о которых она уже сообщила, в наборе, чтобы знать, на какие из них она не будет пытаться отправить сигналы снова. Возможно, что процесс с идентификатором N мог быть сигнализирован, завершен и удален из таблицы процессов жнецом / родителем; и затем что-то внутри cgroup разветвляет новый процесс, которому снова присваивается тот же идентификатор процесса N. systemd перечитает список идентификаторов процесса cgroup, подумает, что он уже сигнализировал о новом процессе, и не сообщит об этом вообще.

    Они решаются с помощью настоящего «рабочего» механизма. Но группы не такие. cgroups были задуманы как усовершенствование традиционных механизмов ограничения ресурсов Unix, устраняя некоторые из их давних и известных недостатков дизайна. Они не были разработаны, чтобы быть эквивалентными VMS или объекту задания Windows NT .

    Нет, морозильник не ответ. Systemd не только не использует морозильник, но и системные люди явно описывают его как « нарушенную семантику наследования логики ядра ». Вам придется спросить их, что они подразумевают под этим , но морозильник для них волшебным образом не превращает cgroups в механизм работы.

    Более того: это не говоря о том, что Docker и другие будут манипулировать состоянием замораживания контрольных групп для своих собственных целей, и не существует реального беспроблемного механизма для совместного использования этого параметра среди нескольких владельцев, такого как атомарное чтение и обновление. для этого.
    Настоящим предоставляется разрешение на копирование и распространение этой веб-страницы в ее исходной неизмененной форме, если сохраняется ее дата последнего изменения.

    • TerminateJobObject () функция

       Terminates all processes currently associated with the job. If the job is nested, this function terminates all processes currently associated with the job and all of its child jobs in the hierarchy. 
    • Объекты заданий Windows NT

       A job object allows groups of processes to be managed as a unit. Job objects are namable, securable, sharable objects that control attributes of the processes associated with them. Operations performed on a job object affect all processes associated with the job object. Examples include enforcing limits such as working set size and process priority or terminating all processes associated with a job. 

    Ответ, предложенный в объяснении Джонатана:

    Концепции управления ресурсами systemd

    Единицы обслуживания, области действия и среза напрямую отображаются на объекты в дереве cgroup. Когда эти юниты активируются, каждый из них отображается напрямую (по модулю выхода некоторых символов) на пути группирования, построенные из названий юнитов. Например, сервис quux.service в срезе foobar-waldo.slice находится в cgroup foobar.slice / foobar-waldo.slice / quux.service /.

    Сервисы, области и fragmentы могут создаваться администратором свободно или динамически программами. Однако по умолчанию ОС определяет количество встроенных служб, необходимых для запуска системы. Кроме того, по умолчанию определены четыре среза: в первую очередь корневой срез -.slice (как упомянуто выше), а также system.slice, machine.slice, user.slice. По умолчанию все системные службы размещаются в первом слайсе, все виртуальные машины и контейнеры – во втором, а пользовательские сеансы – в третьем. Однако это просто по умолчанию, и администратор может свободно определять новые fragmentы и назначать им сервисы и области. Также обратите внимание, что все сеансы входа в систему автоматически помещаются в отдельную единицу области действия, как и процессы VM и контейнера. Наконец, все пользователи, вошедшие в систему, также получат свой неявный fragment, где размещены все области сеансов .

    Как вы можете видеть, сервисы и области содержат процессы и размещаются в срезах, а срезы не содержат собственных процессов. Также обратите внимание, что специальный “-.slice” не показан, поскольку он неявно идентифицируется с корнем всего дерева.

    Лимиты ресурсов могут быть установлены для служб, областей и срезов одинаково. …

Перейдите по ссылкам выше, чтобы получить полное объяснение.

  1. « Cgroups v2: управление ресурсами стало еще хуже во второй раз » (14 октября 2016 г.), автор davmac:

    Вы можете создать вложенную иерархию так, чтобы в других группах были группы, а вложенные группы совместно использовали ресурсы своей родительской группы (и могут быть дополнительно ограничены). Вы перемещаете процесс в группу, записывая его PID в один из контрольных файлов группы. Поэтому группа потенциально содержит как процессы, так и подгруппы.

    Двумя очевидными ресурсами, которые вы можете захотеть ограничить, являются память и процессорное время, и у каждого из них есть «controller», но потенциально есть другие (например, пропускная способность ввода / вывода), и некоторые controllerы Cgroup на самом деле не управляют использованием ресурсов. как таковой (например, controller / подсистема «морозильник»). Интерфейс Cgroups v1 позволил создать несколько иерархий с разными controllerами (значение этого сомнительно, но возможность есть).

    Важно отметить, что процессы наследуют свое членство в cgroup от своего родительского процесса и не могут выйти из (или войти) в cgroup, если у них нет соответствующих привилегий, что означает, что процесс не может избежать своих ограничений, наложенных на него путем разветвления. Сравните это с использованием setrlimit, где использование процесса памятью (например) может быть ограничено с помощью ограничения RLIMIT_AS (адресное пространство), но процесс может быть разветвлен, и его дочерние элементы могут потреблять дополнительную память без рисования из ресурсов оригинала. процесс. С другой стороны, с Cgroups процесс и все его дочерние элементы извлекают ресурсы из содержащей группы.

    Контроллеры cgroup реализовали несколько ручек, которые никогда не будут приняты в качестве открытых API, потому что они просто добавляли управляющие ручки в псевдофайловую систему управления системой. В результате cgroup получили интерфейсные ручки, которые не были должным образом абстрагированы или уточнены, и непосредственно раскрыли внутренние детали ядра.

    Эти кнопки открывались отдельным приложениям через плохо определенный механизм делегирования, эффективно использующий cgroup в качестве ярлыка для реализации общедоступных API без прохождения требуемой проверки.

    cgroup v1 позволяет streamам находиться в любых cgroups, что создает интересную проблему, когда streamи, принадлежащие родительской cgroup и ее дочерним cgroups, конкурируют за ресурсы. Это было противно, так как два разных типа сущностей конкурировали, и не было никакого очевидного способа решить это. Разные controllerы делали разные вещи.

  2. Также см. Документацию cgroup v2: « Проблемы с v1 и обоснования для v2 »:

    Несколько Иерархий

    cgroup v1 допускает произвольное количество иерархий, и каждая иерархия может содержать любое количество controllerов. Хотя это казалось высоким уровнем гибкости, на практике это было бесполезно.

    Например, поскольку существует только один экземпляр каждого controllerа, controllerы типа утилит, такие как морозильник, которые могут быть полезны во всех иерархиях, могут использоваться только в одной. Эта проблема усугубляется тем фактом, что controllerы не могут быть перемещены в другую иерархию после заполнения иерархий. Другая проблема заключалась в том, что все controllerы, связанные с иерархией, были вынуждены иметь абсолютно одинаковое представление об иерархии. Было невозможно изменить степень детализации в зависимости от конкретного controllerа.

    На практике эти проблемы сильно ограничены тем, какие controllerы могут быть помещены в одну и ту же иерархию, и большинство конфигураций прибегают к размещению каждого controllerа в своей собственной иерархии. Только близкородственные, такие как controllerы cpu и cpuacct, имеют смысл размещать в одной иерархии. Это часто означало, что пользовательская область в конечном итоге управляла несколькими одинаковыми иерархиями, повторяя одни и те же шаги в каждой иерархии, когда была необходима операция управления иерархией.

    Кроме того, поддержка нескольких иерархий стоила очень дорого. Это значительно усложнило реализацию ядра cgroup, но, что более важно, поддержка нескольких иерархий ограничивала возможности использования cgroup в целом и возможности controllerов.

    Не было ограничений на количество иерархий, что означало, что членство в cgroup streamа не может быть описано в конечной длине. Ключ может содержать любое количество записей и имеет неограниченную длину, что делает его очень неудобным для манипулирования и приводит к добавлению controllerов, которые существуют только для идентификации членства, что, в свою очередь, усугубляет первоначальную проблему растущего числа иерархий.

    Кроме того, поскольку controller не может ожидать каких-либо ожиданий относительно топологий иерархий, которые могут быть включены другими controllerами, каждый controller должен был предполагать, что все другие controllerы были присоединены к полностью ортогональным иерархиям. Это сделало невозможным или, по крайней мере, очень громоздким, чтобы controllerы сотрудничали друг с другом.

    В большинстве случаев использование controllerов в иерархиях, которые полностью ортогональны друг другу, необязательно. Обычно требуется способность различаться по степени детализации в зависимости от конкретного controllerа. Другими словами, иерархия может быть свернута от листа к корню при просмотре с определенных controllerов. Например, данная конфигурация может не заботиться о том, как распределяется память за пределами определенного уровня, и при этом все еще хочет контролировать, как распределяются циклы ЦП.

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

  1. Связь между Леннартом Поеттерингом (разработчик systemd) и Дэниелом П. Берранджем (Redhat) в среду, 20.07.16 12:53, извлеченная из архивов systemd-devel под названием: « [systemd-devel] Ограничение ВСЕХ процессов в ЦП / ОЗУ через cpuset controller “:

    В среду, 20.07.16 12:53, Даниэль П. Берранж (berrange at redhat.com) писал:

    Для виртуализированных хостов довольно часто требуется ограничить все процессы ОС хоста подмножеством процессорных / RAM-узлов, оставляя остальные доступными для исключительного использования QEMU / KVM. Исторически люди использовали kernel ​​«isolcpus» в качестве аргумента этого, но в прошлом году его семантика изменилась, так что любые процессоры, перечисленные там, также исключаются из распределения нагрузки по расписанию, что делает его совершенно бесполезным в общих случаях использования не в реальном времени. где вы все еще хотите, чтобы streamи QEMU были сбалансированы между процессорами.

    Таким образом, единственный вариант – использовать controller cpuset cgroup для ограничения прокосов. AFAIK, в настоящее время systemd не имеет явной поддержки controllerа cpuset, поэтому я пытаюсь найти «оптимальный» способ добиться этого за спиной systemd, одновременно сводя к минимуму риск того, что будущие выпуски systemd могут что-то сломать.

    В среду, 20 июля 2016 года в 03:29:30 +0200, Леннарт Петеринг ответил:

    Да, мы не поддерживаем это на данный момент, но мы бы хотели. Дело в том, что интерфейс ядра для него довольно прост, как сейчас, и пока это не исправлено, мы вряд ли будем поддерживать это в systemd. (И, как я понял, Tejun в mem и cpu в процессоре, вероятно, тоже не останется таким, как сейчас).

    Следующее сообщение

    В среду, 20.07.16 14:49, Даниэль П. Беррандж (berrange на redhat.com) писал:

    Скорее всего, cgroupsv2 сломает многие вещи после переключения дистрибутивов, поэтому я предполагаю, что это не будет сделано в незначительном обновлении – только в новом выпуске дистрибутива, так что, не так уж и важно.

Я надеюсь, что это проясняет ситуацию.

  • Как монтировать / usr во время загрузки без initramf?
  • Как я могу систематизировать цели для остановки служб от других целей?
  • Как параметр systemd Type влияет на запуск других устройств?
  • Есть ли способ разработки сценариев выскочки на Fedora 21?
  • Могу ли я задержать запуск скрипта systemd при загрузке?
  • systemd, которому нужен пароль .ssh / id_dsa
  • Сервис и поддержка Systemd Initrd
  • systemd share cifs "ошибка монтирования (13): разрешение отклонено"
  • Запуск системного модуля systemd при следующей загрузке, но не последующие загрузки
  • Как просмотреть журнал stderr только с помощью журналаctl?
  • Почему расширение параметра bash не работает внутри системных файлов systemd?
  • Interesting Posts

    Как автоматически создать пустой каталог при создании нового пользователя?

    ls дает мне разные заказы сортировки во время работы cron

    Ошибка при загрузке разделяемых библиотек: libdl.so.2 (и другие), нет такого файла. Существуют библиотеки

    Сгенерируйте порядковый номер для каждой строки в конце. Изменено. Может ли кто-нибудь помочь и предложить на этом

    Почему Linux должен иметь как `/ dev / cdrom`, так и` / media / cdrom`?

    скрипт, в котором перечислены все файлы в каталоге и подкаталогах, отсортированных по размеру, перечисляя только имена файлов, а не полные пути

    Каковы все пробелы в файле / etc / fstab?

    Как написать вывод на экран из службы systemd во время загрузки?

    Имеются ли разделители на уровне блоков?

    Как клонировать установку Linux на внешний жесткий диск?

    Как продолжать работу в Android при отключении терминала adb? команда "nohup" не найдена

    grep путается именами файлов с тире

    Как создать случайные шестнадцатеричные цифры без пробелов?

    Как контролировать попытки аутентификации в PAM?

    apache2 нет доступных сокетов для прослушивания, но ничего не слышно на порту

    Linux и Unix - лучшая ОС в мире.