Трассировка семафоров средствами ядра
Крестов С.Г., Ковтушенко А.П., МГТУ им. Н.Э. Баумана [email protected], [email protected]
Аннотация
Данная работа посвящена системе, позволяющей проводить трассировку состояний семафоров в операционной системе Linux. Была изучена подсистема межпроцессорного взаимодействия ОС Linux и способы трассировки ядра.
1 Введение
При написании программ разработчик сталкивается в числе прочих с задачей уменьшения времени работы программы. Существует несколько подходов к решению этой проблемы:
• использование более совершенных алгоритмов;
• запуск программы на более производительной ЭВМ;
• разбиение задачи на подзадачи и их параллельный запуск.
В зависимости от специфики решаемой проблемы и реализации можно выбрать один из этих способов или разработать свой подход. Рассмотрим последний пункт и сложности, с ним связанные. Разбиение задачи на отдельные процессы выполняется, когда можно выделить в программе участки, которые могут выполняться не только последовательно. Примеры задач, которые поддаются распараллеливанию при таком подходе:
• выделение клиентов в разные потоки на сервере;
• применение разбиения быстрой сортировки на разных участках массива;
• выделение отдельного потока под пользовательский интерфейс;
• параллельный расчёт свойств точки в компьютерной графике.
Такой подход даёт массу преимуществ, но порождает множество проблем. Например, организация доступа к разделяемым ресурсам, организация межпроцессного взаимодействия, гонки процессов. В этой статье будет затронут один из подходов к решению проблемы разделяемых ресурсов и межпроцессного взаимодействия.
Разделяемый ресурс — ресурс, который используют в своей работе несколько процессов. Проблема работы с подобным ресурсом заключается в том, что если несколько потоков решат им воспользоваться одновременно, последствия могут повлиять на корректность работы программы. Например, существуют два процесса, которые делят некоторую переменную. Предположим, что в один и тот же момент времени один поток решил прочитать эту переменную, а второй поток решил записать в эту переменную новые данные. В результате этих действий программа может начать работать некорректно. Для решения подобных проблем был разработан механизм блокировок процессов, одна из реализаций которого будет рассмотрена в этой статье. В таком случае может произойти падение производительности, если в ходе выполнения множество потоков попытаются захватить одну и ту же блокировку. Однако в данной статье будет описан подход к созданию инструмента для выявления проблемных мест в многопоточных системах.
На данный момент для целей трассировки в ОС Linux существует единственный инструмент, ipcs, представляющий информацию о средствах межпроцессного взаимодействия, зарегистрированных в системе. В частности, о семафорах представляется следующая информация:
• имя и группа владельца;
• имя и группа создателя;
• количество семафоров в наборе;
• время завершения последней операции с множеством семафоров.
Для выявления блокировок, замедляющих систему, желательно иметь следующую информацию:
• владелец семафора;
• процессы, находящиеся в ожидании семафора.
Эти сведения на данный момент не предоставляются, хотя они помогут выявить семафоры с наибольшими очередями и внести соответствующие изменения в ПО.
Таким образом, в рамках работы должны быть решены следующие задачи:
• анализ реализации семафоров в ОС Linux;
• анализ возможных способов трассировки;
• извлечение информации о том, кто заблокировал семафор;
• извлечение информации о процессах, ожидающих семафор.
В данной работе использовалось ядро MAO^-generic'^ ОС Ubuntu 16.04 LTS.
2 Межпроцессное взаимодействие
В ядре ОС Linux предусмотрен механизм межпроцессного взаимодействия,
включающий в себя разделяемую память, очереди сообщений, семафоры, сигналы, сокеты, каналы, файлы. Остановимся на первых трёх, которые включены в систему SysVIPC.
Объекты системы IPC создаются в несколько этапов.
• Получают ключ системы IPC.
• Через ключ получают идентификатор необходимого объекта.
• Объект инициализируется. Пользователь работает не с самим объектом, а с его идентификатором в системе.
2.1 Средства межпроцессного взаимодействия
Разделяемая память позволяет динамически выделить память в адресном пространстве операционной системы, что позволяет организовать совместный доступ нескольких процессов к памяти. Использование такой памяти ограничено только фантазией программиста, однако важно учитывать необходимость организации безопасного доступа.
Очереди сообщений — механизм межпроцессного взаимодействия, при котором один процесс может отправить другому сообщение. Сообщение ставится в очередь и удаляется из неё после прочтения.
Семафор — самый часто употребляемый метод для синхронизации потоков и для контролирования одновременного доступа множеством потоков/процессов к общей памяти (к примеру, глобальной переменной). Взаимодействие между процессами в случае с семафорами заключается в том, что процессы работают с одним и тем же набором данных и корректируют свое поведение в зависимости от этих данных. Так, если процесс решил получить доступ к переменной, то он должен сначала заблокировать соответствующий
семафор. Если этот семафор уже заблокирован, то процесс переводится в режим ожидания до освобождения семафора. Также Linux поддерживает наборы семафоров, позволяющие группировать семафоры для удобства использования.
2.2 Семафоры в sysvipc
В ядре Linux все объекты межпроцессного взаимодействия хранятся в едином массиве (ipc objects) и идентифицируются единым ключом. Это относится и к семафорам. Этот ключ используется для общения с объектом из клиентской программы. Семафоры реализуются при помощи структур[1]:
• sem — семафор;
• sem_array — набор семафоров.
Эти структуры хранят в себе также очереди операций, хранящие операции и pid, процессов, ожидающих этот семафор.
2.3 Идея работы
В ходе работы процесс взаимодействует с семафором или набором семафоров через системные вызовы:
• semget — получить или создать семафор;
• semop — произвести операцию над семафором;
• semctl — произвести операцию над множеством семафоров.
Сравним semop со схожим с ним semtimedop, выполняющим ту же функцию, но с ограничением по времени. Проверив исходный код, можно установить, что semop и semtimedop вызывают одну и ту же функцию в ядре, но с разными параметрами. В теле этой функции находим вызов, применяющий операцию к массиву семафоров.
2.4 Символы ядра
Для работы загружаемых модулей ядро предоставляет некоторый набор имён, к которым можно обращаться. Так как не все символы ядра разрешены для доступа возникает ряд проблем, связанных с доступом к необходимым функциям ядра.
2.5 О разработке
В ходе проведенного анализа были выявлены общие принципы работы и аспекты, на которые необходимо обратить внимание при разработке инструмента для трассировки семафоров в Linux. Также на
общем уровне был разобран процесс использования семафора и других средств межпроцессного взаимодействия. Полученная на этом этапе информация показывает необходимые для работы программы данные, и особенности реализации семафоров, которые необходимо будет учитывать в приведённой работе.
3 Доступ к данным
После обнаружения необходимых полей необходимо получить к ним доступ. Были рассмотрены следующие способы получения доступа к полям.
3.1 Файловая система proc
Файловая система proc служит для предоставления пользователю информации о подсистемах ядра и для динамического конфигурирования. По сути, proc является псевдофайловой системой, так как содержащиеся в ней файлы физически не существуют, а попытка взаимодействия с ними вызывает определённую функцию, которая предоставляет необходимую информацию. Используя файловую систему proc, также можно получить статистику по семафорам. Для этого надо вывести содержимое файла /proc/sysvipc/sem. В этом файле имеется информация об id набора семафоров, правах доступа, количестве семафоров в наборе. Однако этой информации дял решения задачи недостаточно.
3.2 Внутренние символы ядра
При помощи уже описанной выше файловой системы возможно узнать адрес объекта внутри ядра через его имя. Также эту информацию можно получить через функцию ядра.
3.3 KProbe
KProbe — это механизм отладки ядра. Он позволяет вмешаться в выполнение функций ядра для получения отладочной информации
[5].
Сам KProbe задаётся через адрес подменяемой функции и представляет собой функции, вызываемые до вызова трассируемой функции, после вызова трассируемой функции.
3.4 JProbe
JProbe также является средством отладки ядра, однако предоставляет доступ к аргументам. JProbe вызывается перед запрашиваемой функцией, и его функция-обработчик должна иметь прототип, совпадающий с заменяемой функцией с точностью до имени [5].
JProbe позволяет получить доступ к аргументам трассируемой функции, что расширяет спектр его применения.
Было решено использовать JProbe для обёртки внутренних функций ядра и вывода необходимых данных.
4 Состав программы
Получившаяся программа состоит из двух частей:
• модуль ядра;
• пользовательская программа.
4.1 Модуль ядра
Linux предоставляет возможность динамически добавлять функциональность в ядро при помощи загружаемых модулей ядра. Это даёт возможность использовать некоторые функции ядра, недоступные для программ, находящихся в адресном пространстве пользователя.
Для реализации модуля ядра был использован язык C ввиду высокой скорости написания программ (если сравнивать с ASM) и возможности совершать действия, необходимые для решения поставленной задачи.
Модуль работает по следующей схеме.
• Регистрация JProbe.
• Создание файла в /proc.
• Сбор статистики.
• Выдача статистики.
• Удаление файла.
• Удаление JProbe.
В JProbe регистрируется функция, которая собирает статистику по набору семафоров и запоминает её. Файл /proc используется для взаимодействия модуля и пользовательской программы.
4.2 Сбор статистики
Для сбора статистики JProbe применяется к функции применения операции, так как она имеет доступ к необходимым данным в ядре через аргументы.
В ходе сбора статистики происходит обход списков заблокированных операций набора семафоров и каждого семафора в отдельности. Информация о каждом заблокированном процессе заносится в кольцевой буфер. Также в другой буфер записывается информация о первой и последней записях сессии сбора статистики. Это делается для того, чтобы можно было проследить динамику изменения очередей в зависимости от количества прошедших над набором операций. После прочтения записи из обоих буферов удаляются.
4.3 Пользовательская программа
Пользовательская программа осуществляет загрузку, выгрузку модуля ядра и запуск тестируемой программы переданной как комманду bash. В процессе выполнения пользовательская программа опрашивает файл в директории /proc и записывает все данные из него в отчёт.
5 Тестирование
В ходе тестов было установлено, что система справляется с поставленными задачами.
Был реализован алгоритм читателей-писателей с задержкой через цикл на 999999999 итераций. Был получен отчёт следующего вида.
0 2 6986
#####
0 2 6986
0 3 6990
#####
0 2 6986
0 3 6990
0 3 6995
#####
0 2 6986
0 3 6990
0 3 6995
0 2 6988
#####
Первый столбец — id набора семафоров, второй столбец — номер семафора, третий столбец — pid заблокированного процесса.
Так, в первой секции представлен один процесс заблокированный на втором семафоре из набора семафоров с индексом 0.
В следующей секции был заблокирован ещё один процесс на том же наборе, но на третьем семафоре.
Заключение
Целью данной работы был поиск путей трассировки семафоров в Linux без изменения исходного кода программы. В результате проделанной работы были рассмотрены принципы написания загружаемого модуля ядра Linux, экспортируемые и не экспортируемые символы, проецирование пространства имён на файловую систему /proc. Также был рассмотрен механизм работы семафоров в Linux и основы методов трассировки ядра при
помощи встроенных средств. В ходе написания проекта был разработан модуль ядра, позволяющий получать текущую информацию (при помощи файловой системы proc) о состоянии семафора и процессах, заблокированных на нём.
Список литературы
Linux kernel IPC. — [2016].URL
http://tldp.org/LDP/lpg/node7.html. (дата обращения 01.12.2016)
Исходный Код ядра Linux. — [2016]. URL http: //lxr.free-electrons.com/.(дата обращения
01.12.2016)
linux kernel API. — [2016]. URL https://www. kernel.org/doc/htmldocs/kernel-api/. (дата обращения 01.12.2016)
.introducing-slinux-kernel-symbols. — [2016]. URL https://onebitbug.me/2011/03/04/introducing-linux-kernel-symbols/. (дата обращения
01.12.2016)
An introduction to KProbes. — [2016]. URL https: //lwn.net/Articles/132196/. (дата обращения 01.12.2016)