proc文件系统是一种虚拟文件系统,其信息不能从块设备读取。只有在读取文件内容时才动态生成响应的信息。使用proc文件系统,可以获得有关内核各子系统的信息(如内存利用率、附接的外设等),也可以在不重新编译内核源代码的情况下修改内核的行为,或重启系统。
proc文件系统提供一种接口,可用于该机制导出所有选项,直接地修改参数无需开发专门程序,只需要一个shell和标准的cat、echo即可。
/proc信息
信息可以分为一下几类:
内存管理,系统进程的特征数据,文件系统,设备驱动程序,系统总线,电源管理,终端,系统控制参数。
proc常见文件
buddyinfo
:用于诊断内存碎片问题;
cmdline
:在启动时传递值内核的相关参数信息,这些信息通常由lilo或grub等启动管理工具进行传递。
cpuinfo
:处理器的相关信息文件。
crypto
:系统上已安装的内核使用的密码算法及每个算法的详细信息列表。
devices
:系统已经加载的所有块设备和字符设备的信息;
diskstats
:每块磁盘设备的磁盘I/O统计信息列表。
fb
:帧缓冲设备列表文件,包含帧缓冲设备的设备号和相关驱动信息。
filesystems
:当前被内核支持的文件系统类型列表文件,被标示为nodev的文件系统表示不需要块设备的支持。
interrupts
:X86或X86_64体系架构系统上每个IRQ相关的中断号列表;
iomem
:每个物理设备上的记忆体(RAM或者ROM)在系统内存中的映射信息。
ioports
:当前正在使用且已经注册过的与物理设备进行通讯的输入-输出端口范围信息列表。
kallsyms
:模块管理工具用来动态链接或绑定模块的符号定义,由内核输出;
locks
:保存当前由内核锁定的文件的相关信息,包含内核内部的调试数据;每个锁占一行,且具有一个唯一编号。
meminfo
:系统中关于当前内存的利用状况等的信息,常由free
命令使用。
mounts
:文件系统挂载情况
modules
:当前装入内核的所有模块名称列表,可以由lsmod命令使用,也可以直接查看。
partitions
:块设备每个分区的主设备号(major)和次设备号(minor)等信息。
stat
:实时追踪自系统上次启动以来的多种统计信息。
swaps
:当前系统上的交换分区及其空间利用信息。
uptime
:系统上次启动以来的运行时间
version
:当前系统运行的内核版本。
vmstat
:当前系统虚拟内存的多种统计数据。
zoneinfo
:内存区域(zone)的详细信息列表。
proc的数据结构
实现proc文件系统的代码紧围绕这些数据结构而建立的,proc大量使用VFS的数据结构,因为作为一种文件系统,他必须集成到内核的VFS抽象层中。
还有一些特定于proc的数据结构,用于组织内核提供的数据信息。还必须提供一个到内核各个子系统的接口,使得内核能从其数据结构中提取信息,然后借助/proc
提供给用户空间。proc文件系统中的每个数据项都由proc_dir_entry的一个实例描述。
/*
* This is not completely implemented yet. The idea is to
* create an in-memory tree (like the actual /proc filesystem
* tree) of these proc_dir_entries, so that we can dynamically
* add new files to /proc.
*
* parent/subdir are used for the directory structure (every /proc file has a
* parent, but "subdir" is empty for all non-directory entries).
* subdir_node is used to build the rb tree "subdir" of the parent.
*/
struct proc_dir_entry {
unsigned int low_ino;//inode编号
umode_t mode;//反应对应数据项的类型(文件,目录等),以及访问权限分配
nlink_t nlink;//子目录和软连接项目
kuid_t uid;//用户ID
kgid_t gid;//组ID
loff_t size;//表示按字节计算的文件长度
const struct inode_operations *proc_iops;//节点操作
const struct file_operations *proc_fops;//文件操作
struct proc_dir_entry *parent;//父目录指针
struct rb_root subdir;//指向一个目录的第一个子数据项
struct rb_node subdir_node;//子目录节点
void *data;
atomic_t count; /* use count */
atomic_t in_use; /* number of callers into module in progress; */
/* negative -> it's going away RSN */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
u8 namelen;
char name[];
};
proc文件系统中的每个数据项的数据结构,都是由proc_dir_entry
一个实例进行描述的。
装载proc文件系统
内核内部用于描述proc文件系统结构和内容的数据已经初始化之后,下一步是将该文件系统装载到目录树中,在内核添加新的文件系统时,会扫描一个链表,查找与该文件系统相关的file_system_type
实例。
static struct file_system_type proc_fs_type = {
.name = "proc",
.mount = proc_mount,
.kill_sb = proc_kill_sb,
.fs_flags = FS_USERNS_VISIBLE | FS_USERNS_MOUNT,
};
struct file_system_type {
const char *name;
int fs_flags;
#define FS_REQUIRES_DEV 1
#define FS_BINARY_MOUNTDATA 2
#define FS_HAS_SUBTYPE 4
#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
#define FS_USERNS_DEV_MOUNT 16 /* A userns mount does not imply MNT_NODEV */
#define FS_USERNS_VISIBLE 32 /* FS must already be visible */
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
const char *, void *);
void *(*alloc_mnt_data) (void);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct hlist_head fs_supers;
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
struct lock_class_key s_vfs_rename_key;
struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];
struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key i_mutex_dir_key;
};
proc文件系统超级块由proc_get_sb
提供,此函数基于另一个内核辅助例程(get_sb_single
),借助proc_fill_supper
(主要负责用一些定义后从不改变的值来填充super_block的每个成员)来填充一个super_block
的新实例。
proc_sops
中对超级块的各个操作,其中收集内核管理proc文件系统所需的各个函数。
静态的proc_dir_entry
实例:
/*
* This is the root "inode" in the /proc tree..
*/
struct proc_dir_entry proc_root = {
.low_ino = PROC_ROOT_INO,
.namelen = 5,
.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,
.subdir = RB_ROOT,
.name = "/proc",
};
管理/proc数据项
1.数据项的创建和注册
新数据项分为两个步骤添加到proc文件系统,首先,创建proc_dir_entry
的一个新实例,填充描述该数据项的所有需要的信息。
使用fs/proc/generic.c
中的proc_register
将其注册到proc
文件系统中:
static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
int ret;
ret = proc_alloc_inum(&dp->low_ino);
if (ret)
return ret;
write_lock(&proc_subdir_lock);
dp->parent = dir;
if (pde_subdir_insert(dir, dp) == false) {
WARN(1, "proc_dir_entry '%s/%s' already registered\n",
dir->name, dp->name);
write_unlock(&proc_subdir_lock);
proc_free_inum(dp->low_ino);
return -EEXIST;
}
write_unlock(&proc_subdir_lock);
return 0;
}
创建数据项之后,注册到/proc文件系统步骤:
a.生成一个唯一一个proc内部编号,向数据项赋予身份,get_inode_number
返回一个未使用的编号,用于为动态生成的数据项;
b.必须适当的设置proc_dir_entry
实例的next和parent成员,将新数据项集成到proc文件系统的层次结构中;
c.如果此proc_dir_entry
成员的proc_iops
或proc_fops
为NULL指针,那么需要根据文件类型,适当的设置指向file_operations
和inode_operations
结构实例的指针。
用于管理proc的数据项:
a.proc_mkdir创建一个新目录
b.proc_mkdir_mode:创建一个新目录,目录的访问权限可以显示指定。
c.proc_symlink:生成一个符号链接
d.remove_proc_entry:从目录中删除一个动态生成的数据项。
内核源码相关示例文件,在Documentation/DocBook/procfs_example.c.
负责proc文件系统的读写例程和内核子系统之间的交互。