Видео смотреть бесплатно

Смотреть красивое видео

Официальный сайт 24/7/365

Смотреть видео бесплатно

dementiy 06.12.2010 02:08

CodingФайловая система proc

1. В двух словах о procfs

«procfs – виртуальная файловая система, используемая в UNIX-like операционных системах. procfs позволяет получить доступ к информации о системных процессах из ядра, она необходима для выполнения таких команд как ps, w, top...» - Wikipedia

procfs является псевдофайловой системой, которая хранит и собирает информацию о системе и о процессах в частности. Например, информация о процессоре (процессорах) содержится в файле /proc/cpuinfo и получить ее можно с помощью команды cat:

 1
2
3
4
5
6
7
8
9
10
$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 15
model name : Intel(R) Core(TM)2 Duo CPU T7100 @ 1.80GHz
stepping : 13
cpu MHz : 800.000
cache size : 2048 KB
...


В том числе procfs позволяет менять поведение ядра без необходимости заново его собирать, загружать модули или перезагружать систему.
Более подробно о назначении файлов и каталогов procfs и о том, какую информацию они предоставляют, вы можете узнать на http://welinux.ru/post/4487. Также есть замечательное руководство "Red Hat Enterprise Linux 6. Deployment Guide" (ссылка на pdf), где содержимому /proc (Раздел 19) уделяется большое внимание.

2. Структуры VFS

Прежде чем перейти к файловой системе proc, вкратце рассмотрим основные структуры (объекты) VFS.
Virtual File System или Virtual Filesystem Switch (VFS) представляет собой "слой" между приложениями пространства пользователя и реализацией файловых систем, другими словами, VFS предоставляет единый интерфейс для доступа к различным ФС:


Рис. 2.1. Взаимодействие с VFS

Это означает, что мы имеем возможность использовать одни и те же системные вызовы для разных файловых систем.
Для VFS есть несколько основных объектов, которые можно условно разделить на два типа: объекты, описывающие файловую систему, и объекты, описывающие элементы файловой системы.
Первый тип объектов:
Структура "file_system_type" представляет зарегистрированную ФС:

 1
2
3
4
5
6
7
8
9
10
11
12
include/linux/fs.h:
struct file_system_type {
const char *name;
int fs_flags;
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct list_head fs_supers;
...
};


name – имя файловой системы;
fs_flags – флаги ФС. Например, флаг FS_REQUIRES_DEV указывает на то, что ФС не является "псевдо" (в файле /proc/filesystems указывается "nodev", если данный флаг не был выставлен). Все флаги определены в файле include/linux/fs.h;
get_sb – указатель на функцию выделения суперблока;
kill_sb – указатель на функцию освобождения суперблока;
owner – если ФС была собрана как загружаемый модуль - указатель на модуль, отвечающий за данную ФС. В противном случае, если ФС встроена в ядро, указывается NULL;
next – указатель на следующую файловую систему. Все зарегистрированные ФС образуют список, на вершину которого указывает глобальная переменная "file_systems" (см. рис. 2.2, полный список зарегистрированных ФС можно получить в файле /proc/filesystems);
fs_supers – список суперблоков для файловых систем данного типа.
Рис. 2.2. Список зарегистрированных файловых систем
Структура "vfsmount" представляет точку монтирования:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
include/linux/mount.h:
struct vfsmount {
struct list_head mnt_hash;
struct vfsmount *mnt_parent; /* fs we are mounted on */
struct dentry *mnt_mountpoint; /* dentry of mountpoint */
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
...
int mnt_flags;
const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
struct list_head mnt_list;
...
int mnt_id; /* mount identifier */
...
};


mnt_parent — родительская точка монтирования;
mnt_mountpoint — dentry (имя) для точки монтирования;
mnt_root — dentry (имя) для корневой точки монтирования (обычно «/»);
mnt_sb — указатель на связанный суперблок;
mnt_flags — флаги, с которыми монтируется ФС. Например, флаг MS_RDONLY указывает, что ФС будет смонтирована только для чтения. Все флаги определены в файле include/linux/fs.h;
mnt_devname — имя устройства;
mnt_list — список всех точек монтирования (mounts). Данный список можно просмотреть в файле /proc/mounts;
mnt_id — идентификатор точки монтирования.
Второй тип объектов:
Объект суперблок (superblock) — это структура данных, которая хранит информацию о ФС в целом (типе ФС, размере, состоянии и т.д.) и выделяется при монтировании для каждой ФС:

 1
