proc文件系统
1. proc简介(Process Data System, 进程数据系统)
proc文件系统属于虚拟文件系统,即该文件系统的数据,由内核动态生成,并不会存放在持久存储数据中。
proc文件系统,使得内核可以生成与系统状态和配置有关的信息。该信息可以由用户和系统程序从普通文件读取,而无需专门的工具与内核通信。比如可以通过cat status打印进程的状态信息,如下:
daniel@daniel-HP-ProBook-4411s:/proc/self$ cat status
Name: bash
State: S (sleeping)
Tgid: 5797
Pid: 5797
PPid: 4378
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
......
从内核开发趋势来看,正在远离用proc文件系统提供的信息,而倾向于采用特定与问题的虚拟文件系统来导出数据。一个很好的例子就是USB文件系统,将与USB子系统有关的许多状态信息导出到用户空间,而没有给proc增加新的负担。但这并不意味这,proc文件系统变的多余,当今,/proc依旧重要,不仅在安装新的发布版时,而且也用于支持(自动化的)系统管理。
proc文件系统主要有如下几个主要功能:
a. 用于查看进程的数据信息,别忘了proc的全称为process data system
b. 查看与特定的内核子系统无关的一般信息,如/proc/iomem, /proc/ioports, /proc/interrupts
c. 查看网络信息,/proc/net子目录提供了内核的各种网络选项的有关数据。
d. 查看和修改系统控制参数,由/proc/sys子目录提供。例,可以通过cat /proc/sys/vm/swappiness 查看交换算法在换出页时的积极程度。
2. proc的数据结构
与Ext2一样,proc大量使用了VFS数据结构,因为作为一种文件系统,它必须集成到内核的VFS抽象层中。但,毕竟proc只是用于获取内核的数据为主要目的,所以在其设计的过程中,遵循简单实用的特性,较Ext2简单。
图1-1 底层为proc的VFS结构
图1-1中绿色方框的部分为proc实现的部分,proc中文件的大小为0,所以inode中的i_mapping并没有使用,值为NULL。proc_dir_entry用于表示一个proc文件系统中的目录,通过proc_mkdir创建,目录之间的树形结构通过proc_dir_entry中的*next, *parent, *subdir维护。图1-1中所涉及的结构体请参考书中的第8, 10章和Linux内核源代码。
以下以查询一个目录为例(主要涉及path_lookup函数,查找过程与VFS与Ext2文件系统一致),得益于VFS文件系统,proc文件系统只需要自定义实现lookup接口。因为查找过程将在一定时间到达real_lookup,该函数将通过调用dentry->i_op->lookup实现。
dentry->i_op->lookup,对于/proc文件,为proc_root_lookup;对于/proc目录一下的非PID目录,为proc_lookup;对于/proc下的PID目录,为proc_tgid_base_operations。
// proc_root_lookup函数的主要功能
static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry,
struct nameidata *nd)
{
if (!proc_lookup(dir, dentry, nd)) {//顺序扫描指定路径的各个分量
return NULL;
}
//需要扫描的目录为一个pid目录,而该pid目录的proc_dir_entry还没有创建。
return proc_pid_lookup(dir, dentry, nd); // 查找指定pid目录下的目录或文件
}
}
// proc_lookup 函数,PDE主要是由container_of机制实现的
struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
return proc_lookup_de(PDE(dir), dir, dentry);
}
/*
* Don't create negative dentries here, return -ENOENT by hand
* instead.
*/
struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
struct dentry *dentry)
{
struct inode *inode = NULL;
int error = -ENOENT;
spin_lock(&proc_subdir_lock);
for (de = de->subdir; de ; de = de->next) { //遍历de下面的所有路径分量
if (de->namelen != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
unsigned int ino;
ino = de->low_ino;
de_get(de);
spin_unlock(&proc_subdir_lock);
error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de); // 查找成功,创建proc_dir_entry *de对应的proc_inode, 并返回&(proc_inode.vfs_inode)。注意inode的inode_operations初始化为de->proc_iops。
goto out_unlock;
}
}
spin_unlock(&proc_subdir_lock);
out_unlock:
if (inode) {
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode); // 将新初始化的dentry加入dentry缓存
return NULL;
}
if (de)
de_put(de);
return ERR_PTR(error);
}
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry,
struct nameidata *nd)
{
struct dentry *result = ERR_PTR(-ENOENT);
struct task_struct *task;
unsigned tgid;
struct pid_namespace *ns;
result = proc_base_lookup(dir, dentry); // if (dentry->d_name== self) 创建self的inode
if (!IS_ERR(result) || PTR_ERR(result) != -ENOENT)
goto out;
tgid = name_to_int(dentry); //将字符串转换为unsigned int
if (tgid == ~0U)
goto out;
ns = dentry->d_sb->s_fs_info;
rcu_read_lock();
task = find_task_by_pid_ns(tgid, ns); // 查找对应pid对应的task_struct
if (task)
get_task_struct(task); // 使用计数加1
rcu_read_unlock();
if (!task)
goto out;
result = proc_pid_instantiate(dir, dentry, task, NULL);// 为该pid目录创建inode和dentry,注意inode的inode_operations初始化为proc_tgid_base_inode_operations。
put_task_struct(task);
out:
return result;
}