Билет 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 является немонтируемой. Под этим подразумевается, что она не связана ни с каким внешним носителем (она видна в файловых системах, подключенных к корневому каталогу, но не связана ни с каким внешним устройством. Поэтому она и называется псевдо-файловой системой).

Однако для того чтобы ФС была доступна, она должна быть подмонтирована, в результате должна быть доступна информация из суперблока, который является основной структурой, описывающей файловую систему. Когда происходит обращение к ФС proc, информация к которой идет обращение создается на лету, то есть файловая система виртуальная.
Файловая система монтируется при загрузке системы. Но ее также можно смонтировать вручную:
mount -t proc proc/porcЭто сделано для общности - система работает единообразно со всеми файловыми системами.
В виртуальной файловой системе 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 используют структуру 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; // счетчик ссылок на файл
...
};Начиная с ядра 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 определена в ядре 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);