Linux系统 proc self,Linux内核源代码情形分析-特殊文件系统/proc-对/proc/self/cwd的访问...
Linux內(nèi)核源代碼情景分析-特殊文件系統(tǒng)/proc-對/proc/self/cwd的訪問
繼上篇文章Linux內(nèi)核源代碼情景分析-特殊文件系統(tǒng)/proc,我們對/proc/loadavg訪問后,這篇文章是對/proc/self/cwd的訪問。
int __user_walk(const char *name, unsigned flags, struct nameidata *nd)
{
char *tmp;
int err;
tmp = getname(name);//在系統(tǒng)空間分配一個頁面,并從用戶空間把文件名復(fù)制到這個頁面
err = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
err = 0;
if (path_init(tmp, flags, nd))
err = path_walk(tmp, nd);
putname(tmp);
}
return err;
}? ? name就為/proc/self/cwd,重要分析下path_walk函數(shù),請參考Linux內(nèi)核源代碼情景分析-從路徑名到目標(biāo)節(jié)點。
第一次循環(huán)path_walk發(fā)現(xiàn)/proc是個安裝節(jié)點而通過_follow_down找到了proc文件系統(tǒng)的根節(jié)點的dentry結(jié)構(gòu),nameidata結(jié)構(gòu)中的指針dentry指向這個數(shù)據(jù)結(jié)構(gòu)。、
第二次循環(huán)搜索路徑名中的下一個節(jié)點self,由于這個節(jié)點并不是路徑名的最后一個節(jié)點,所以執(zhí)行的代碼如下:
dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//在內(nèi)存中尋找該節(jié)點業(yè)已建立的dentry結(jié)構(gòu)
if (!dentry) {//如果沒有找到
dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//那么就要建立該節(jié)點的dentry結(jié)構(gòu)
err = PTR_ERR(dentry);
if (IS_ERR(dentry))
break;
}? ? 參考Linux內(nèi)核源代碼情景分析-特殊文件系統(tǒng)/proc,最終也要通過proc_root_lookup()調(diào)用proc_lookup(),試圖為節(jié)點建立起其dentry結(jié)構(gòu)和inode結(jié)構(gòu)。可是由于/proc/self并沒有一個固定的proc_dir_entry結(jié)構(gòu),所以對proc_lookup()的調(diào)用必然會失敗,因而會進(jìn)一步調(diào)用proc_pid_lookup(),代碼如下:
static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
{
if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */
int nlink = proc_root.nlink;
nlink += nr_threads;
dir->i_nlink = nlink;
}
if (!proc_lookup(dir, dentry))///由于/proc/self并沒有一個固定的proc_dir_entry結(jié)構(gòu),所以對proc_lookup()的調(diào)用必然會失敗
return NULL;
return proc_pid_lookup(dir, dentry);//會調(diào)用這個函數(shù)
}struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode *inode;
struct proc_dir_entry * de;
int error;
error = -ENOENT;
inode = NULL;
de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) {//找不到/proc/self節(jié)點
for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino)
continue;
if (de->namelen != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino;
error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
break;
}
}
}
if (inode) {
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
return NULL;
}
return ERR_PTR(error);//返回錯誤碼
}
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry)
{
unsigned int pid, c;
struct task_struct *task;
const char *name;
struct inode *inode;
int len;
pid = 0;
name = dentry->d_name.name;
len = dentry->d_name.len;
if (len == 4 && !memcmp(name, "self", 4)) {//執(zhí)行這里,name等于self
inode = new_inode(dir->i_sb);
if (!inode)
return ERR_PTR(-ENOMEM);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_ino = fake_ino(0, PROC_PID_INO);
inode->u.proc_i.file = NULL;
inode->u.proc_i.task = NULL;
inode->i_mode = S_IFLNK|S_IRWXUGO;
inode->i_uid = inode->i_gid = 0;
inode->i_size = 64;
inode->i_op = &proc_self_inode_operations;
d_add(dentry, inode);
return NULL;//返回了
}
while (len-- > 0) {
c = *name - '0';
name++;
if (c > 9)
goto out;
if (pid >= MAX_MULBY10)
goto out;
pid *= 10;
pid += c;
if (!pid)
goto out;
}
read_lock(&tasklist_lock);
task = find_task_by_pid(pid);
if (task)
get_task_struct(task);
read_unlock(&tasklist_lock);
if (!task)
goto out;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_INO);
free_task_struct(task);
if (!inode)
goto out;
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_base_inode_operations;
inode->i_fop = &proc_base_operations;
inode->i_nlink = 3;
inode->i_flags|=S_IMMUTABLE;
dentry->d_op = &pid_base_dentry_operations;
d_add(dentry, inode);
return NULL;
out:
return ERR_PTR(-ENOENT);
}? ? 其中proc_self_inode_operations結(jié)構(gòu)定義如下:
static struct inode_operations proc_self_inode_operations = {
readlink:proc_self_readlink,
follow_link:proc_self_follow_link,
};? ? 還是第二輪循環(huán),從proc_root_lookup返回到path_walk中以后,接著要檢查和處理兩件事,第一件是新找到的節(jié)點是否為安裝點;第二件就是它是否是一個連接節(jié)點。這正是我們在這里所關(guān)心的,因為/proc/self就是個連接節(jié)點。繼續(xù)看path_walk,代碼如下:
if (inode->i_op->follow_link) {//看看這個指針是否為NULL,這個指針是在ext2_read_inode中設(shè)置的
err = do_follow_link(dentry, nd);static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)//參數(shù)dentry為proc/self節(jié)點的dentry結(jié)構(gòu)
{
int err;
if (current->link_count >= 8)
goto loop;
current->link_count++;
UPDATE_ATIME(dentry->d_inode);
err = dentry->d_inode->i_op->follow_link(dentry, nd);//proc_self_follow_link
current->link_count--;
return err;
loop:
path_release(nd);
return -ELOOP;
}? ??entry->d_inode->i_op->follow_link指向proc_self_follow_link,代碼如下:
static int proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
{
char tmp[30];
sprintf(tmp, "%d", current->pid);
return vfs_follow_link(nd,tmp);
}int vfs_follow_link(struct nameidata *nd, const char *link)
{
return __vfs_follow_link(nd, link);
}static inline int
__vfs_follow_link(struct nameidata *nd, const char *link)
{
int res = 0;
char *name;
if (IS_ERR(link))
goto fail;
if (*link == '/') {
path_release(nd);
if (!walk_init_root(link, nd))
/* weird __emul_prefix() stuff did it */
goto out;
}
res = path_walk(link, nd);
out:
if (current->link_count || res || nd->last_type!=LAST_NORM)
return res;
/*
* If it is an iterative symlinks resolution in open_namei() we
* have to copy the last component. And all that crap because of
* bloody create() on broken symlinks. Furrfu...
*/
name = __getname();
if (IS_ERR(name))
goto fail_name;
strcpy(name, nd->last.name);
nd->last.name = name;
return 0;
fail_name:
link = name;
fail:
path_release(nd);
return PTR_ERR(link);
}? ? 在__vfs_follow_link()中會調(diào)用path_walk()來尋找連接的目標(biāo)節(jié)點,所以又會調(diào)用其父節(jié)點/proc的lookup函數(shù),即proc_root_lookup(),不同的只是這次尋找的不是"self",而是當(dāng)前進(jìn)程的pid字符串。
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry)
{
unsigned int pid, c;
struct task_struct *task;
const char *name;
struct inode *inode;
int len;
pid = 0;
name = dentry->d_name.name;
len = dentry->d_name.len;
if (len == 4 && !memcmp(name, "self", 4)) {//不執(zhí)行這里,name不等于self
inode = new_inode(dir->i_sb);
if (!inode)
return ERR_PTR(-ENOMEM);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_ino = fake_ino(0, PROC_PID_INO);
inode->u.proc_i.file = NULL;
inode->u.proc_i.task = NULL;
inode->i_mode = S_IFLNK|S_IRWXUGO;
inode->i_uid = inode->i_gid = 0;
inode->i_size = 64;
inode->i_op = &proc_self_inode_operations;
d_add(dentry, inode);
return NULL;//返回了
}
while (len-- > 0) {//執(zhí)行這里
c = *name - '0';
name++;
if (c > 9)
goto out;
if (pid >= MAX_MULBY10)
goto out;
pid *= 10;
pid += c;
if (!pid)
goto out;
}
read_lock(&tasklist_lock);
task = find_task_by_pid(pid);
if (task)
get_task_struct(task);
read_unlock(&tasklist_lock);
if (!task)
goto out;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_INO);
free_task_struct(task);
if (!inode)
goto out;
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_base_inode_operations;//注意這個指針,一會會用到
inode->i_fop = &proc_base_operations;
inode->i_nlink = 3;
inode->i_flags|=S_IMMUTABLE;
dentry->d_op = &pid_base_dentry_operations;
d_add(dentry, inode);
return NULL;
out:
return ERR_PTR(-ENOENT);
}
static struct inode_operations proc_base_inode_operations = {
lookup:proc_base_lookup,
};
proc_pid_make_inode,為進(jìn)程創(chuàng)建一個inode結(jié)構(gòu)static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino)
{
struct inode * inode;
/* We need a new inode */
inode = new_inode(sb);
if (!inode)
goto out;
/* Common stuff */
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_ino = fake_ino(task->pid, ino);
inode->u.proc_i.file = NULL;
/*
* grab the reference to task.
*/
inode->u.proc_i.task = task;//當(dāng)前進(jìn)程的task賦值到這里
get_task_struct(task);
if (!task->p_pptr)
goto out_unlock;
inode->i_uid = 0;
inode->i_gid = 0;
if (ino == PROC_PID_INO || task->dumpable) {
inode->i_uid = task->euid;
inode->i_gid = task->egid;
}
out:
return inode;
out_unlock:
iput(inode);
return NULL;
}
從path_walk返回后,nd->dentry已指向代表著當(dāng)前進(jìn)程的目錄節(jié)點的dentry結(jié)構(gòu),之后層層返回到proc_self_follow_link,最后返回到主path_walk的第二次循環(huán)中,開始執(zhí)行第三次循環(huán)。
第三次循環(huán),最后一個節(jié)點是"cwd",這一次所搜索的節(jié)點已經(jīng)是路徑名中的最后一個節(jié)點,所以轉(zhuǎn)到last_component的地方,同樣也是在real_lookup()中通過父節(jié)點的inode_operations結(jié)構(gòu)中的lookup函數(shù)指針執(zhí)行實際的操作,也就是proc_base_lookup,代碼如下:
static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
{
struct inode *inode;
int error;
struct task_struct *task = dir->u.proc_i.task;//取出當(dāng)前進(jìn)程的task
struct pid_entry *p;
error = -ENOENT;
inode = NULL;
for (p = base_stuff; p->name; p++) {//在base_stuff中找到cwd,關(guān)于base_stuff如下面所示
if (p->len != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, p->name, p->len))
break;
}
if (!p->name)
goto out;
error = -EINVAL;
inode = proc_pid_make_inode(dir->i_sb, task, p->type);//p->type為5
if (!inode)
goto out;
inode->i_mode = p->mode;
/*
* Yes, it does not scale. And it should not. Don't add
* new entries into /proc// without very good reasons.
*/
switch(p->type) {
case PROC_PID_FD:
inode->i_nlink = 2;
inode->i_op = &proc_fd_inode_operations;
inode->i_fop = &proc_fd_operations;
break;
case PROC_PID_EXE:
inode->i_op = &proc_pid_link_inode_operations;
inode->u.proc_i.op.proc_get_link = proc_exe_link;
break;
case PROC_PID_CWD:
inode->i_op = &proc_pid_link_inode_operations;//兩者很重要
inode->u.proc_i.op.proc_get_link = proc_cwd_link;//兩者很重要,inode->u.proc_i指向了proc_inode_info,結(jié)構(gòu)如下面所示
break;
case PROC_PID_ROOT:
inode->i_op = &proc_pid_link_inode_operations;
inode->u.proc_i.op.proc_get_link = proc_root_link;
break;
case PROC_PID_ENVIRON:
inode->i_fop = &proc_info_file_operations;
inode->u.proc_i.op.proc_read = proc_pid_environ;
break;
case PROC_PID_STATUS:
inode->i_fop = &proc_info_file_operations;
inode->u.proc_i.op.proc_read = proc_pid_status;
break;
case PROC_PID_STAT:
inode->i_fop = &proc_info_file_operations;
inode->u.proc_i.op.proc_read = proc_pid_stat;
break;
case PROC_PID_CMDLINE:
inode->i_fop = &proc_info_file_operations;
inode->u.proc_i.op.proc_read = proc_pid_cmdline;
break;
case PROC_PID_STATM:
inode->i_fop = &proc_info_file_operations;
inode->u.proc_i.op.proc_read = proc_pid_statm;
break;
case PROC_PID_MAPS:
inode->i_fop = &proc_maps_operations;
break;
#ifdef CONFIG_SMP
case PROC_PID_CPU:
inode->i_fop = &proc_info_file_operations;
inode->u.proc_i.op.proc_read = proc_pid_cpu;
break;
#endif
case PROC_PID_MEM:
inode->i_op = &proc_mem_inode_operations;
inode->i_fop = &proc_mem_operations;
break;
default:
printk("procfs: impossible type (%d)",p->type);
iput(inode);
return ERR_PTR(-EINVAL);
}
dentry->d_op = &pid_dentry_operations;
d_add(dentry, inode);
return NULL;
out:
return ERR_PTR(error);
}struct pid_entry {
int type;
int len;
char *name;
mode_t mode;
};
enum pid_directory_inos {
PROC_PID_INO = 2,
PROC_PID_STATUS,
PROC_PID_MEM,
PROC_PID_CWD,
PROC_PID_ROOT,
PROC_PID_EXE,
PROC_PID_FD,
PROC_PID_ENVIRON,
PROC_PID_CMDLINE,
PROC_PID_STAT,
PROC_PID_STATM,
PROC_PID_MAPS,
PROC_PID_CPU,
PROC_PID_FD_DIR = 0x8000,/* 0x8000-0xffff */
};
#define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)}
static struct pid_entry base_stuff[] = {
E(PROC_PID_FD,"fd",S_IFDIR|S_IRUSR|S_IXUSR),
E(PROC_PID_ENVIRON,"environ",S_IFREG|S_IRUSR),
E(PROC_PID_STATUS,"status",S_IFREG|S_IRUGO),
E(PROC_PID_CMDLINE,"cmdline",S_IFREG|S_IRUGO),
E(PROC_PID_STAT,"stat",S_IFREG|S_IRUGO),
E(PROC_PID_STATM,"statm",S_IFREG|S_IRUGO),
#ifdef CONFIG_SMP
E(PROC_PID_CPU,"cpu",S_IFREG|S_IRUGO),
#endif
E(PROC_PID_MAPS,"maps",S_IFREG|S_IRUGO),
E(PROC_PID_MEM,"mem",S_IFREG|S_IRUSR|S_IWUSR),
E(PROC_PID_CWD,"cwd",S_IFLNK|S_IRWXUGO),
E(PROC_PID_ROOT,"root",S_IFLNK|S_IRWXUGO),
E(PROC_PID_EXE,"exe",S_IFLNK|S_IRWXUGO),
{0,0,NULL,0}
};
#undef Estruct proc_inode_info {
struct task_struct *task;
int type;
union {
int (*proc_get_link)(struct inode *, struct dentry **, struct vfsmount **);
int (*proc_read)(struct task_struct *task, char *page);
} op;
struct file *file;
};? ? 從proc_base_lookup()經(jīng)由real_lookup()返回到path_walk時,返回值dentry已經(jīng)指向了這個特定"cwd"節(jié)點dentry結(jié)構(gòu)。但是接著同樣要受到對其Inode結(jié)構(gòu)中的i_op指針以及相應(yīng)inode_operations結(jié)構(gòu)的指針follow_link的檢驗,看path_walk的代碼:
inode = dentry->d_inode;
if ((lookup_flags & LOOKUP_FOLLOW)//和第一次和第二次循環(huán)不同,必須LOOKUP_FOLLOW標(biāo)志位置1
&& inode && inode->i_op && inode->i_op->follow_link) {
err = do_follow_link(dentry, nd);static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
{
int err;
if (current->link_count >= 8)
goto loop;
current->link_count++;
UPDATE_ATIME(dentry->d_inode);
err = dentry->d_inode->i_op->follow_link(dentry, nd);
current->link_count--;
return err;
loop:
path_release(nd);
return -ELOOP;
}
dentry->d_inode->i_op指向了proc_pid_link_inode_operations結(jié)構(gòu),是在這里設(shè)置的:
case PROC_PID_CWD:
inode->i_op = &proc_pid_link_inode_operations;//兩者很重要
inode->u.proc_i.op.proc_get_link = proc_cwd_link;//兩者很重要,inode->u.proc_i指向了proc_inode_info結(jié)構(gòu)static struct inode_operations proc_pid_link_inode_operations = {
readlink:proc_pid_readlink,
follow_link:proc_pid_follow_link
};? ? ?dentry->d_inode->i_op->follow_link(dentry, nd),proc_pid_follow_link(dentry, nd),也就是代碼如下:
static int proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)//參數(shù)dentry為"cwd"節(jié)點dentry結(jié)構(gòu)
{
struct inode *inode = dentry->d_inode;
int error = -EACCES;
/* We don't need a base pointer in the /proc filesystem */
path_release(nd);
if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
goto out;
error = proc_check_root(inode);
if (error)
goto out;
error = inode->u.proc_i.op.proc_get_link(inode, &nd->dentry, &nd->mnt);//也就是proc_cwd_link
nd->last_type = LAST_BIND;
out:
return error;
}? ??inode->u.proc_i.op.proc_get_link(inode, &nd->dentry, &nd->mnt),也就是proc_cwd_link(inode, &nd->dentry, &nd->mnt),代碼如下:
static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
{
struct fs_struct *fs;
int result = -ENOENT;
task_lock(inode->u.proc_i.task);
fs = inode->u.proc_i.task->fs;//task指向相應(yīng)進(jìn)程的task_struct結(jié)構(gòu),進(jìn)而可以得到這個進(jìn)程的fs_struct結(jié)構(gòu)
if(fs)
atomic_inc(&fs->count);
task_unlock(inode->u.proc_i.task);
if (fs) {
read_lock(&fs->lock);
*mnt = mntget(fs->pwdmnt);//nd->mnt指向了該目錄所在設(shè)備安裝時的vfsmount結(jié)構(gòu)
*dentry = dget(fs->pwd);//nd->dentry指向了該進(jìn)程的"當(dāng)前工作目錄"的dentry結(jié)構(gòu)
read_unlock(&fs->lock);
result = 0;
put_fs_struct(fs);
}
return result;
}? ? 當(dāng)從proc_cwd_link()經(jīng)由do_follow_link()返回到path_walk()中時,nameidata結(jié)構(gòu)中指針已經(jīng)指向最終的目標(biāo),即當(dāng)前進(jìn)程的當(dāng)前工作目錄。
也就是:
int __user_walk(const char *name, unsigned flags, struct nameidata *nd)
{
char *tmp;
int err;
tmp = getname(name);//在系統(tǒng)空間分配一個頁面,并從用戶空間把文件名復(fù)制到這個頁面
err = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
err = 0;
if (path_init(tmp, flags, nd))
err = path_walk(tmp, nd);
putname(tmp);
}
return err;
}? ?此時nd->mnt指向了該目錄所在設(shè)備安裝時的vfsmount結(jié)構(gòu),nd->dentry指向了該進(jìn)程的"當(dāng)前工作目錄"的dentry結(jié)構(gòu)。
總結(jié)
以上是生活随笔為你收集整理的Linux系统 proc self,Linux内核源代码情形分析-特殊文件系统/proc-对/proc/self/cwd的访问...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux命令菜鸟ping,Linux
- 下一篇: linux培训机构 网络班,Linux基