linux内核伪文件系统—proc分析
文章目录
一、proc文件系统简介
首先/proc
文件系统是一个接口,linux内核通过它可以获取linux系统上的运行信息。很多用户空间程序都需要依靠/proc文件系统中的内容来完成它们的工作。如下图所示:
linux内核中运行的每一个用户进程都会对应/proc文件系统中的一个条目。例如:1号进程init
。他在/proc对应的条目则为1。可以使用:
ls -l /proc/1
得到init进程相关的条目:
注意,linux系统中运行的用户进程都有对应的条目。因此,在开发过程中,可以查看对应的条目,从而得到系统中运行进程的相关信息。
- 如果需要获取到init进程相关联的内存信息,则使用:
cat /proc/1/maps
- 如果需要获取init进程的状态信息,则使用:
cat /proc/1/status
-
如果需要获取系统中的处理器信息,则使用
cpuinfo
条目。 -
如果需要获取系统内存的统计信息,则使用
meminfo
条目。 -
如果需要获取linux内核版本的信息,则使用
version
条目。
对于/proc文件系统来说,其中给出了许多关于linux内核运行过程中的信息(这里不一一列举啦)。其中有很多的信息条目值得仔细分析和学习。同样的,也有很多软件帮助我们提取/proc文件系统中的条目信息。例如:top、ps命令等。
- 通过修改
/proc/sys
目录下条目,可以动态更改内核参数或变量。
linux内核的这个机制,由sysctl
接口提供了动态更改某些内核参数和变量的方法,而不需要重新编译linux内核或重新引导系统。其会在/proc/sys
目录下生成一棵可修改的sysctl
条目树。通过修改该目录下的值就可以动态的更改内核中的某些参数和变量。该机制在实际开发中非常好用。
二、proc文件系统源码分析
本小结将分析以下两个小问题:
(1)linux内核下的/proc文件系统如何创建条目?
(2)linux内核如何初始化/proc文件系统?
(2-1)linux内核下的/proc文件系统如何创建条目
/proc
文件系统的源码位于(/fs/proc)目录下,如下图所示:
从Makefile
文件可知,其大致的一个编译流程。从其目录的源文件组织结构来看,大致与ls /proc
命令下的条目相关联。例如:/proc目录下有个version
条目,则在源码目录结构下将有一个对应的version.c
源文件。来看一下该文件中的内容:
(/fs/proc/version.c)
static const struct file_operations version_proc_fops = {
.open = version_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init proc_version_init(void)
{
proc_create("version", 0, NULL, &version_proc_fops);
return 0;
}
fs_initcall(proc_version_init);
fs_initcall宏定义:
(/include/linux/init.h)
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn; \
LTO_REFERENCE_INITCALL(__initcall_##fn##id)
#define fs_initcall(fn) __define_initcall(fn, 5)
从以上代码片段可见:version.c文件的写法与设备驱动的编写类似:定义一个struct file_operations
,并指定对应的callback,然后使用proc_create()
创建version条目,最后使用fs_initcall()
宏进行初始化。(注:其目录下的许多文件都有类似的程序结构和写法,大同小异)
(2-2)linux内核如何初始化/proc文件系统
/proc
是一个文件系统,那么在linux内核源码中,就应该使用struct file_system_type
来描述。其定义如下(/fs/proc/root.c):
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,
};
【函数调用链】
在start_kernel()
函数中将调用proc_root_init()
函数来初始化/proc
文件系统。
(/fs/proc/root.c)
void __init proc_root_init(void)
{
int err;
//初始化 proc的inode cache
proc_init_inodecache();
err = register_filesystem(&proc_fs_type);
if (err)
return;
proc_self_init();
proc_thread_self_init();
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_create_mount_point("fs/nfsd"); /* somewhere for the nfsd filesystem to be mounted */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
/* just give it a mountpoint */
proc_create_mount_point("openprom");
#endif
proc_tty_init();
proc_mkdir("bus", NULL);
proc_sys_init();
}
上述代码第7行,调用register_filesystem()
函数向linux 内核注册/proc文件系统。
其次,上面代码中多处还调用proc_mkdir()
函数创建对应的/proc下的条目((/fs/proc/generic.c)):
struct proc_dir_entry *proc_mkdir(const char *name,struct proc_dir_entry *parent)
{
return proc_mkdir_data(name, 0, parent, NULL);
}
最后都将调用到共同的proc_mkdir_data()
函数,如下定义(/fs/proc/generic.c):
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
struct proc_dir_entry *parent, void *data)
{
struct proc_dir_entry *ent;
if (mode == 0)
mode = S_IRUGO | S_IXUGO;
//创建proc下的目录节点
ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
if (ent) {
ent->data = data;
ent->proc_fops = &proc_dir_operations;
ent->proc_iops = &proc_dir_inode_operations;
parent->nlink++;
//将通过__proc_create()创建出的目录节点添加到父节点
if (proc_register(parent, ent) < 0) {
kfree(ent);
parent->nlink--;
ent = NULL;
}
}
return ent;
}
三、总结
本文没有过多分析proc
文件系统背后的实现细节和机制。描述了在开发中,/proc文件系统下关于系统运行的一些信条目信息;从源码角度简单分析了/proc文件系统,描述了linux内核启动过程中是如何初始化/proc文件系统的。
搜索关注【嵌入式小生】wx公众号获取更多精彩内容>>>>