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

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

Файловая подсистема /proc

Назначение.
/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 );

### Использование структуры file_operations для регистрации функций работы с файлами
С некоторой версии ядра file_operations не поддерживается. Пример из лаб. раб. регистрации функций работы с файлами:
```C
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
#define HAVE_PROC_OPS
#endif

#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

Передача данных из адресного пространства пользователя в адресное пространство ядра и обратно (лаб. раб.)

// const char __user *buf - адрес источника в пространстве пользователя
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;

    printk(KERN_INFO "== reading successful");
    return len;
}

БУФЕР КОЛЬЦЕВОЙ, НЕ ЦИКЛИЧЕСКИЙ

Обоснование необходимости использования специальных функций для передачи данных из пространства пользователя в ядро и из ядра в пространство пользователя.

Из U в K: напрямую приложение обратиться к буферу ядра не может. Т.е. та последовательность действий по read/write, которая реализована при обращении к внешним устройствам (файлам, клавиатуре, мыши) здесь не работает, поскольку идет обращение к ядру. Ядро защищено.
Из K в U:
Когда ядро обращается к приложениям, там обратная ситуация, поскольку выполняется код ядра, может возникнуть ситуация, когда буфер, к которому обращается код ядра, находится на странице, которая выгружена по каким-то причинам.

⚠️ **GitHub.com Fallback** ⚠️