Наши партнеры

UnixForum



  • Фестул
  • Фестул официальный сайт! Официальный офис продаж! Кредит! Гарантия! Жмите
  • ftrussia.ru


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


К предыдущей статье Оглавление К следующей статье