1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/open-atom-os-atom-os-kernel

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Кэширование в Linux: принципы работы и ограничения

Cache, если kswapd не смог выполнить сбор мусора или выполнял его медленно, то происходит переход к direct_reclaim для более глубокого сбора мусора до тех пор, пока оставшаяся память системы не будет соответствовать требованиям. Если это не помогает, то может произойти OOM. Достаточно ли такого механизма?

В некоторых сценариях с атомарными операциями, например, в контексте прерывания, невозможно заснуть, поэтому при выделении памяти в таких сценариях не происходит перехода к direct_reclaim и даже не пробуждается процесс kswapd. Например, в функции обработки мягкого прерывания сетевого адаптера может возникнуть ситуация, когда page cache занимает слишком много места, и системе не хватает оставшейся памяти для выделения данных пакета. В этом случае пакет просто отбрасывается. Это как раз та проблема, которую решает ограничение page cache.

Введение в page cache

Page cache — это дисковый кэш файлов, реализованный в ядре Linux. Его основная цель — уменьшить количество операций ввода-вывода на диск, делая доступ к данным более быстрым за счёт их кэширования в физической памяти. Page cache используется для повышения производительности доступа к файлам. Когда именно создаётся page cache и где он хранится?

На схеме ниже показано примерное расположение page cache в системе:

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

Если в системе нет соответствующего page cache, вызывается соответствующая файловая система, которая инициирует операцию чтения с диска. После того как данные прочитаны и возвращены, вызывается функция add_to_page_cache_lru() для добавления только что прочитанных страниц (pages) в список lru. Список lru управляет page cache, разделяя его на активные и неактивные списки в зависимости от активности доступа. Более подробную информацию о механизме управления lru можно найти в статьях в интернете.

Анализ реализации ограничения page cache

Ограничение page cache вступает в действие, когда приложение добавляет page cache (вызывая функцию add_to_page_cache_lru()). Оно проверяет, не превышает ли размер page cache установленный предел (/proc/sys/vm/pagecache_limit_ratio).

Если обнаружено превышение лимита, вызывается функция shrink_page_cache() для освобождения page cache. Эта функция является основной для освобождения page cache.

Функция shrink_page_cache() освобождает page cache до достижения порогового значения. Процесс освобождения происходит следующим образом:

  1. Освобождение неактивных страниц из списка lru inactive_list.
  2. Освобождение активных страниц из списка lru active_list, но не включая анонимные и отображённые страницы, а также грязные страницы.
  3. Освобождение активных страниц из списка lru active_list, включая анонимные страницы, но исключая отображённые и грязные страницы (если pagecache_limit_ignore_dirty равен 0, то также освобождаются грязные страницы).

Важно отметить, что этот процесс не обязательно должен завершиться полностью. Как только объём page cache становится меньше порогового значения, процесс освобождения завершается. Анонимные и отображённые страницы не освобождаются, поскольку они требуют swap, что занимает больше времени, а отображённые страницы представляют собой только отображение в таблице страниц процесса.

Описание и использование функций ограничения page cache

Мы предоставляем три интерфейса /proc:

  • /proc/sys/vm/pagecache_limit_ratio — значение по умолчанию равно 0. Чтобы включить ограничение page cache, можно использовать команду echo x > /proc/sys/vm/pagecache_limit_ratio, где x находится в диапазоне от 0 до 100. Значение, отличное от нуля, например 30, означает, что page cache может занимать не более 30% общей памяти системы.
  • /proc/sys/vm/pagecache_limit_reclaim_ratio — определяет долю фактического освобождения page cache относительно установленного предела pagecache_limit_ratio. Для предотвращения частых освобождений page cache мы увеличиваем этот показатель на 2%.
  • /proc/sys/vm/pagecache_limit_ignore_dirty — указывает, следует ли игнорировать грязные страницы при подсчёте использования памяти page cache. По умолчанию значение равно 1, что означает игнорирование грязных страниц, так как их освобождение занимает больше времени.
  • /proc/sys/vm/pagecache_limit_async — определяет способ освобождения page cache:
    • 1 — асинхронное освобождение page cache. Создаётся поток ядра kpclimitd для выполнения освобождения.
    • 0 — синхронное освобождение. Не создаётся специальный поток для освобождения, и процесс выполняется в контексте приложения. По умолчанию используется синхронное освобождение (значение равно 0). Асинхронное освобождение имеет преимущество в том, что освобождение не влияет на контекст бизнес-процесса, но может привести к неточным результатам контроля использования page cache и потенциальному OOM. Синхронное освобождение обеспечивает более точный контроль над использованием page cache, но может вызвать дополнительную нагрузку на бизнес-процесс и временные задержки. Поэтому выбор между этими двумя вариантами зависит от конкретных требований и условий бизнес-сценария.