2
3
4
5
6
7
8
9
10
include/linux/fs.h:
struct super_block {
struct list_head s_list; /* Keep this first */
...
struct file_system_type *s_type;
const struct super_operations *s_op;
...
struct dentry *s_root;
...
};


s_list — список суперблоков;
s_type — тип файловой системы, к которой принадлежит суперблок;
s_root — dentry корневой точки монтирования;
s_op — указатель на таблицу операций, связанных с суперблоком:

 1
2
3
4
5
6
7
8
9
10
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
...
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
...
int (*statfs) (struct dentry *, struct kstatfs *);
...
};


alloc_inode — выделяет память под inode;
destroy_inode — освобождает память, выделенную под inode;
drop_inode — вызывается, когда счетчик ссылок на inode становится равным нулю;
delete_inode — удаляет inode;
statfs — получение статистики ФС.
Объект файл (file) — представляет открытый файл (создается при открытии файла), связанный с процессом:

 1
2
3
4
5
6
7
8
9
10
11
12
include/linux/fs.h:
struct file {
union {
struct list_head fu_list;
...
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
...
};


fu_list — список открытых файлов;
f_path.dentry — указатель на соответствующую компоненту пути;
f_path.mnt — указатель на точку монтирования;
f_op — указатель на таблицу файловых операций. Файловый операции проводятся над содержимым файла. Названия большинства файловых операций совпадают с названиями соответствующих системных вызовов:

 1
2
3
4
5
6
7
8
9
10
11
12
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
...
int (*readdir) (struct file *, void *, filldir_t);
...
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
...
};


Объект элемент каталога (dentry) — представляет определенный элемент каталога (компонент пути). Основной целью использования dentry является установление связи между именем файла (каталога, FIFO и т.д. - «everything is file») и соответствующим inode:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
include/linux/dcache.h:
struct dentry {
...
struct inode *d_inode; /* Where the name belongs to - NULL is negative */
...
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
...
union {
struct list_head d_child; /* child of parent list */
...
} d_u;
struct list_head d_subdirs; /* our children */
...
const struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
...
unsigned char d_iname<dname_inline_len_min>; /* small names */
};</dname_inline_len_min>


d_inode — указатель на связанный inode;
d_parent — указатель на dentry родителя;
d_name — структура qstr содержит имя файла, его длину и хэш-сумму. Хранится не абсолютное имя, а только последняя его часть. Например, если абсолютное имя «/home/user/staff/example», то поле name структуры qstr содержит только «example», т.к. остальные компоненты пути можно получить из родительского dentry (см. рис. 2.3);
d_child — список подкаталогов dentry родителя;
d_subdirs — список подкаталогов данного dentry;
d_sb — указатель на связанный суперблок;
d_iname — содержит короткое имя файла;
d_op — указатель на таблицу dentry операций (большинство файловых систем не реализуют эти функции, а используют реализацию, предоставляемую VFS):

 1
2
3
4
5
6
7
8
9
10
include/linux/dcache.h:
struct dentry_operations {
int (*d_revalidate)(struct dentry *, struct nameidata *);
int (*d_hash) (struct dentry *, struct qstr *);
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
int (*d_delete)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
};


Рис. 2.3. Абсолютное имя «example»

На рис. 2.1. вы можете видеть directory entry cache (dentry cache), который включается в VFS. Данный кэш предназначен для увеличения скорости поиска inode, связанного с именем файла (имя файла передается в качестве аргумента таким системным вызовам, как open(), stat(), chmod() и т.д.).
Объект файловый индекс (inode) — представляет определенный файл (обычные файлы, директории, FIFO и т.д.):

 1
2
3
4
5
6
7
8
9
10
11
12
include/linux/fs.h:
struct inode {
...
unsigned long i_ino;
atomic_t i_count;
umode_t i_mode
...
const struct inode_operations *i_op;
const struct file_operations *i_fop; /*former ->i_op->default_file_ops */
struct super_block *i_sb;
...
};


