Билет 14 - honeycarbs/bmstu-os-6sem GitHub Wiki

Файловая подсистема /proc – назначение, особенности, файлы, поддиректории, ссылка self, информация об окружении, состоянии процесса, прерываниях. Структура proc_dir_entry: функции для работы с элементами /proc. Структура, перечисляющая функции, определенные на файлах. Использование структуры file_operations для регистрации собственных функций работы с файлами. Передача данных из пространства пользователя в пространство ядра и из ядра в пространство пользователя. Обоснование необходимости этих функций. Функция printk() – назначение и особенности. Пример программы «Фортунки» из лаб. работы.

Назначение. /proc (process information pseudo-file-system) - интерфейс, предоставляющий доступ к структурам ядра. Реализован в виде файловой системы. Информацию мы получаем аналогично обращениям к файлам с помощью стандартных обращений к функциям работыс файлами. Она была создана для того, чтобы разработчики в режиме пользователя могли получать ифнормацию о процессах и ресурсах, которые используют эти процессы. Эта информация доступна из структур ядра, в ЛР3 был написан код для получения информации о процессах из структур ядра, мы обращались к struct task_struct. А proc предоставляет информацию в usermode. Соотвественно, для получения этой информации не надо писать LKM.

Особенности В более старых источниках написано, что файловая система proc является немонтируемой. Под этим подразумевается, что она не связана ни с каким внешним носителем (она видна в файловых системах, подключенных к корневому каталогу, но не связана ни с каким внешним устройством. Поэтому она и называется псевдо-файловой системой).

OnPaste 20220613-112049

Однако для того чтобы ФС была доступна, она должна быть подмонтирована, в результате должна быть доступна информация из суперблока, который является основной структурой, описывающей файловую систему. Когда происходит обращение к ФС proc, информация к которой идет обращение создается на лету, то есть файловая система виртуальная.

Файловая система монтируется при загрузке системы. Но ее также можно смонтировать вручную:

mount -t proc proc/porc

Это сделано для общности - система работает единообразно со всеми файловыми системами.

Файлы, поддиректории, ссылка self

В виртуальной файловой системе proc для каждого процесса существует директория идентификатора процесса /proc/[pid]. Для данной поддиректории также для каждого процесса существует /proc/self, для того, чтобы не вызывать getpid(). В директории /proc/self находятся директории и файлы, несущие информацию об окружении процесса, открытых файлах процессов и т.д.

элемент тип содержание
cmdline файл readonly, содержит полную командную строку для создания
вызовов open, mkdir, symlink, mknode.
cwd символическая ссылка Указывает на директорию процесса
environ файл Информация об окружении процесса
exe символическая ссылка Указывает на образ процесса (на его файл)
fd директория Ссылки на файлы, которые «открыл» процесс
root символическая ссылка Указывает на корень файловой системы процесса
stat файл Информация о процессе

/proc/[pid]/environ — информация об окружении процесса.

Данный файл содержит исходное окружение, которое было установлено при запуске текущего процесса (вызове execve()). Переменные окружения разделены символами конца строки ('\0'). Если после вызова execve() окружение процесса будет модифицировано (например, вызовом функции putenv() или модификацией переменной окружения напрямую), этот файл не отразит внесенных изменений.

/proc/[pid]/stat — информация о состоянии процесса.

Данный файл содержит информацию о состоянии процесса (pid, comm, state, ppid, pgrp, session, tty_nr, tpgid, flags и др.)

/proc/interrupts — информация о об источниках прерываний и о частоте возникновения этих прерываний.

Файл /proc/interrupts предоставляет таблицу о количестве прерываний на каждом из процессоров в следующем виде:

  • Первая колонка: номер прерывания
  • Колонки CPUx: счётчики прерываний на каждом из процессоров
  • Следующая колонка: вид прерывания:
    • IO-APIC-edge — прерывание по фронту на контроллер I/O APIC
    • IO-APIC-fasteoi — прерывание по уровню на контроллер I/O APIC
    • PCI-MSI-edge — MSI прерывание
    • XT-PIC-XT-PIC — прерывание на PIC контроллер
  • Последняя колонка: устройство, ассоциированное с данным прерыванием

Структура proc_dir_entry

Файлы и поддиректории файловой системы /proc используют структуру proc_dir_entry:

struct proc_dir_entry {
	const char *name; // имя виртуального файла
	mode_t mode; // режим доступа
	uid_t uid; // уникальный номер пользователя — владельца файла
	uid_t id; // уникальный номер группы, которой принадлежит файл
	struct inode_operations *proc_iops; // функции-обработчики операций с inode
	struct file_operations *proc_fops; // функции-обработчики операций с файлом
	struct proc_dir_entry *next, *parent, *subdir;
	...
	read_proc_t *read_proc; // функция чтения из /proc
	write_proc_t *write_proc; // функция записи в /proc
	void *data; // Указатель на локальные данные
	atomic_t count; // счетчик ссылок на файл
	...
};

Функции для работы с элементами /proc

Начиная с ядра 3.10 больше не поддерживается функция create_proc_entry(). Вместо нее используются функции:

#include <linux/types.h>
#include <linux/fs.h>

extern struct proc_dir_entry *proc_create_data(const char *, umode_t, struct proc_dir_entry *, const struct file_operations *, void *);
struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops);

Создавать каталоги в файловой системе /proc можно используя proc_mkdir(), а также символические ссылки с proc_symlink(). Для простых элементов /proc, для которых требуется только функция чтения, используется create_proc_read_entry(), которая создает запись /proc и инициализирует функцию read_proc в одном вызове.

