|
Навигация
|
Главная » Linux Доступ к ядру Linux через файловую систему /proc (исходники)Источник: IBM developerWorks Россия М. Тим Джонс Изначально файловая система /proc разрабатывалась как средство предоставления информации о выполняющихся в системе процессах. Но из-за ее удобства многие подсистемы ядра стали использовать эту файловую систему как средство предоставления информации и динамического конфигурирования.Файловая система /proc содержит каталоги (для структурирования информации) и виртуальные файлы. Виртуальный файл, как уже было сказано, может предоставлять пользователю информацию, полученную из ядра и, кроме того, служить средством передачи в ядро пользовательской информации. На самом деле, виртуальный файл не обязательно выполняет обе функции, но в этой статье я расскажу о том, как настроить файловую систему как для ввода, так и для вывода. В короткой статье нельзя описать файловую систему /proc во всех деталях, но вполне возможно продемонстрировать несколько вариантов ее использования, дающих представление о ее возможностях. В листинге 1 показан интерактивный обзор некоторых элементов /proc. Мы видим корневой каталог файловой системы /proc. Обратите внимание на файлы с номерными именами в левой части листинга. Это - каталоги, содержащие информацию о выполняющихся в системе процессах. Process-id равный 1 присвоен процессу init , который в системе GNU/Linux запускается первым. Если выполнить команду ls для такого каталога, будет отображен список находящихся в нем файлов. В каждом файле содержатся те или иные сведения о процессе. Например, для того, чтобы посмотреть сведения о параметрах командной строки, с которыми был запущен процесс init , достаточно просмотреть содержимое файла cmdline с помощью команды cat .В /proc есть и другие интересные файлы. Например, cpuinfo , содержащий сведения о типе и производительности центрального процессора, pci , из которого можно получить информацию об устройствах на шине PCI и modules , в котором находится список загруженных в ядро модулей.Листинг 1. Интерактивный обзор файловой системы /proc
В листинге 2 показаны чтение и запись параметров ядра в виртуальный файл, находящийся в /proc. Приведенный пример кода отображает значение параметра, управляющего режимом "IP forwarding" стека TCP/IP ядра и затем включает его. Листинг 2. Чтение и запись /proc (конфигурирование ядра)
Другим способом изменения параметров конфигурации ядра является использование команды sysctl . На самом деле, /proc - не единственная виртуальная файловая система в ОС GNU/Linux. Аналогичная файловая система sysfs имеет сходные функциональные возможности и немного более удачную структуру (при ее разработке был учтен опыт /proc). Тем не менее /proc является де-факто стандартом и, несмотря на то, что sysfs имеет некоторые преимущества, будет и впредь оставаться таковым. Можно упомянуть еще одну виртуальную файловую систему - debugfs , которая (как следует из ее названия), представляет собой скорее отладочный интерфейс. Ее преимуществом является простота, с которой происходит экспорт значения из ядра в пользовательское пространство (фактически, это требует единственного системного вызова). Знакомство с модулями ядраХорошим примером для демонстрации возможностей файловой системы /proc являются загружаемые модули ядра (LKM), позволяющие динамически добавлять и, при необходимости, удалять код из ядра Linux. LKM завоевали популярность как удобный механизм реализации в ядре Linux драйверов устройств и файловых систем.Если вам приходилось вручную собирать ядро Linux, вы, вероятно, обращали внимание на то, что многие драйверы устройств и другие компоненты ядра компилируются в виде модулей. Если драйвер скомпилирован как часть ядра, его код и статические данные занимают память даже тогда, когда он не используется. Но если скомпилировать драйвер как модуль, он будет занимать память только если он действительно необходим и загружен в ядро. Удивительно, но заметной потери производительности при использовании LKM не происходит. Это делает загружаемые модули незаменимым средством при сборке ядра с низкими требованиями к объему памяти и возможностью использования не только штатного набора оборудования, но и подключаемых устройств. Сравним код простого загружаемого модуля, приведенный в листинге 3 и обычный код ядра (не загружаемый динамически). В листинге 3 приведен код простейшего загружаемого модуля. (Ниже вы можете загрузить исходные коды всех примеров, приведенных в статье. Код в листинге 3 начинается с обязательного заголовка (описывающего интерфейс модуля, типы и макросы). Затем, с помощью макроса MODULE_LICENSE , указывается тип лицензии, под которой распространяется модуль. В данном примере мы используем лицензию GPL , чтобы не получать предупреждений о "заражении" ядра проприетарным кодом.Далее в листинге 3 следует определение функций модуля init и cleanup . Функция my_module_init вызывается при загрузке модуля и поэтому может использоваться для инициализации. Другая функция, my_module_cleanup , вызывается в момент выгрузки модуля. В ней происходит освобождение памяти и ликвидация следов пребывания модуля в ядре. Обратите внимание на то, что мы используем функцию printk : это аналог printf для ядра. С помощью макроса KERN_INFO можно записать в кольцевой буфер ядра произвольную строку (аналогично функции syslog ).Функции, вызываемые при загрузке и выгрузке модуля, задаются в заключительных строках листинга с помощью макросов module_init and module_exit . Такой способ определения вспомогательных функций init and cleanup позволяет называть их как угодно. Достаточно лишь сообщить их имена ядру.Листинг 3. Простой, но работоспособный загружаемый модуль (simple-lkm.c)
В листинге 3 приведен код простого, но работоспособного загружаемого модуля. Теперь мы соберем его и протестируем на ядре версии 2.6. Начиная с этой версии, в ядре появилась поддержка нового метода сборки модулей ядра, который, на мой взгляд, проще, чем методы, использовавшиеся ранее. Чтобы им воспользоваться, необходимо, помимо файла simple-lkm.c , создать make-файл, содержащий единственную строку, приведенную ниже:
Для сборки модуля, выполните команду make , как показано в листинге 4.Листинг 4. Сборка модуля
После сборки должен появиться файл simple-lkm.ko . Новый способ именования позволяет легко отличать модули ядра от обыкновенных объектов. Теперь, когда модуль готов, можно его загрузить, затем выгрузить и посмотреть на выведенные при этом сообщения. Чтобы загрузить модуль, выполните команду insmod ; для выгрузки выполните команду rmmod . Команда lsmod выводит список модулей, загруженных в данный момент (см. листинг 5).Листинг 5. Загрузка, проверка загрузки и выгрузка модуля
Следует учесть, что сообщения, генерируемые кодом ядра, выводятся в кольцевой буфер ядра, а не в stdout , поскольку последний привязан к конкретному процессу. Для просмотра сообщений в кольцевом буфере ядра, вы можете воспользоваться командой dmesg (или просмотреть сообщения в самой файловой системе /proc с помощью команды cat /proc/kmsg ). В листинге 6 приведены несколько последних сообщений, выведенных по команде dmesg .Листинг 6. Сообщения, сгенерированные тестовым модулем
Сообщения, выведенные тестовым модулем, видны в общем потоке сообщений ядра. А теперь предлагаю перейти от нашего простого примера к интерфейсам ядра, позволяющим разрабатывать загружаемые модули. Интеграция с файловой системой /procРазработчики загружаемых модулей могут использовать все интерфейсы, доступные их коллегам, разрабатывющим само ядро. Допускается даже использование ядром переменных и функций, экспортируемых загружаемым модулем. Детальный анализ этих интерфейсов выходит за рамки статьи, так что я просто расскажу о некоторых элементах, которыми я собираюсь воспользоваться для разработки более сложного модуля.Создание и удаление виртуального файла в /procДля того, чтобы создать виртуальный файл в файловой системе /proc, используется функцияcreate_proc_entry . Эта функция принимает в качестве параметров имя создаваемого файла, режим доступа к нему и один из подкаталогов /proc для его размещения. Функция create_proc_entry возвращает указатель на структуру proc_dir_entry (или NULL в случае возникновения ошибки). Полученный указатель можно использовать для настройки остальных параметров виртуального файла, таких, как функция, вызываемая при чтении из файла. Прототип функции create_proc_entry и фрагмент структуры proc_dir_entry показаны в листинге 7.Листинг 7. Элементы интерфейсов управления виртуальными файлами /proc
Чуть позже вы узнаете, как использовать команды read_proc and write_proc для задания функций чтения и записи виртуального файла.Для удаления файла из /proc, используйте функцию remove_proc_entry . При вызове в эту функцию передается строка, содержащая имя удаляемого файла и его местонахождение в файловой системе /proc (родительский каталог). Прототип этой функции приведен в листинге 7.Параметр parent принимает значение NULL если файл находится непосредственно в каталоге /proc или другое значение, соответствующее каталогу, в который вы хотите поместить файл. В таблице 1 приведена часть предопределенных переменных proc_dir_entry , передаваемых как значение параметра parent, и соответствующих им каталогов файловой системы /proc.Таблица 1. Список предопределенных переменных proc_dir_entry
Callback-функция записиВы можете записывать данные в виртуальный файл (из пользовательского пространства в ядро) с помощью функцииwrite_proc . Эта функция имеет прототип следующего вида:
Параметр filp представляет собой структуру, соответствующую открытому файлу устройства (нам он не понадобится). Параметр buff соответствует строке, передаваемой в модуль. Поскольку буфер, в котором находится строка находится в пользовательском пространстве, к нему нельзя будет получить непосредственный доступ из модуля. Параметр len содержит количество подлежащих записи байт, находящихся в buff . Параметр data содержит указатель на локальные данные (см. листинг 7). В нашем тестовом модуле сallback-функция записи служит для обработки входящих данных.В Linux предусмотрен набор API для перемещения данных между пользовательским пространством и пространством ядра. Для операций с данными, находящимися в пользовательском пространстве, в функции write_proc из нашего примера, используется семейство функций copy_from_user .Callback-функция чтенияВы можете считать данные из виртуального файла (из ядра в пользовательское пространство) с помощью функцииread_proc . Ее прототип выглядит так:
Параметр page содержит указатель на буфер, в который будут записаны данные, полученные из ядра, при этом параметр count определяет максимальное число символов, которое может быть записано в данный буфер. Если планируется получить более одной страницы данных (обычно, 4KB), следует использовать параметры start и off . После того, как все данные получены, установите признак eof (конец файла). По аналогии с кодом функции write , параметр data соответствует локальным данным. Буфер page , используемый в данной функции располагается в пространстве ядра. Следовательно, для записи в него не требуется вызов функции copy_to_user .Другие полезные функцииКроме обыкновенных файлов, в файловой системе /proc можно создавать каталоги, используя функциюproc_mkdir и символьные ссылки (symlinks ), используя proc_symlink . Файлы /proc, для которых определена только операция чтения (функция read ), можно создать единственным вызовом функции create_proc_read_entry , создающей файл и задающей для него функцию read_proc . Прототипы вышеупомянутых функций показаны в листинге 8.Листинг 8. Прочие полезные функции для работы с /proc
Выдача 'фортунок' с помощью файловой системы /procВ этом примере мы создадим загружаемый модуль ядра с поддержкой операций чтения и записи. Это простое приложение будет по запросу выдавать изречения-'фортунки'. После загрузки модуля, пользователь сможет записать в него текст 'фортунок' с помощью командыecho и затем считывать их по одной в случайном порядке, используя команду cat .Исходный код данного модуля приведен в листинге 9. Init -функция (init_fortune_module ) выделяет блок памяти для хранения 'фортунок' вызовом vmalloc и затем заполняет его нулями с помощью memset . После того, как cookie_pot создан и обнулен, в каталоге /proc создается виртуальный файл (типproc_dir_entry ) с именем fortune . После того, как файл (proc_entry ) успешно создан, происходит инициализация локальных переменных и структуры proc_entry . В соответствующие поля этой структуры записываются указатели на функции модуля read и write (см. листинги 9 и 10, а также информация о владельце модуля. Функция cleanup удаляет файл из файловой системы /proc и освобождает память, занимаемую cookie_pot .Хранилище 'фортунок' cookie_pot занимает страницу памяти (4KB) и обслуживается двумя индексами. Первый из них, cookie_index , определяет адрес, по которому будет записана следующая 'фортунка'. торой индекс - переменная next_fortune , содержит адрес 'фортунки', которая будет выдана по следующему запросу. После того как выдана последняя 'фортунка', переменной next_fortune присваивается адрес первого элемента и выдача начинается сначала.Листинг 9. Функции init/cleanup и переменные модуля.
Записать 'фортунку' в хранилище очень просто (см. листинг 10). Зная длину записываемой 'фортунки', можно определить, достаточно ли места для ее размещения. Если места недостаточно, модуль возвратит пользовательскому процессу код -ENOSPC . В противном случае строка копируется в cookie_pot с помощью функции copy_from_user . После этого происходит увеличение значения переменной cookie_index (на величину, зависящую от длины полученной строки), в конец строки дописывается NULL. Алгоритм завершает свою работу тем, что возвращает пользовательскому процессу количество символов фактически записанных в cookie_pot .Листинг 10. Функция записи 'фортунки'
Чтение 'фортунки' нисколько не сложнее ее записи. Поскольку буфер, в который нужно произвести запись 'фортунки' ( page ), уже находится в пользовательском пространстве, для вывода фортунки можно использовать непосредственно функцию sprintf . Если значение индекса next_fortune превышает значение cookie_index (индекс следующего свободного для записи элемента), переменной next_fortune присваивается 0, то есть, индекс первого элемента. После того, как фортунка записана в буфер пользовательского процесса, я увеличиваю индекс next_fortune на ее длину. Теперь этот индекс содержит адрес 'фортунки', которая будет выдана следующей . Длина 'фортунки' также передается пользовательскому процессу в качестве возвращаемого значения.Листинг 11. Функция чтения 'фортунки'
Это простой пример показывает что обмен данными между ядром и пользовательским процессом является тривиальной задачей. А сейчас предлагаю посмотреть на модуль выдачи 'фортунок' в действии (листинг 12). Листинг 12. Загружаемый модуль выдачи 'фортунок' в действии
Виртуальная файловая система /proc широко используется как средство сбора информации о состоянии ядра и для его динамического конфигурирования. Она незаменима для разработки драйверов и модулей ядра, в чем несложно убедиться. Размещение нескольких сайтов с поддержкой SSL на одной сетевой карте с помощью IP-алиасинг (документация). Различия между UNIX и Linux. "Железный" бизнес Oracle.. Oracle представила "облачную" систему Exalogic Elastic Cloud. Oracle оптимизирует системы Sun Blade для облачных и виртуализованных сред. Главная » Linux |
© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено. |