Заключение

После тестирования было подтверждено, что можно ограничить использование page cache согласно настройкам пользователя. При скорости чтения и записи данных 1 Гбит/с дополнительная нагрузка на процессор, вызванная ограничением page cache, составляет менее 3%. Однако уменьшение объёма page cache может увеличить вероятность промахов кэша, хотя обычно мы не устанавливаем слишком низкие ограничения на page cache, и эти параметры могут быть адаптированы под конкретные требования бизнес-сценариев. Поддержка kpatch для arm64

Включает удаление регистрации ftrace_ops, а также функции, связанные с переадресацией данных при загрузке модуля.

Переадресация данных: краткое описание кода

Рисунок 1.

Инструменты пользовательского режима: модификация

Из предыдущего описания работы в пользовательском режиме мы можем видеть, что при создании diff.o мы не можем использовать возможности компилятора и должны анализировать вручную.

  1. ELF-файлы

    • Объектный файл (obj) — это только скомпилированный код, который содержит информацию о неопределённых символах. В C-языке основной единицей кода является функция, но в obj-файле основной единицей является section. Существуют различные типы section, такие как text и data.
    • Исполняемый файл — это результат связывания нескольких объектных файлов, которое позволяет определить конкретные адреса каждого символа в памяти.
    • Динамически подключаемая библиотека (so) — ядро использует только объектные файлы и исполняемые файлы.
  2. Процесс связывания В объектном файле есть важный section — relocation section. Связывание в основном решает следующие проблемы: * Объединение различных section, таких как текстовые сегменты всех obj-файлов. * Разрешение символов. После объединения можно определить адрес каждого символа, на который ссылается объектный файл, не находящийся в текущем файле. * Определение адреса каждого символа и выполнение переадресации согласно каждой записи в relocation section.

    Функция переадресации в архитектуре arm64 имеет несколько типов:

     1. R_AARCH64_ABS64: абсолютный адрес символа во время выполнения.
         *addr = symbol.addr
    
     2. R_AARCH64_PREL32: вычисление адреса символа относительно текущего адреса.
         *addr = symbol.addr - addr
    
     3. R_AARCH64_CALL26: инструкция br для переадресации.
         *addr = symbol.addr - addr
    
     4. R_AARCH64_ADR_PREL_PG_HI21: переадресация страницы.
         *addr = Page(symbol.addr) - Page(addr)
    
     5. R_AARCH64_ADD_ABS_LO12_NC: добавление абсолютного адреса младших 12 бит.
         *addr = symbol.addr[11:0]
  3. Kernel module Понимая принцип работы процесса связывания, можно легко понять, как работает kernel module. Фактически, ядро переносит работу компоновщика в ядро, позволяя загружать объектные файлы. Файл ko по сути является объектным файлом, но с добавлением других section для описания вашего kernel module. Когда вы используете команду insmod, ядро анализирует relocation section файла ko, разрешает символы и успешно запускает ваш код.

    Следует отметить, что хотя kernel module переносит процесс связывания в ядро, из-за ограничений открытых исходных кодов он помогает разрешать только символы, экспортированные через EXPORT_SYMBOL_XXX.

Поиск изменений в функциях Можно сравнить соответствующие section, чтобы найти изменения. Однако сложно определить, какая именно функция изменилась. Компилятор предоставляет специальные опции для создания отдельного section для каждой функции и глобальной переменной, что упрощает поиск изменённых функций.

Создание diff.o для архитектуры arm64 После сравнения мы можем найти изменённые section и соответствующие функции. Для создания diff.o необходимо учесть несколько моментов: * Поскольку diff.o не создаётся кодом C, нам нужно использовать инструменты для ручного создания нового obj-файла. Это можно сделать с помощью библиотеки libelf. * Изменённые функции могут вводить новые глобальные символы, которые могут привести к созданию новых section. Эти section также должны быть включены в diff.o. * Изменённые функции могут вызывать уже существующие системные символы. Мы можем использовать relocation section, чтобы ядро помогло нам разрешить эти символы. Однако нам нужно убедиться, что оно разрешает все системные символы.

Анализ основан на использовании спецификаций ELF для архитектуры arm64, поэтому реализация должна соответствовать этим спецификациям.

Проверка