i_ino – номер индекса;
i_count – счетчик ссылок;
i_mode – права доступа;
i_sb – указатель на суперблок;
i_fop – указатель на таблицу файловых операций. Когда открывается существующий файл, и для него создается структура "struct file", то файловые операции берутся из этого поля;
i_op – указатель на таблицу inode операций. Inode операции производятся над структурами и метаданными, связанными с файлом. Также, как и с файловыми операциями, названия функций совпадают с названиями соответствующих системных вызовов:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
include/linux/fs.h:
struct inode_operations {
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
struct dentry * (*lookup) (struct inode *,struct dentry *,
struct nameidata *);
int (*link) (struct dentry *,struct inode *,struct dentry *);
...
int (*mkdir) (struct inode *,struct dentry *,int);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,int,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
...
};


Взаимосвязь между описанными объектами показана на рисунке 2.4.


Рис.2.4. Взаимосвязь между объектами VFS

Подведем некоторый итог и обратимся к рисунку 2.1. Справа показаны «приставки» и «окончания». Что они означают? Рассмотрим пример чтения из файла и предположим, что у нас есть следующий код:

1
2
3
...
err = read(fd, buf, count);
...


Функция read() является библиотечной функцией и принимает три аргумента: дескриптор файла (fd), из которого будет производиться чтение, указатель на буфер (buf), куда будут сохранены данные и сколько информации должно быть прочитано (count). В свою очередь read() является оберткой для системного вызова sys_read():

 1
2
3
4
5
6
7
8
9
10
11
12
13
fs/read_write.c:
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
...
file = fget_light(fd, &fput;_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos;);
...
}
return ret;
}


В системном вызове sys_read() определяется объект file, связанный с файловым дескриптором fd (с помощью функции fget_light()), определяется текущая позиция в файле pos (с помощью функции file_pos_read()) и затем вызывается функция vfs_read():

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
fs/read_write.c:
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
...
if (ret &gt;= 0) {
count = ret;
if (file-&gt;f_op-&gt;read)
ret = file-&gt;f_op-&gt;read(file, buf, count, pos);
else
ret = do_sync_read(file, buf, count, pos);
...
}
return ret;
}


Здесь возможны два варианта: если в таблице файловых операций определен соответствующий метод, в данном случае чтение из файла, то чтение производится средствами файловой системы, к которой принадлежит этот файл, в противном случае будет вызвана стандартная функция VFS.

Дополнительную информацию о VFS можно получить в документации к ядру Documentation/fs/vfs.txt, а также по следующим ссылкам:
http://www.ibm.com/developerworks/ru/library/l-linux-filesystem/index.html;
http://www.ibm.com/developerworks/library/l-virtual-filesystem-switch/;
http://www.opennet.ru/base/sys/linux_vfs.txt.html;
http://tldp.org/LDP/khg/HyperNews/get/fs/vfstour.html.

3. Инициализация procfs и ее структуры

