Библиотека сайта rus-linux.net
Драйверы устройств в Linux
Часть 16: О директории /proc
Оригинал: "Kernel Window — Peeping through /proc"Автор: Anil Kumar Pugalia
Дата публикации: March 26, 2012
Перевод: Н.Ромоданов
Дата перевода: июнь 2012 г.
В этой статье, которая является частью серии статей о драйверах устройств в Linux, демонстрируется создание и использование файлов в директории виртуальной файловой системы /proc.
Светлана и Пагс неоднократно встречались и вместе достаточно просто разбирались с некоторыми техническими вопросами. Теперь они могли уже беспрепятственно получать всю системную информацию, в частности ту, что можно (с помощью команды cat
) получать из директории виртуальной файловой системы /proc
, и с ее помощью разбираться в различных особенностях драйверов устройств Linux.
Ниже приводится краткий список поддиректорий директории /proc
:
/proc/modules
— динамически загружаемые модули/proc/devices
— зарегистрированные старшие номера символьных и блочных устройств/proc/iomem
— адреса системной оперативной памяти и адреса шин/proc/ioports
— адреса системных портов ввода/вывода (главным образом, для систем x86)/proc/interrupts
— зарегистрированные номера запросов прерываний/proc/softirqs
— зарегистрированные номера программных прерываний IRQ/proc/kallsyms
— работающие модули ядра, в том числе загруженные/proc/partitions
— подключенные в настоящий момент блочные устройства и их разделы/proc/filesystems
— драйвера активных в настоящий момент файловых систем/proc/swaps
— активная в настоящий момент область свопинга/proc/cpuinfo
— информация о процессорах, имеющихся в системе/proc/meminfo
— информация о памяти, используемой в системе...
Специальные поддиректории ядра
"Да, они действительно были полезными для понимания и отладки драйверов устройств Linux. Но можем ли мы рассчитывать на такую помощь? Да, я имею в виду, можем ли мы прямо в /proc
создать один такой директорий с данными, получаемыми из ядра?" - спросила Светлана.
"Почему только один? Ты можешь создать таких директорий столько, сколько ты хочешь. И это просто — воспользуйся правильным набором API и - вперед".
"Это для тебя все просто" — проворчала Светлана.
"Нет, ааа, это достаточно сложно" - улыбнулся Пагс. "Просто пронаблюдай за тем, как я создам для тебя один из таких директориев" - добавил он.
И Пагс быстро написал следующий файл proc_window.c
:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/jiffies.h> static struct proc_dir_entry *parent, *file, *link; static int state = 0; int time_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len, val; unsigned long act_jiffies; len = sprintf(page, "state = %d\n", state); act_jiffies = jiffies - INITIAL_JIFFIES; val = jiffies_to_msecs(act_jiffies); switch (state) { case 0: len += sprintf(page + len, "time = %ld jiffies\n", act_jiffies); break; case 1: len += sprintf(page + len, "time = %d msecs\n", val); break; case 2: len += sprintf(page + len, "time = %ds %dms\n", val / 1000, val % 1000); break; case 3: val /= 1000; len += sprintf(page + len, "time = %02d:%02d:%02d\n", val / 3600, (val / 60) % 60, val % 60); break; default: len += sprintf(page + len, "<not implemented>\n"); break; } len += sprintf(page + len, "{offset = %ld; count = %d;}\n", off, count); return len; } int time_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { if (count > 2) return count; if ((count == 2) && (buffer[1] != '\n')) return count; if ((buffer[0] < '0') || ('9' < buffer[0])) return count; state = buffer[0] - '0'; return count; } static int __init proc_win_init(void) { if ((parent = proc_mkdir("anil", NULL)) == NULL) { return -1; } if ((file = create_proc_entry("rel_time", 0666, parent)) == NULL) { remove_proc_entry("anil", NULL); return -1; } file->read_proc = time_read; file->write_proc = time_write; if ((link = proc_symlink("rel_time_l", parent, "rel_time")) == NULL) { remove_proc_entry("rel_time", parent); remove_proc_entry("anil", NULL); return -1; } link->uid = 0; link->gid = 100; return 0; } static void __exit proc_win_exit(void) { remove_proc_entry("rel_time_l", parent); remove_proc_entry("rel_time", parent); remove_proc_entry("anil", NULL); } module_init(proc_win_init); module_exit(proc_win_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>"); MODULE_DESCRIPTION("Kernel window /proc Demonstration Driver");
А потом Пагс сделал следующее:
- С помощью обычного для драйверов файла
Makefile
собрал файл драйвера (proc_window.ko
) - Загрузил драйвер с помощью команды
insmod
. - Провел различные эксперименты и показал информацию из только что созданной директории proc
- И, наконец, выгрузил драйвер с помощью команды
rmmod
.
Рис.1: Смотрим, что есть в директории /proc
Демистификация подробностей
Начнем с конструктора proc_win_init()
- в нем создаются три записи proc:
- С помощью команды
proc_mkdir()
создается поддиректорияanil
внутри директории/proc
(т.е., его родитель - NULL parent) с правами доступа 0755, используемыми по умолчанию - С помощью команды
create_proc_entry()
в указанной выше поддиректории создается обычный файлrel_time
с правами доступа 0666. - С помощью команды
proc_symlink()
в той же самой поддиректории создается программная ссылка на файлrel_time
Удаление этих элементов соответственно выполняется в обратном хронологическом порядке в функции remove_proc_entry()
деструктора.
Для каждой записи, созданной в /proc
, формируется соответствующая запись struct proc_dir_entry
. В дальнейшем можно будет по мере необходимости обновлять поля этих записей:
- mode — права на доступ к файлу
- uid — идентификатор ID пользователя — владельца файла
- gid — идентификатор ID группы пользователей, к которой принадлежит этот файл
Кроме того, для обычного файла могут быть предложены следующие два указателя на функции чтения из файла и записи в файл, соответственно:
int (*read_proc)(char *page, char **start, off_t off, int count, int *eof, void *data)
int (*write_proc)(struct file *file, const char __user *buffer, unsigned long count, void *data)
Операция write_proc()
очень похожа на операцию чтения файла
драйвера символьного устройства write()
. Приводимая выше
реализация позволяет пользователю записывать цифры с 0 по 9, и,
соответственно, задавать внутреннее состояние. Операция
read_proc()
в приводимой выше реализации позволяет считывать
текущее состояние и время с момента загрузки системы — в единицах
времени, зависящих от текущего состояния. В состоянии 0 — это тики
(системные отсчеты времени), миллисекунды в состоянии 1, секунды и
миллисекунды в состоянии 2, часы, минуты и секунды в состоянии 3 и <not
implemented> (<не применяется>) в других состояниях.
А чтобы проверить точность вычислений, на рис.2 показано время с момента загрузки, которое выдает команда top. Страничный параметр read_proc
является буфером, имеющим размер страницы и, как правило, заполняемым с указанием смещения в байтах. Но чаще (из-за малого объема содержимого) просто происходит вывод данных на страницу с игнорированием всех других параметров.
Рис.2: Сравнение с результатом работы команды top
Все определения структур и объявления функций, касающихся директории
/proc
, есть в заголовочном файле <linux/proc_fs.h>
. Объявления
функций и макроопределения, используемые при подсчете тиков, находятся
в заголовочном файле <linux/jiffies.h>
. Специально отметим, чо действительное количество тиков вычисляется путем вычитания значения INITIAL_JIFFIES
, поскольку при загрузке в INITIAL_JIFFIES
заносится не нуль, а число тиков, которое было на момент загрузки.
Подведем итог
"Эй, Пагс! Зачем ты выбрал в качестве имени директории имя anil
? Кто такой Анил? Ты бы мог воспользоваться моим именем или, возможно, своим" — предложила Светлана. "Ха! Вот сюрприз. Мое настоящее имя - Анил; просто каждый в колледже знает меня под именем Пагс" - улыбнулся Пагс.
К предыдущей статье | Оглавление | К следующей статье |