Сначала необходимо загрузить модуль kpatch, затем новый функциональный модуль, созданный инструментами пользователя. Используйте команду lsmod для проверки успешной загрузки модулей. Kpatch также предоставляет интерфейс sysfs для просмотра информации о загруженных новых функциональных модулях, включая адреса старых и новых функций. Вы можете использовать команду /sys/kernel/kpatch/xxx/enabled для удаления модуля и восстановления исходной функции.

Процесс проверки включает следующие шаги:

  • Рисунок 2.

Защита процесса от gdb

Эта функция предотвращает доступ даже root-пользователя к процессу после установки защиты. Она также предотвращает получение памяти процесса, загрузку динамических библиотек и другие действия. Gdb использует системный вызов ptrace для реализации этих функций, и мы модифицируем этот вызов, добавляя условия для предотвращения доступа к процессу. Модификации включают:

--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -1118,6 +1118,9 @@ static struct task_struct *ptrace_get_task_struct(pid_t pid)
 #define arch_ptrace_attach(child)      do { } while (0)
 #endif
 
+int (*ptrace_pre_hook)(long request, long pid, struct task_struct *task, long addr, long data);
+EXPORT_SYMBOL(ptrace_pre_hook);
+
 SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
                 unsigned long, data)
 {
@@ -1136,6 +1139,12 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
               ret = PTR_ERR(child);
               goto out;
           }
 
+       
+       if (ptrace_pre_hook) {
+       ret = ptrace_pre_hook(request, pid, child, addr, data);
+       if (ret)
+       goto out_put_task_struct;
+       }

Здесь ptrace_pre_hook — указатель на функцию обратного вызова, которая устанавливается в модуле ttools. Модуль ttools создаёт символьное устройство /dev/ttools и предоставляет два ioctl для включения и отключения защиты процесса от gdb.

#define TTOOLS_PTRACE_PROTECT       _IO(TTOOLS_IO, 0x00)
#define TTOOLS_PTRACE_UNPROTECT     _IO(TTOOLS_IO, 0x01)

Пользователь должен сначала выполнить команду modprobe ttools для загрузки модуля ttools, а затем использовать код для включения или отключения защиты процесса. Реализация модуля ttools находится в каталоге kernel/tkernel/ttools. Троттлинг с использованием cgroup для ограничения IOPS и BPS. Одновременно на уровне диспетчеризации ввода-вывода ядро изолирует процессы, используя различные планировщики, в соответствии с пропорциями их весов.

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

Идея дизайна

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

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

Для каждой группы мы вводим два веса, представляющих сумму их весов:

  • leaf weight — отношение между этой группой и её непосредственными дочерними группами;
  • weight — определяет долю использования этой группы от общего веса родительского узла.

Метод расчёта:

Для некоторой группы c записываем:
c.sum = c.leaf_weight + Σc.child[i].weight, где c.child[i].weight — значение веса всех прямых дочерних групп группы c.

Доля пропускной способности корневого узла составляет 100%.
Доля пропускной способности некорневой группы c составляет:
c.weight / c.parent.sum.

Использование

В каталоге cgroup blkio есть два файла: blkio.throttle.leaf_weight_device blkio.throttle.weight_device.

Конфигурирование этих файлов выполняется следующим образом:

echo major:min $weight > $file,
где:
major и min соответствуют блочному устройству (не может быть раздел);
вес находится в диапазоне от 10 до 1000;
file — это файлы конфигурации двух интерфейсов группы c.

Пример использования

echo "259:2 800 > /sys/fs/cgroup/blkio/blkio.throttling.leaf_weight_device"
echo "259:2 200 > /sys/fs/cgroup/blkio/offline_tasks/blkio.throttling.weight_device".
Это означает, что мы хотим, чтобы вес корневого узла для устройства 259:2 был в четыре раза больше, чем у offline_tasks. Когда корневой узел в течение некоторого времени не выполняет операции ввода-вывода для этого устройства, весь его вес будет распределён между узлами offline_tasks.

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

AtomOS-Kernel — это компонент ядра операционной системы AtomOS, основанный на ядре Linux с открытым исходным кодом (upstream Linux kernel). В него были добавлены многочисленные функции, адаптированные для конкретных сценариев использования, а также проведена оптимизация производительности. Это обеспечивает высокую производительность, более стаби... Развернуть Свернуть
GPL-2.0
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/open-atom-os-atom-os-kernel.git
git@api.gitlife.ru:oschina-mirror/open-atom-os-atom-os-kernel.git
oschina-mirror
open-atom-os-atom-os-kernel
open-atom-os-atom-os-kernel
4.14/master