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

Смотреть узбекский видео

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

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

06.12.10 02:08 dementiy

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

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

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

1
2
3
4
struct proc_pid_entry *entry;
...
entry->read_proc  = read_func;  /* read_func наша функция */
entry->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 > 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:

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 есть сноски с небольшими пояснениями). Что хотелось бы сказать по поводу самой статьи. Я думаю, что вы заметили - много кода, могу убрать под спойлер, так что говорите, как будет удобнее. Далее, я обещал написать ее к концу ноября, но не успел. Сама статья не является завершенной, это был черновик (я его как мог "причесал" и вот он перед вами). Но продолжать работу над статьей сейчас нет желания. Если кто-то захочет ее детализировать и дополнить, напишите в ЛС, буду рад и подскажу, что еще хотелось бы видеть.
Если заметите очепятки, то в ЛС. Если будут смысловые ошибки, то пишите в комментарии (даже если сомневаетесь, все спокойно обсудим).


Теги:

le087 06.12.10 09:45 # +0
Круто. Вы сделали большую работу. Спасибо.
mrdev 06.12.10 13:12 # +0
Очень интересно. Спасибо.
segoon 06.12.10 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.10 18:37 # +1
Спасибо за дополнения. Соглашусь по всем пунктам, хотя про то, что file может быть не связан с процессом я не задумывался. На счет "сляб", так обычно пишут в источниках на русском, поэтому я тоже использовал такое написание, хотя наверное лучше оставлять термин на языке оригинала и писать "slab".
segoon 06.12.10 19:12 # +2
Более того, ни у одного процесса данный файл может не быть в таблице открытых файловых дескрипторов, при этом структура file может существовать - её может использовать какой-то левый драйвер. У структуры есть счётчик ссылок, пока последний драйвер не отпустит структуру, счётчик не обнулится, и структура не будет освобождена.

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

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


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

Посты Комментарии
Последние посты
    Посты Комментарии
    Последние комментарии
      Посты Комментарии
      Изменения
        Посты Комментарии Изменения Черновики Избранное
        Черновики (все)
          Посты Комментарии Изменения Черновики Избранное
          Избранное (всё)
            Посты Комментарии Изменения Черновики Избранное
            Лучшие блоги (все 151)
            Топ пользователей Топ блогов
            Топ пользователей Топ блогов
            Элита (все 3057 из 226 городов)
            Топ пользователей Топ блогов
            welinux.ru

            В хорошем качестве hd видео

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


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

            Online video HD

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

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

            Full HD video online

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

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

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