浅析proc文件系统的创建和create_proc_read_entry函数的具体实现
start_kernel
=>proc_root_init
和http://blog.chinaunix.net/u1/38994/showart_1328541.html
《浅析kern_mount加载sysfs的流程》细节部分一模一样,
因为sysfs是一个内存文件系统,所以文件的物理存储关系就需要使用sd来维护,因此sysfs_dirent即sd就类似于硬盘中的磁道.
sysfs文件系统是一个排它式的文件系统,不论被mount多少次都只产生一个sb超级块,
如果尝试再次mount,即尝试再次调用sysfs_get_sb获取另一个sb超级块,那么将执行atomic_inc(old->s_active);增加
已被mount的引用计数,然后如果s已经执行了alloc_super,那么调用destroy_super将其销毁,然后返回这个已被mount了的
super_block超级块old,这样就实现了sysfs文件系统不论被mount多少次都只产生一个sb超级块的效果,所以取名为get_sb_single[luther.gliethttp]
类似的:proc也是一个内存文件系统,所以文件的物理存储关系是通过proc_dir_entry来维护,因此proc_dir_entry即dp就类似硬盘中的物理磁道.
proc的sb超级块和sysfs一样,不论被mount多少次也都只产生一个sb超级块.
如果de指定了内容,那么,将inode格式化为de这个"物理介质上的文件"的所对应的信息
这和sysfs的实现方式一模一样[luther.gliethttp].
proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);执行完成之后,proc_root这个"物理介质上的文件"对应的内存inode节点就创建出来了,也就是proc_root这个"物理介质上的文件"信息成功的被加载到kernel内存,翻译成了kernel所需的存在形式,因此内核将知晓该proc_root"物理介质上的文件"的存在,创建查找文件之类的操作也就能够紧接着可以使用[luther.gliethttp]
将dp这个'物理文件'添加到'物理磁盘'的文件视图链表中,以供以后查找访问
这是一个从proc_root开始遍历所有子目录的过程函数,以下纯属'物理磁盘'性质的操作,所以和inode没有产生任何关系,
将在proc_lookup函数中来建立这种'物理磁盘文件'和inode之间一一对应的映射关联[luther.gliethttp]
//在'物理磁盘'上创建'物理文件',然后替换为我们这里指定的proc_fops方法
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件',完全由mode指定[luther.gliethttp]
void create_seq_entry(char *name, mode_t mode, const struct file_operations *f);
//固定方法,创建目录型的'物理文件'
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件'
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);
init进程会执行:
mkdir("/proc", 0755);
mount("proc", "/proc", "proc", 0, NULL);//将proc文件系统可视化的挂载到创建出来的/proc目录下
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,
};
void __init proc_root_init(void)
{
int err = proc_init_inodecache();
if (err)
return;
err = register_filesystem(&proc_fs_type);//登记proc文件系统结构体
//参看http://blog.chinaunix.net/u1/38994/showart_1328559.html
//《浅析sysfs文件系统注册流程》
if (err)
return;
proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);//生成"proc"文件系统的mount节点
//参看http://blog.chinaunix.net/u1/38994/showart_1328541.html
//《浅析kern_mount加载sysfs的流程》
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt)) {
unregister_filesystem(&proc_fs_type);
return;
}
proc_misc_init();
proc_net_init();
#ifdef CONFIG_SYSVIPC
proc_mkdir("sysvipc", NULL);
#endif
proc_root_fs = proc_mkdir("fs", NULL);
proc_root_driver = 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_bus = proc_mkdir("bus", NULL);
proc_sys_init();
}
static struct file_system_type proc_fs_type = {
.name = "proc",
.get_sb = proc_get_sb,
.kill_sb = proc_kill_sb,
};
static int proc_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
int err;
struct super_block *sb;
struct pid_namespace *ns;
struct proc_inode *ei;
if (proc_mnt) {
//已经被执行过一次kern_mount_data(&proc_fs_type, &init_pid_ns);了
/* Seed the root directory with a pid so it doesn't need
* to be special in base.c. I would do this earlier but
* the only task alive when /proc is mounted the first time
* is the init_task and it doesn't have any pids.
*/
ei = PROC_I(proc_mnt->mnt_sb->s_root->d_inode);
if (!ei->pid)
ei->pid = find_get_pid(1);
}
if (flags & MS_KERNMOUNT)
ns = (struct pid_namespace *)data;
else
ns = current->nsproxy->pid_ns;
sb = sget(fs_type, proc_test_super, proc_set_super, ns);
if (IS_ERR(sb))
return PTR_ERR(sb);
if (!sb->s_root) {
sb->s_flags = flags;
err = proc_fill_super(sb);//只有第一次生成sb才会执行此句来填充超级块
if (err) {
up_write(&sb->s_umount);
deactivate_super(sb);
return err;
}
ei = PROC_I(sb->s_root->d_inode);//找到包含sb超级块对应的inode的proc_inode指针
if (!ei->pid) {
rcu_read_lock();
ei->pid = get_pid(find_pid_ns(1, ns));
rcu_read_unlock();
}
sb->s_flags |= MS_ACTIVE;
ns->proc_mnt = mnt;//指定current内核管理结构体的proc_mnt信息
}
return simple_set_mnt(mnt, sb);//做一个简单mnt设置
}
int proc_fill_super(struct super_block *s)
{
struct inode * root_inode;
s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;//sb超级块操作函数集
s->s_time_gran = 1;
de_get(&proc_root);
root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
//和sysfs的实现一模一样,请参考《浅析kern_mount加载sysfs的流程》
if (!root_inode)
goto out_no_root;
root_inode->i_uid = 0;
root_inode->i_gid = 0;
s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto out_no_root;
return 0;
out_no_root:
printk("proc_read_super: get root inode failed\n");
iput(root_inode);
de_put(&proc_root);
return -ENOMEM;
}
struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
struct proc_dir_entry *de)
{
struct inode * inode;
if (de != NULL && !try_module_get(de->owner))
goto out_mod;
inode = iget_locked(sb, ino);//生成inode,和sysfs不同的是
//proc超级块的s_op = &proc_sops;操作函数集自定义了.alloc_inode = proc_alloc_inode,
//节点生成函数,而sysfs使用的是
//inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);//所以从cache中获取一个空闲染色的slab对象
//
if (!inode)
goto out_ino;
if (inode->i_state & I_NEW) {
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
PROC_I(inode)->fd = 0;
PROC_I(inode)->pde = de;//该inode对应"物理介质上的文件"--de
if (de) {
//如果de指定了内容,那么,将inode格式化为de这个"物理介质上的文件"的所对应的信息
//这和sysfs的实现方式一模一样[luther.gliethttp].
if (de->mode) {
inode->i_mode = de->mode;
inode->i_uid = de->uid;
inode->i_gid = de->gid;
}
if (de->size)
inode->i_size = de->size;
if (de->nlink)
inode->i_nlink = de->nlink;
if (de->proc_iops)
inode->i_op = de->proc_iops;
if (de->proc_fops) {
if (S_ISREG(inode->i_mode)) {
#ifdef CONFIG_COMPAT
if (!de->proc_fops->compat_ioctl)
inode->i_fop =
&proc_reg_file_ops_no_compat;
else
#endif
inode->i_fop = &proc_reg_file_ops;
} else {
inode->i_fop = de->proc_fops;
}
}
}
unlock_new_inode(inode);
}
return inode;
out_ino:
if (de != NULL)
module_put(de->owner);
out_mod:
return NULL;
}
//proc文件系统自定义特殊化的inode节点生成函数
static struct inode *proc_alloc_inode(struct super_block *sb)
{
struct proc_inode *ei;
struct inode *inode;
ei = (struct proc_inode *)kmem_cache_alloc(proc_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
ei->pid = NULL;
ei->fd = 0;
ei->op.proc_get_link = NULL;
ei->pde = NULL;
inode = &ei->vfs_inode;//ei的vfs_inode为inode节点
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;//当前时间
return inode;
}
====================================================
看看proc文件系统下如何创建文件
void __init proc_misc_init(void)
{
static struct {
char *name;
int (*read_proc)(char*,char**,off_t,int,int*,void*);
} *p, simple_ones[] = {
{"loadavg", loadavg_read_proc},
{"uptime", uptime_read_proc},
{"meminfo", meminfo_read_proc},
{"version", version_read_proc},
#ifdef CONFIG_PROC_HARDWARE
{"hardware", hardware_read_proc},
#endif
#ifdef CONFIG_STRAM_PROC
{"stram", stram_read_proc},
#endif
{"filesystems", filesystems_read_proc},
{"cmdline", cmdline_read_proc},
{"execdomains", execdomains_read_proc},
{NULL,}
};
for (p = simple_ones; p->name; p++)
create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);//我们就分析这个函数,其它函数大同小异[luther.gliethttp]
...
}
//在'物理磁盘'上创建'物理文件',然后替换为我们这里指定的读方法
static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void * data)
{
struct proc_dir_entry *res=create_proc_entry(name,mode,base);
if (res) {
res->read_proc=read_proc;//替换为我们期望的,指定的读方法
res->data=data;
}
return res;
}
//在parent"物理存储空间"下创建一个新的"物理文件"[luther.gliethttp]
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent;
nlink_t nlink;
if (S_ISDIR(mode)) {//目录
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO | S_IXUGO;
nlink = 2;
} else {
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;//普通文件
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO;
nlink = 1;
}
ent = __proc_create(&parent, name, mode, nlink);//检查,创建,同时获得parent的"物理存储空间"信息
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
ent = NULL;
}
}
return ent;
}
static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
const char *name,
mode_t mode,
nlink_t nlink)
{
struct proc_dir_entry *ent = NULL;
const char *fn = name;
int len;
/* make sure name is valid */
if (!name || !strlen(name)) goto out;
if (!(*parent) && xlate_proc_name(name, parent, &fn) != 0)
goto out;
/* At this point there must not be any '/' characters beyond *fn */
if (strchr(fn, '/'))//fn为最终需要创建的文件名,所以不会存在'/'字符
goto out;
len = strlen(fn);
ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
//proc_dir_entry结构体的记下来内存用来存放这个'物理文件'的文件名[luther.gliethttp].
if (!ent) goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);//拷贝name文件名,到这个'物理磁盘文件存储空间'
ent->name = ((char *) ent) + sizeof(*ent);//name紧邻proc_dir_entry结构体之后的剩余内存
//所以这样就导致了不能使用定大小的slab对象方式来以着色手段快速和基本不浪费内存的方式获取结构体,
//所以不论从速度还是内存利用率上proc生成文件都是低效的[luther.gliethttp]
ent->namelen = len;
ent->mode = mode;//填充dp->mode该'物理文件'的文件模式属性[luther.gliethttp]
ent->nlink = nlink;
atomic_set(&ent->count, 1);
ent->pde_users = 0;
spin_lock_init(&ent->pde_unload_lock);
ent->pde_unload_completion = NULL;
out:
return ent;
}
//这是一个从proc_root开始遍历所有子目录的过程函数,以下纯属'物理磁盘'性质的操作,所以和inode没有产生任何关系,
//将在proc_lookup函数中来建立这种'物理磁盘文件'和inode之间一一对应的映射关联[luther.gliethttp]
static int xlate_proc_name(const char *name,
struct proc_dir_entry **ret, const char **residual)
{
const char *cp = name, *next;
struct proc_dir_entry *de;
int len;
int rtn = 0;
spin_lock(&proc_subdir_lock);
de = &proc_root;//如果name本来就没有'/'路径,那么将直接将proc_root作为parent返回
while (1) {
next = strchr(cp, '/');
if (!next)
break;//查到最后一个或者根本就没有'/'绝对路径
len = next - cp;
for (de = de->subdir; de ; de = de->next) {
if (proc_match(len, cp, de))//简单的比较文件名是否相同
break;
}
//执行到这里说明是以'/'开头的绝对路径,所以必须保证每一个父目录的存在.
if (!de) {
rtn = -ENOENT;//尝试在一个缺失父目录的路径下创建文件,错误返回[luther.gliethttp]
goto out;
}
cp += len + 1;//继续查找进一层的parent父目录在这个"物理存储系统"上是否存在,我们期望它存在.
}
*residual = cp;//返回路径的最后文件名,比如:'luther/gliethttp',那么如果luther存在,那么cp等于'gliethttp'
*ret = de;//比如:'luther/gliethttp',需要生成的gliethttp文件的parent为de,也就是'luther'这个'物理存储'文件.
out:
spin_unlock(&proc_subdir_lock);
return rtn;
}
static int proc_match(int len, const char *name, struct proc_dir_entry *de)
{
if (de->namelen != len)
return 0;
return !memcmp(name, de->name, len);
}
//维护'物理磁盘文件'之间的关联图谱[luther.gliethttp]
static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
unsigned int i;
struct proc_dir_entry *tmp;
i = get_inode_number();//从radix树上获得一个顺序生成的唯一整数,作为inode
if (i == 0)
return -EAGAIN;
dp->low_ino = i;//inode节点号
//根据mode来填充操作该'物理磁盘文件'的操作函数集,前提是dp没有自定义自己的操作函数处理集[luther.gliethttp]
if (S_ISDIR(dp->mode)) {
if (dp->proc_iops == NULL) {//没指定
dp->proc_fops = &proc_dir_operations;
dp->proc_iops = &proc_dir_inode_operations;
}
dir->nlink++;
} else if (S_ISLNK(dp->mode)) {
if (dp->proc_iops == NULL)//没指定
dp->proc_iops = &proc_link_inode_operations;
} else if (S_ISREG(dp->mode)) {
if (dp->proc_fops == NULL)//没指定
dp->proc_fops = &proc_file_operations;
if (dp->proc_iops == NULL)//没指定
dp->proc_iops = &proc_file_inode_operations;
}
spin_lock(&proc_subdir_lock);
for (tmp = dir->subdir; tmp; tmp = tmp->next)
//遍历dir目录,保证dp文件不存在'物理磁盘'上.
if (strcmp(tmp->name, dp->name) == 0) {
printk(KERN_WARNING "proc_dir_entry '%s' already "
"registered\n", dp->name);
dump_stack();
break;
}
//将dp这个'物理文件'添加到'物理磁盘'的文件视图链表中,以供以后查找访问[luther.gliethttp]
dp->next = dir->subdir;
dp->parent = dir;
dir->subdir = dp;
spin_unlock(&proc_subdir_lock);
return 0;
}
就这样我们成功的向proc这个内存方式的'物理磁盘'上,添加了我们需要的'物理文件',同时细化了该'物理文件'排它的个性化信息[luther.gliethttp]
====================================================
//在'物理磁盘'上创建'物理文件',然后替换为我们这里指定的proc_fops方法
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件',完全由mode指定[luther.gliethttp]
void create_seq_entry(char *name, mode_t mode, const struct file_operations *f)
{
struct proc_dir_entry *entry;
entry = create_proc_entry(name, mode, NULL);
if (entry)
entry->proc_fops = f;
}
====================================================
//固定方法,创建目录型的'物理文件'
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent)
{
return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
}
truct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent;
ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
ent = NULL;
}
}
return ent;
}
====================================================
//可以用来在'物理磁盘'上创建文件型的'物理文件'或者目录型的'物理文件'
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent;
nlink_t nlink;
if (S_ISDIR(mode)) {
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO | S_IXUGO;
nlink = 2;
} else {
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO;
nlink = 1;
}
ent = __proc_create(&parent, name, mode, nlink);
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
ent = NULL;
}
}
return ent;
}
|