proc文件系统------《深入理解Linux内核架构》笔记

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简单。

proc文件系统------《深入理解Linux内核架构》笔记_第1张图片

1-1 底层为procVFS结构

1-1中绿色方框的部分为proc实现的部分,proc中文件的大小为0,所以inode中的i_mapping并没有使用,值为NULLproc_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;
}


你可能感兴趣的:(linux,内核)