Инициализация файловой системы proc происходит еще на стадии загрузки ядра (про загрузку ядра можно прочитать на http://welinux.ru/post/4453). В функции start_kernel(), при условии что ядро было собрано с опцией «CONFIG_PROC_FS=y» (данная опция включена по умолчанию), вызывается proc_init_root():

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
fs/proc/root.c:
void __init proc_root_init(void)
{
int err;

proc_init_inodecache();
err = register_filesystem(&proc;_fs_type);
if (err)
return;
proc_mnt = kern_mount_data(&proc;_fs_type, &init;_pid_ns);
if (IS_ERR(proc_mnt)) {
unregister_filesystem(&proc;_fs_type);
return;
}

proc_symlink("mounts", NULL, "self/mounts");

proc_net_init();

#ifdef CONFIG_SYSVIPC
proc_mkdir("sysvipc", NULL);
#endif
proc_mkdir("fs", NULL);
proc_mkdir("driver", NULL);
proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
/* just give it a mountpoint */
proc_mkdir("openprom", NULL);
#endif
proc_tty_init();
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init();
#endif
proc_mkdir("bus", NULL);
proc_sys_init();
}


proc_init_inodecache() — создается новый кэш "proc_inode_cache". Кэш представляет собой один или несколько слябов (slabs) и используется ядром Linux для быстрого выделения и освобождения структур ядра, связанных с процессами, файлами и сетевым стеком;
register_filesystem() — регистрируется новая файловая система proc. Регистрация файловой системы фактически является простым добавлением ее в список. Структура "file_system_type" описывает ФС и ее свойства и для procfs выглядит довольно просто:

1
2
3
4
5
6
fs/proc/root.c:
static struct file_system_type proc_fs_type = {
.name = "proc", /* имя файловой системы */
.get_sb = proc_get_sb, /* указатель на функцию выделения суперблока */
.kill_sb = proc_kill_sb, /* указатель на функцию освобождения суперблока */
};


kern_mount_data() — выделяется суперблок и монтируется в «/proc» (в отличие от «настоящих» ФС, у которых суперблок хранится на диске, у procfs он хранится в памяти). Суперблок выделяется в функции «proc_get_super()», которая, в свою очередь, вызывает функцию «proc_fill_super()» для заполнения основных полей суперблока;
proc_mkdir()/proc_symlink()/proc_xxx_init() — создание записей (каталоги, файлы, символические ссылки) в «/proc».

Остальные записи в «/proc» создаются другими компонентами ядра.
Каждая запись описывается структурой "proc_dir_entry":

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
include/linux/proc_fs.h:
struct proc_dir_entry {
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
const struct file_operations *proc_fops;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
...
};


low_ino — идентификатор записи (по аналогии с inode);
name – указатель на строку, в которой хранится имя файла;
namelen – содержит длину имени файла;
mode – содержит права доступа к файлу (стандартные владелец/группа/ остальные);
nlink – количество подкаталогов и символических ссылок в данном каталоге;
uid/gid – определяют идентификатор пользователя и идентификатор группы, соответственно;
size – размер файла в байтах;
proc_iops/proc_fops – указатели на структуры inode_operations и file_operations, они содержат операции, которые могут проводиться над inode и файлами, соответственно;
next/parent/subdir — указатели на следующую запись, родителя и подкаталог, соответственно;
data — используется для хранения дополнительных данных (например, для хранения целевого пути (target path) в случае ссылки);
read_proc/write_proc – указатели на функции для чтения/записи данных из/в пространства ядра;
count – счетчик использования.
За счет элементов nlink, next, parent и subdir структура файловой системы получает иерархическое представление (см. рис. 3.1). Корневая запись (root entry) представлена отдельной статической структурой «proc_dir_entry» и называется «proc_root»:

 1
2
3
4
5
6
7
8
9
10
11
12
fs/proc/root.c:
struct proc_dir_entry proc_root = {
.low_ino = PROC_ROOT_INO,
.namelen = 5,
.name = "/proc",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
.nlink = 2,
.count = ATOMIC_INIT(1),
.proc_iops = &proc;_root_inode_operations,
.proc_fops = &proc;_root_operations,
.parent = &proc;_root,
};



Рис. 3.1. Иерархическое представление структуры ФС экземплярами pde

Также ФС proc имеет и inode-ориентированное представление записей, за счет структуры proc_inode:

1
2
3
4
5
6
7
8
9
include/linux/proc_fs.h:
struct proc_inode {
struct pid *pid;
int fd;
union proc_op op;
struct proc_dir_entry *pde;
...
struct inode vfs_inode;
};


pid — указатель на структуру pid, связанную с процессом (struct pid является внутренним представлением ядра о идентификаторе процесса);
fd — файловый дескриптор, для которого представлена информация в файле /proc//fd;
op — в объединении находятся операции proc_get_link, proc_read и proc_show для получения process-specific информации;
pde — указатель на соответствующую структуру «proc_dir_entry»;
vfs_inode — inode, используемый уровнем VFS. Таким образом, перед каждым inode, связанным с ФС proc, есть дополнительные данные в памяти.

4. Поиск записей

Поиск записей начинается с корневой записи proc_root:

 1
2
3
4
5
6
7
8
9
10
fs/proc/root.c:
static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry,
struct nameidata *nd)
{
if (!proc_lookup(dir, dentry, nd)) {
return NULL;
}

return proc_pid_lookup(dir, dentry, nd);
}


