Вы находитесь на странице: 1из 15

Псевдо­файловая система proc

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

«procfs   –   виртуальная   файловая   система,   используемая   в   UNIX­like   операционных  


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

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

$ 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. 

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

Прежде   чем   перейти   к   файловой   системе   proc,   вкратце   рассмотрим   основные 


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

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

• Структура "file_system_type" представляет зарегистрированную ФС:

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  – если ФС была собрана как загружаемый модуль ­ указатель на модуль, 
отвечающий   за   данную   ФС3.   В   противном   случае,   если   ФС   встроена   в   ядро, 
указывается NULL;
• next  – указатель на следующую файловую систему. Все зарегистрированные ФС 
образуют   список,   на   вершину   которого   указывает   глобальная   переменная 
"file_systems"   (см.   рис.   2.2,   полный   список   зарегистрированных   ФС   можно 
получить в файле /proc/filesystems);
• fs_supers – список суперблоков для файловых систем данного типа.

Рис. 2.2. Список зарегистрированных файловых систем
2 Термины "структура" и "объект" в данном контексте равнозначны. Ни для кого не секрет, что в некоторых 
частях ядра используется объектно­ориентированный подход.
3 Обычно при написании модулей указывается "THIS_MODULE"
• Структура "vfsmount" представляет точку монтирования:

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) — это структура данных, которая хранит информацию 
о ФС в целом (типе ФС, размере, состоянии и т.д.) и выделяется при монтировании 
для каждой ФС:

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 — тип файловой системы, к которой принадлежит суперблок4;
• s_root — dentry корневой точки монтирования;.
• s_op — указатель на таблицу операций, связанных с суперблоком:

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 *);

4 Для ФС одного типа только одна структура "file_system_type", но разные суперблоки.
...
int (*statfs) (struct dentry *, struct kstatfs *);
...
};

• alloc_inode — выделяет память под inode;
• destroy_inode — освобождает память, выделенную под inode;
• drop_inode — вызывается, когда счетчик ссылок на inode становится равным 
нулю;
• delete_inode — удаляет inode;
• statfs — получение статистики ФС.

• Объект файл (file) — представляет открытый файл (создается при открытии файла), 
связанный с процессом:

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  —   указатель   на   таблицу   файловых   операций.   Файловый   операции 
проводятся   над   содержимым   файла.   Названия   большинства   файловых 
операций совпадают с названиями соответствующих системных вызовов:

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:

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 */
};

• 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):

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() и т.д.) .

• Объект  файловый   индекс  (inode5)   —   представляет   определенный   файл   (обычные 


файлы, директории, FIFO и т.д.):

include/linux/fs.h:
struct inode {
...
unsigned long i_ino;

5 Inode – information node (информационный узел) или index node (индексный узел), также говорят индексный 
дескриптор. Неизвестна точная трактовка термина "inode".
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 операции производятся над 
структурами   и   метаданными6,   связанными   с   файлом.   Также,   как   и   с 
файловыми   операциями,   названия   функций   совпадают   с   названиями 
соответствующих системных вызовов:

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.1. Справа показаны «приставки» и 
«окончания». Что они означают? Рассмотрим пример чтения из файла и предположим, что у 
нас есть следующий код:

...
err = read(fd, buf, count);
...

Функция  read()  является   библиотечной   функцией     и   принимает   три   аргумента: 


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

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);

6 Метаданные, то есть данные о данных
7 Предполагается, что файл уже был открыт, например, с помощью функции open, и дескриптор сохранен в 
fd
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
...
}

return ret;
}

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

В системном вызове  sys_read()  определяется объект  file, связанный с файловым 


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

fs/read_write.c:
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
...
if (ret >= 0) {
count = ret;
if (file->f_op->read)
ret = file->f_op->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():

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 
выглядит довольно просто:

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":

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»:

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. Иерархическое представление структуры ФС экземплярами pde8

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


структуры proc_inode:

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;
};

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

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

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

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<n>/mb_groups  и   т.д.)   и   поиск   process­specific   записей   (например 
/proc/1/cmdline).   Так   как   заранее   не   известно   какого   типа   ищется   запись,   то   вначале 
производится поиск обычных записей в функции  proc_lookup(),  и, если запись не была 
найдена, производится поиск process­specific записей функцией proc_pid_lookup().
Рассмотри второй вариант — поиск process­specific записи:

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/<pid>,  и установлены  операции, 


которые можно с ним проводить. Как быть с его содержимым? И снова поиск:

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():

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­директорий  всегда   один   и   тот   же  и   определяется   еще   на   стадии   конфигурирования 
ядра9. Массив tgid_base_stuff и определяет этот список:

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():

static struct dentry *proc_pident_lookup(struct inode *dir,


struct dentry *dentry,

9 Более точным было бы сказать, что на стадии конфигурирования ядра определяются дополнительные 
компоненты этого списка. В листинге приведены те записи, которые есть всегда.
const struct pid_entry *ents,
unsigned int nents)
{
...
last = &ents[nents - 1];
for (p = ents; p <= last; p++) {
if (p->len != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, p->name, p->len))
break;
}
...
error = proc_pident_instantiate(dir, dentry, task, p);
...
}

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

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

$ cat /proc/filesystems

Что   происходит?   Происходит   вызов  sys_open(),   который   является   системным 


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

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

ФС   proc   предоставляет   удобные   интерфейсы   для   управления   записями.   Следует 


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

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.

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


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

struct proc_pid_entry *entry;


...
entry->read_proc = read_func; /* read_func наша функция */
entry->write_proc = write_func; /* write_func наша функция */

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

/*
* 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 > 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->read_proc = buf_read; /* чтение */
de->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");

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

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

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

# 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/ .