/proc/<pid>
在/proc目录下,除了/proc/sys、/proc/meminfo、/proc/slabinfo等系统级信息之外,系统中所有的存在的进程都会在这个目录下存在一个目录,而里面则有大量的进程级的信息;这些文件和目录都是什么时候创建的呢?
proc文件系统
proc文件系统是什么时候挂载的呢?
struct pid *alloc_pid(struct pid_namespace *ns)
---
pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
...
tmp = ns;
pid->level = ns->level;
for (i = ns->level; i >= 0; i--) {
int pid_min = 1;
// 会在每个命名空间都申请一个pid
nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min,
pid_max, GFP_ATOMIC);
...
tmp = tmp->parent;
}
// 这个命名空间的1号进程就是child reaper
if (unlikely(is_child_reaper(pid))) {
if (pid_ns_prepare_proc(ns))
goto out_free;
}
---
当某个pid命名空间的1号进程被创建时,或者更加准确的说,1号pid被申请时,会执行执行一次proc目录的挂载;换句话说,每个pid命名空间都有一个独立视角的proc目录。proc文件系统的卸载是在该namespace的pid都释放了之后,通过kwork进行的,参考free_pid()。
通过proc文件系统的root目录的file和inode方法,如下:
static const struct file_operations proc_root_operations = {
.read = generic_read_dir,
.iterate_shared = proc_root_readdir,
.llseek = generic_file_llseek,
};
/*
* proc root can do almost nothing..
*/
static const struct inode_operations proc_root_inode_operations = {
.lookup = proc_root_lookup,
.getattr = proc_root_getattr,
};
我们知道,他们只能对它做两件事,即目录遍历(ls)和目录查询(cd、cat等);
/proc/<pid>哪来的?
先说结论,/proc/<pid>并不是进程创建就有的,而是通过proc_pid_instantiate()在需要的时候创建的;那么什么时候需要?readdir和lookup,参考:
proc_pid_lookup()
---
ns = dentry->d_sb->s_fs_info;
rcu_read_lock();
task = find_task_by_pid_ns(tgid, ns);
if (task)
get_task_struct(task);
rcu_read_unlock();
if (!task)
goto out;
result = proc_pid_instantiate(dentry, task, NULL);
put_task_struct(task);
---
proc_pid_readdir()
---
if (pos == TGID_OFFSET - 2) {
struct inode *inode = d_inode(ns->proc_self);
if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK))
return 0;
ctx->pos = pos = pos + 1;
}
if (pos == TGID_OFFSET - 1) {
struct inode *inode = d_inode(ns->proc_thread_self);
if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK))
return 0;
ctx->pos = pos = pos + 1;
}
iter.tgid = pos - TGID_OFFSET;
iter.task = NULL;
for (iter = next_tgid(ns, iter);
iter.task;
iter.tgid += 1, iter = next_tgid(ns, iter)) {
...
len = snprintf(name, sizeof(name), "%u", iter.tgid);
ctx->pos = iter.tgid + TGID_OFFSET;
if (!proc_fill_cache(file, ctx, name, len,
proc_pid_instantiate, iter.task, NULL)) {
put_task_struct(iter.task);
return 0;
}
}
---
当我们去遍历/proc或者查询某个pid的目录时,其相关的inode节点会在那个时候被创建;以此类似,/proc/<pid>/下面的目录和文件也是一样方式;参考函数,proc_pident_lookup()和proc_pident_instantiate()。
/proc/<pid>哪去了?
do_exit()
-> exit_notify()
-> release_task()
-> do_task_dead()
release_task()
-> proc_flush_task()
-> proc_flush_task_mnt()
---
name.name = buf;
name.len = snprintf(buf, sizeof(buf), "%u", pid);
/* no ->d_hash() rejects on procfs */
dentry = d_hash_and_lookup(mnt->mnt_root, &name);
if (dentry) {
d_invalidate(dentry);
dput(dentry);
}
---