Тут возможны два варианта: поиск обычных записей (например /proc/cpuinfo, /proc/fs/ext4/sda/mb_groups и т.д.) и поиск process-specific записей (например /proc/1/cmdline). Так как заранее не известно какого типа ищется запись, то вначале производится поиск обычных записей в функции proc_lookup(), и, если запись не была найдена, производится поиск process-specific записей функцией proc_pid_lookup().
Рассмотри второй вариант — поиск process-specific записи:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fs/proc/base.c:
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry,
struct nameidata *nd)
{
...
result = proc_base_lookup(dir, dentry); /* получить информацию о себе? */
if (!IS_ERR(result) || PTR_ERR(result) != -ENOENT)
goto out;

tgid = name_to_int(dentry); /* преобразовать строку (PID) в число */
...
task = find_task_by_pid_ns(tgid, ns); /* task_struct, связанная с PID */
...
/*
* создать inode (proc_pid_make_inode), связанный с процессом task и
* назначить операции для inode:
* inode->i_op = &proc;_tgid_base_inode_operations;
* inode->i_fop = &proc;_tgid_base_operations;
*/
result = proc_pid_instantiate(dir, dentry, task, NULL);
...
out:
return result;
}


К данному моменту был создан inode для /proc/, и установлены операции, которые можно с ним проводить. Как быть с его содержимым? И снова поиск:

1
2
3
4
5
static const struct inode_operations proc_tgid_base_inode_operations = {
.lookup = proc_tgid_base_lookup,
.getattr = pid_getattr,
.setattr = proc_setattr,
};


За дальнейший поиск отвечает proc_tgid_base_lookup(), для чего предназначены другие две функции, вы уже должны понимать (получить/установить атрибуты каталога, например, владельца или права доступа).
proc_tgid_base_lookup() передает всю работу proc_pident_lookup():

1
2
3
4
5
static struct dentry *proc_tgid_base_lookup(struct inode *dir, 
struct dentry *dentry, struct nameidata *nd){
return proc_pident_lookup(dir, dentry, tgid_base_stuff,
ARRAY_SIZE(tgid_base_stuff));
}


Здесь следует обратить внимание на массив tgid_base_stuff. Список содержимого pid-директорий всегда один и тот же и определяется еще на стадии конфигурирования ядра. Массив tgid_base_stuff и определяет этот список:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fs/proc/base.c:
static const struct pid_entry tgid_base_stuff
= {
DIR("task", S_IRUGO|S_IXUGO, proc_task_inode_operations, proc_task_operations),
DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),
DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations),
...
REG("environ", S_IRUSR, proc_environ_operations),
INF("auxv", S_IRUSR, proc_pid_auxv),
ONE("status", S_IRUGO, proc_pid_status),
ONE("personality", S_IRUSR, proc_pid_personality),
INF("limits", S_IRUSR, proc_pid_limits),
...
REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
...
INF("cmdline", S_IRUGO, proc_pid_cmdline),
ONE("stat", S_IRUGO, proc_tgid_stat),
ONE("statm", S_IRUGO, proc_pid_statm),
REG("maps", S_IRUGO, proc_maps_operations),
...
REG("mem", S_IRUSR|S_IWUSR, proc_mem_operations),
LNK("cwd", proc_cwd_link),
LNK("root", proc_root_link),
LNK("exe", proc_exe_link),
REG("mounts", S_IRUGO, proc_mounts_operations),
REG("mountinfo", S_IRUGO, proc_mountinfo_operations),
REG("mountstats", S_IRUSR, proc_mountstats_operations),
...
INF("oom_score", S_IRUGO, proc_oom_score),
REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations),
...
};


DIR, REG, INF, LNK, ONE являются макросами, предназначеными для генерации директорий (DIR), файлов (REG, INF, ONE) и ссылок (LNK). Каждый макрос определяет тип записи, имя записи, права и операции.
Вернемся к поиску и функции proc_pident_lookup():

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static struct dentry *proc_pident_lookup(struct inode *dir, 
struct dentry *dentry,
const struct pid_entry *ents,
unsigned int nents)
{
...
last = &ents;<nents>;
for (p = ents; p &lt;= last; p++) {
if (p-&gt;len != dentry-&gt;d_name.len)
continue;
if (!memcmp(dentry-&gt;d_name.name, p-&gt;name, p-&gt;len))
break;
}
...
error = proc_pident_instantiate(dir, dentry, task, p);
...
}</nents>


