Библиотека сайта 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? Кто такой Анил? Ты бы мог воспользоваться моим именем или, возможно, своим" — предложила Светлана. "Ха! Вот сюрприз. Мое настоящее имя - Анил; просто каждый в колледже знает меня под именем Пагс" - улыбнулся Пагс.
| К предыдущей статье | Оглавление | К следующей статье |