extern struct proc_dir_entry *proc_symlink(const char *, struct proc_dir_entry *, const char *);
extern struct proc_dir_entry *proc_mkdir(const char *, struct proc_dir_entry *);
struct proc_dir_entry *create_proc_read_entry( const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data );

Чтобы передать данные из адресного пространства пользователя в адресное пространство ядра и обратно используются функции copy_from_user() и copy_to_user()

unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n);

Копирует данные из ядра в пространство пользователя.

unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n);

Копирует данные из пространства пользователя в пространство ядра. Если некоторые данные не могут быть скопированы, эта функция добавит нулевые байты к скопированным данным до требуемого размера.

Обе функции возвращают количество байт, которые не могут быть скопированы. В случае успешного выполнения будет возвращен 0.

Обоснование необходимости этих функций.

Функция printk() - назначение и особенности.

Функция printk определена в ядре Linux и доступна модулям. Функция ведёт себя аналогично библиотечной функции printf. Став частью ядра модуль не может вызывать обычные библиотечные функции, поэтому ядро предоставляет модулю функцию printk. Функция отправляет сообщения в системный журнал. Пишет сообщения в системный буфер, не в журнал.

Система поддерживает 8 уровней протоколирования, например KERN_INFO или KERN_ERR. Каждая строка (макроподстановка) представляет собой целое число в угловых скобках. Чем больше величина, тем больше приоритет.

Пример программы «Фортунки» из лаб. работы.

#include <asm/uaccess.h>
#include <linux/init.h>  // ​Макросы __init и ​__exit
#include <linux/init_task.h>
#include <linux/module.h>   // MODULE_LICENSE, MODULE_AUTHOR
#include <linux/proc_fs.h>  // proc_create
#include <linux/string.h>
#include <linux/version.h>  // KERNEL_VERSION
#include <linux/vmalloc.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tatiana K");

#define _BUF_SIZE PAGE_SIZE

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
#define HAVE_PROC_OPS
#endif

static char *_cookie_pot; // буфер
static unsigned read_inx;
static unsigned write_inx;
static struct proc_dir_entry *proc_entry, *proc_dir, *sym_link;
char tmp[256];

int fortune_open(struct inode *sp_inode, struct file *sp_file);
int fortune_release(struct inode *sp_node, struct file *sp_file);
ssize_t fortune_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos);
static ssize_t fortune_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos);

#ifdef HAVE_PROC_OPS
static struct proc_ops fileops = {
    .proc_read = fortune_read,
    .proc_write = fortune_write,
    .proc_open = fortune_open,
    .proc_release = fortune_release,
};
#else
static struct file_operations fileops = {
    .owner = THIS_MODULE,
    .read = fortune_read,
    .write = fortune_write,
    .open = fortune_open,
    .release = fortune_release,
};
#endif

int fortune_open(struct inode *sp_inode, struct file *sp_file) {
  return 0;
}

int fortune_release(struct inode *sp_node, struct file *sp_file) {
    return 0;
}

ssize_t fortune_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) {
  int space_left = (_BUF_SIZE - write_inx) + 1;

  printk(KERN_INFO "== fortine write is called");

  if (space_left < count) {
    printk(KERN_INFO "== ERROR: buffer is full. Terminating...");
    return -ENOSPC;
    }
    if (copy_from_user(&_cookie_pot[write_inx], buf, count)) {
      return -EFAULT; // no space left on device
    }

    write_inx += count;
    _cookie_pot[write_inx - 1] = '\0';

    printk(KERN_INFO "== writing successful");
    return count;
}

static ssize_t fortune_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) {
    int len;
    if (*ppos > 0) {
        return 0;
    }
    if (read_inx >= write_inx) {
        read_inx = 0;
    }
    len = 0;

    if (write_inx > 0) {
        len = sprintf(tmp, "%s\n", &_cookie_pot[read_inx]);

        copy_to_user(buf, tmp, len);
        buf += len;
        read_inx += len;
    }

    *ppos += len;

    return len;
}

static int __init fortune_init(void) {
    _cookie_pot = (char *)vmalloc(_BUF_SIZE);
    if (!_cookie_pot) {
        return -ENOMEM;
    }
    memset(_cookie_pot, 0, _BUF_SIZE);

    proc_entry = proc_create("fortune", S_IRUGO | S_IWUGO, NULL, &fileops);
    if (!proc_entry) {
        vfree(_cookie_pot);
        printk(KERN_INFO "== cant proc_create\n");
        return -ENOMEM;
    }

    proc_dir = proc_mkdir("fortune_dir", NULL);
    sym_link = proc_symlink("fortune_symlink", NULL, "/proc/fortune_dir");

    if ((proc_dir == NULL) || (sym_link == NULL)) {
        vfree(_cookie_pot);
        printk(KERN_INFO "== cant proc_create\n");
        return -ENOMEM;
    }

    read_inx = 0;
    write_inx = 0;

    printk(KERN_INFO "== module loaded.");
    return 0;
}

static void __exit fortune_exit(void) {
  remove_proc_entry("fortune_seq", NULL);
  remove_proc_entry("fortune_symlink", NULL);
  remove_proc_entry("fortune_dir", NULL);

  if (_cookie_pot) {
    vfree(_cookie_pot);
    }

    printk(KERN_INFO "== module unloaded.\n");
}

module_init(fortune_init);
module_exit(fortune_exit);
⚠️ **GitHub.com Fallback** ⚠️