Поиск в ней осуществляется довольно просто — перебором, сравнивая искомое имя с именем из массива tgid_base_stuff. Затем, когда имя найдено, в функции proc_pident_instantiate создается inode, и устанавливаются соответствующие операции.

Суммируя сказанное и опуская детали, рассмотрим следующую команду:

1
$ cat /proc/filesystems


Что происходит? Происходит вызов sys_open(), который является системным вызовом. В нем подготавливаются все необходимые объекты ядра и ищется inode (много функций спустя мы дойдем до описанной ранее функции proc_root_lookup()), связанный с файлом filesystems. Далее, когда все операции завершены, вызывается sys_read() для чтения данных из файла. Ранее было описано, какой путь проделывает функция чтения и мы остановились на том, что вызывается специфичная для ФС функция чтения, если для файла зарегистрирована операция чтения. В данном случае будет вызвана функция proc_reg_read() (reg означает regular, то есть обычный файл) для чтения данных из файла. И в конце концов будет вызван sys_write(), но уже отношения к proc данный вызов иметь не будет, так как все данные записываются в стандартный вывод (stdout).

5. Модули ядра

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

1
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_pid_entry *parent);


Создание новой записи (обычного файла). Функция возвращает указатель на созданную запись в случае успеха, в противном случае NULL. create_proc_entry() принимает три аргумента:
name — имя записи;
mode — права доступа к файлу (владелец/группа/остальные);
parent — указатель на запись-родителя. Если новая запись создается в корне ФС proc, то следует указывать NULL.

1
void remove_proc_entry(const char *name, struct proc_pid_entry *parent);


Функция remove_proc_entry() удаляет запись и принимает два аргумента: имя удаляемой записи и каталог, из которого она удаляется.
Чтение/запись из файла осуществляется с помощью функций, которые должен разработать пользователь. Указатели на эти функции ставятся в соответствующих полях записи (см. раздел 3, описание структуры proc_dir_entry, поля read_proc/write_proc):

1
2
3
4
struct proc_pid_entry *entry;
...
entry-&gt;read_proc = read_func; /* read_func наша функция */
entry-&gt;write_proc = write_func; /* write_func наша функция */


Ниже представлен простой модуль ядра, при загрузке которого создается новая запись в корне ФС proc и удаляется при выгрузке модуля:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* 
* proc_sample.c
* Данный модуль взят с сайта:
* www.coding.com.br/kernel/adding-procfs-and-sysfs-interface-in-your-lkml/
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

/* Для proc_dir_entry и create_proc_entry */
#include <linux/proc_fs.h>

/* Для функций sprintf и snprintf */
#include <linux/string.h>

/* Для функции copy_from_user */
#include <linux/uaccess.h>

static char internal_buffer<256>;

int buf_read(char *buf, char **start, off_t offset, int count, int *eof, void *data)
{
int len;
len = snprintf(buf, count, "%s", internal_buffer);
return len;
}

static int buf_write(struct file *file, const char *buf, unsigned long count, void *data)
{
if(count &gt; 255) /* Для избежания переполнения буфера */
count = 255;

/* Копирует данные из пространства пользователя в пространство ядра */
copy_from_user(internal_buffer, buf, count);

/* Добавляем NULL в конец строки */
internal_buffer<count> = '\0';
return count;
}

int __init proc_init(void) {
/* Создаем новую запись */
struct proc_dir_entry *de = create_proc_entry("coding", 0666, NULL);

/* Устанвливаем указатели на наши функции */
de-&gt;read_proc = buf_read; /* чтение */
de-&gt;write_proc = buf_write; /* запись */

/* Инициализируем наш internal_buffer с каким-либо текстом. */
sprintf(internal_buffer, "www.coding.com.br");
return 0 ;
}

void __exit proc_cleanup(void) {
/* Удаляем нашу запись */
remove_proc_entry("coding", NULL);
}

module_init(proc_init);
module_exit(proc_cleanup);

MODULE_LICENSE("GPL");</count>


Содержимое Makefile:

1
2
3
4
5
obj-m += proc_sample.o 
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


Собираем модуль и затем загружаем его:

 1
2
3
4
5
6
7
8
9
10
11
# make
... // Если все прошло успешно, то будет создан файл proc_sample.ko
# insmod proc_sample.ko // Загружаем модуль
$ ls -l /proc/coding // В proc появилась новая запись
-rw-rw-rw-. 1 root root 0 Dec 1 03:19 /proc/coding
$ cat /proc/coding // Выводим содержимое
www.coding.com.br
$ echo "some text" >> /proc/coding // Записываем новые данные
$ cat /proc/coding
some text
# rmmod proc_sample // Выгружаем модуль. Запись будет удалена


Больше информации по написанию модулей для ФС proc можно получить на:
http://www.gnugeneration.com/books/linux/2.6.20/procfs-guide/;
http://www.ibm.com/developerworks/ru/library/l-proc/.P.S. Традиционная pdf'ка (пока лучше просто качать, scrib.com надругался над текстом (шрифты уже менял - не помогает); в pdf есть сноски с небольшими пояснениями). Что хотелось бы сказать по поводу самой статьи. Я думаю, что вы заметили - много кода, могу убрать под спойлер, так что говорите, как будет удобнее. Далее, я обещал написать ее к концу ноября, но не успел. Сама статья не является завершенной, это был черновик (я его как мог "причесал" и вот он перед вами). Но продолжать работу над статьей сейчас нет желания. Если кто-то захочет ее детализировать и дополнить, напишите в ЛС, буду рад и подскажу, что еще хотелось бы видеть.
Если заметите очепятки, то в ЛС. Если будут смысловые ошибки, то пишите в комментарии (даже если сомневаетесь, все спокойно обсудим).


Тэги: kernel proc vfs
+ 30 -
Похожие Поделиться

le087 06.12.2010 09:45 #
+ 0 -
Круто. Вы сделали большую работу. Спасибо.
mrdev 06.12.2010 13:12 #
+ 0 -
Очень интересно. Спасибо.
segoon 06.12.2010 16:07 #
+ 5 -
В самом начале про dentry нужно написать зачем разделять понятия dentry и inode - у одного inode'а может быть более одного имени (либо ни одного) из-за hard link'ов.

struct file - не обязательно связан с процессом, ибо можно открыть файл один раз, а дальше передавать с пом. fork(2) или UNIX-сокетов в другие процессы; при этом struct file будет единственной. dup2(2) и пр., к слову, не создают новых struct file.

do_sync_read() не просто какая-то функция, а обёртка вокруг fops->aio_read() (попытка вызова асинхронной функции в синхронном режиме). Не реализовав ни одной функции *read() в ФС, глупо надеяться, что каким-то чудесным образом она появится.

SLAB произносится-таки как слэб/слаб, но не сляб :)

У copy_from_user() нужно проверять результат.
dementiy 06.12.2010 18:37 #
+ 1 -
Спасибо за дополнения. Соглашусь по всем пунктам, хотя про то, что file может быть не связан с процессом я не задумывался. На счет "сляб", так обычно пишут в источниках на русском, поэтому я тоже использовал такое написание, хотя наверное лучше оставлять термин на языке оригинала и писать "slab".
segoon 06.12.2010 19:12 #
+ 2 -
Более того, ни у одного процесса данный файл может не быть в таблице открытых файловых дескрипторов, при этом структура file может существовать - её может использовать какой-то левый драйвер. У структуры есть счётчик ссылок, пока последний драйвер не отпустит структуру, счётчик не обнулится, и структура не будет освобождена.

Вообще ещё в кернел моде можно работать с файлами без таблицы файловых дескрипторов, но за такое бьют молотком по рукам.

Про slab - не знаю, я на русском ничего по ядру не читал. Но по-английски произносится как ?.


Ещё - прежде чем создавать что-либо в /proc, лучше три раза подумать: действительно ли ваща информация напрямую связана с _процессами_? Если это вывод какой-то глобальной информации, то его лучше запихнуть в /sys (если знаете куда именно), либо в /sys/kernel/debug/ (если не знаете).

Смотреть видео онлайн

Онлайн видео бесплатно


Смотреть русское с разговорами видео

Online video HD

Видео скачать на телефон

Русские фильмы бесплатно

Full HD video online

Смотреть видео онлайн

Смотреть HD видео бесплатно

School смотреть онлайн