我们知道,Linux把设备看成特殊的文件,称为设备文件。在操作文件之前,首先必须打开文件,打开文件的函数是通过open系统调用来实现的。而简单的文件打开操作,在Linux内核实现却是非常的复杂。open函数打开原理就是将进程files_struct结构体和文件对象file相关联。那么具体是怎么实现的呢?让我们一起走进Linux内核文件打开流程。
2. 首先,通过系统调用sys_open函数:
在第7行,得到当前进程的files指针。 在第16-18行,返回打开文件表,在打开的文件描述符集open_ds的fds_bits数组查找对应位置为0的位图,返回位置,表示这个文件描述符没有被使用。接下来,在41-43行,分别将open_fds的fd位置的位图置位,并将fd+1赋值给下一下文件描述符。如果这个文件描述符被占用,就将fdt->fd[fd]=NULL. 最后返回文件描述符fd.
http://blog.csdn.net/chenjin_zhong/article/details/8452487http://blog.csdn.net/chenjin_zhong/article/details/8452487
http://blog.csdn.net/chenjin_zhong/article/details/8452487
1. 书结上文,继续分析do_filp_open函数,其传入4个参数:
dfd:相对目录
tmp:文件路径名,例如要打开/usr/src/kernels/linux-2.6.30
flags:打开标志
mode:打开模式
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- static struct file *do_filp_open(int dfd, const char *filename, int flags,
- int mode)
- {
-
- int namei_flags, error;
-
- struct nameidata nd;
- namei_flags = flags;
- if ((namei_flags+1) & O_ACCMODE)
- namei_flags++;
-
- error = open_namei(dfd, filename, namei_flags, mode, &nd);
-
- if (!error)
- return nameidata_to_filp(&nd, flags);
-
- return ERR_PTR(error);
- }
初看起来,寥寥几行代码,貌似简单。其实不然,一会就知道了。此函数调用了open_namei和nameidata_to_filp. 后一个函数通过名字就可以猜出来,是将nameidata结构转化为filp,也就是利用nd结构赋值给文件指针file,然后返回这个文件指针。而open_namei肯定是填充nd结构体,具体功能可表述为: 根据上级目录项对象,查询下一级的目录项对象,如果在目录项缓存找到下一级的目录项对象,则直接返回,并填充nd的挂载点对象和目录项对象。否则,构建一个子目录项对象,并利用iget函数分配一个新的inode结构,将子目录项对象和inode结构相关联。这样,一直循环到最后一下分量。最后返回的是最后一个分量的目录项对象和挂载点对象。可以看到,在这两个函数中,都利用了nameidata结构,具体看一下神奇的结构:
- struct nameidata {
- struct dentry *dentry;
- struct vfsmount *mnt;
- struct qstr last;
- unsigned int flags;
- int last_type;
- unsigned depth;
- char *saved_names[MAX_NESTED_LINKS + 1];
-
-
- union {
- struct open_intent open;
- } intent;
- };
- struct open_intent {
- int flags;
- int create_mode;
- struct file *file;
- };
open_intent文件对象就是最后返回的文件对象。
由于namidata_to_filp比较简单,先看一下:
-
-
-
-
-
-
-
- struct file *nameidata_to_filp(struct nameidata *nd, int flags)
- {
- struct file *filp;
-
-
-
- filp = nd->intent.open.file;
-
-
- if (filp->f_path.dentry == NULL)
- filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
- else
- path_release(nd);
- return filp;
- }
首先取得文件对象指针,然后判断文件对象是否已经初始化,如果没有初始化,就调用__dentry_open函数,对文件对象进行初始化。
-
- static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
- int flags, struct file *f,
- int (*open)(struct inode *, struct file *))
- {
- struct inode *inode;
- int error;
-
- f->f_flags = flags;
- f->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK |
- FMODE_PREAD | FMODE_PWRITE;
-
- inode = dentry->d_inode;
- if (f->f_mode & FMODE_WRITE) {
- error = get_write_access(inode);
- if (error)
- goto cleanup_file;
- }
-
- f->f_mapping = inode->i_mapping;
-
- f->f_path.dentry = dentry;
-
- f->f_path.mnt = mnt;
-
- f->f_pos = 0;
-
- f->f_op = fops_get(inode->i_fop);
- file_move(f, &inode->i_sb->s_files);
-
- if (!open && f->f_op)
- open = f->f_op->open;
-
- if (open) {
- error = open(inode, f);
- if (error)
- goto cleanup_all;
- }
-
- f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
-
- file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
-
-
- if (f->f_flags & O_DIRECT) {
- if (!f->f_mapping->a_ops ||
- ((!f->f_mapping->a_ops->direct_IO) &&
- (!f->f_mapping->a_ops->get_xip_page))) {
- fput(f);
- f = ERR_PTR(-EINVAL);
- }
- }
-
- return f;
-
- cleanup_all:
- fops_put(f->f_op);
- if (f->f_mode & FMODE_WRITE)
- put_write_access(inode);
- file_kill(f);
- f->f_path.dentry = NULL;
- f->f_path.mnt = NULL;
- cleanup_file:
- put_filp(f);
- dput(dentry);
- mntput(mnt);
- return ERR_PTR(error);
- }
首先,设置文件打开标志f->f_flags. 然后初始化地址空间对象,目录项对象,挂载点对象,文件指针位置,文件相关操作。需要说明两点:
(1)地址空间对象和索引节点相关联,在构建索引节点时已经赋值了。它涉及到具体的磁盘块操作,在后面的章节将会解释。
(2)f_op这个非常重要,也是在构建索引节点时,将具体文件系统的文件操作函数集的指针赋给索引节点的i_fop域。对于打开文件,目录,符号链接,对应的操作函数集是不相同的。
接下来,第31行-38行,如果是普通文件,可能不需要打开。如果是设备文件,就需要打开操作。例如SCSI设备的sg_open函数。
最后,对文件预读进行初始化。
在说完nameidata_to_filp函数之后,需要解释open_namei函数:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int open_namei(int dfd, const char *pathname, int flag,
- int mode, struct nameidata *nd)
- {
- int acc_mode, error;
-
- struct path path;
- struct dentry *dir;
- int count = 0;
-
- acc_mode = ACC_MODE(flag);
-
-
-
- if (flag & O_TRUNC)
- acc_mode |= MAY_WRITE;
-
-
-
- if (flag & O_APPEND)
- acc_mode |= MAY_APPEND;
-
-
-
-
-
- if (!(flag & O_CREAT)) {
- error = path_lookup_open(dfd, pathname, lookup_flags(flag),
- nd, flag);
- if (error)
- return error;
- goto ok;
- }
-
-
-
-
-
-
- error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
- if (error)
- return error;
-
-
-
-
-
-
- error = -EISDIR;
- if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
- goto exit;
-
- dir = nd->dentry;
- nd->flags &= ~LOOKUP_PARENT;
- mutex_lock(&dir->d_inode->i_mutex);
-
- path.dentry = lookup_hash(nd);
- path.mnt = nd->mnt;
-
- do_last:
- error = PTR_ERR(path.dentry);
- if (IS_ERR(path.dentry)) {
- mutex_unlock(&dir->d_inode->i_mutex);
- goto exit;
- }
-
- if (IS_ERR(nd->intent.open.file)) {
- mutex_unlock(&dir->d_inode->i_mutex);
- error = PTR_ERR(nd->intent.open.file);
- goto exit_dput;
- }
-
-
-
- if (!path.dentry->d_inode) {
-
- error = open_namei_create(nd, &path, flag, mode);
- if (error)
- goto exit;
- return 0;
- }
-
-
-
-
- mutex_unlock(&dir->d_inode->i_mutex);
- audit_inode_update(path.dentry->d_inode);
-
- error = -EEXIST;
- if (flag & O_EXCL)
- goto exit_dput;
-
- if (__follow_mount(&path)) {
- error = -ELOOP;
- if (flag & O_NOFOLLOW)
- goto exit_dput;
- }
-
- error = -ENOENT;
- if (!path.dentry->d_inode)
- goto exit_dput;
- if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
- goto do_link;
-
- path_to_nameidata(&path, nd);
- error = -EISDIR;
- if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
- goto exit;
- ok:
- error = may_open(nd, acc_mode, flag);
- if (error)
- goto exit;
- return 0;
-
- exit_dput:
- dput_path(&path, nd);
- exit:
- if (!IS_ERR(nd->intent.open.file))
- release_open_intent(nd);
- path_release(nd);
- return error;
-
- do_link:
- error = -ELOOP;
- if (flag & O_NOFOLLOW)
- goto exit_dput;
-
-
-
-
-
-
-
-
-
-
- nd->flags |= LOOKUP_PARENT;
- error = security_inode_follow_link(path.dentry, nd);
- if (error)
- goto exit_dput;
- error = __do_follow_link(&path, nd);
- if (error) {
-
-
-
-
- release_open_intent(nd);
- return error;
- }
- nd->flags &= ~LOOKUP_PARENT;
- if (nd->last_type == LAST_BIND)
- goto ok;
- error = -EISDIR;
- if (nd->last_type != LAST_NORM)
- goto exit;
- if (nd->last.name[nd->last.len]) {
- __putname(nd->last.name);
- goto exit;
- }
- error = -ELOOP;
- if (count++==32) {
- __putname(nd->last.name);
- goto exit;
- }
- dir = nd->dentry;
- mutex_lock(&dir->d_inode->i_mutex);
- path.dentry = lookup_hash(nd);
- path.mnt = nd->mnt;
- __putname(nd->last.name);
- goto do_last;
- }
首先进行文件打开设置工作,第40行,如果是打开操作,则调用path_lookup_open函数。第53行,如果文件不存在,就创建一个文件,调用path_lookup_create函数。在第88行,如果是创建文件,需要建立磁盘上的索引节点,即调用open_namei_create函数。我们逐一解释:
首先path_lookup_open函数:
-
-
-
-
-
-
-
-
- int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags,
- struct nameidata *nd, int open_flags)
- {
- return __path_lookup_intent_open(dfd, name, lookup_flags, nd,
- open_flags, 0);
- }
封装了__path_lookup_intent_open函数。
path_lookup_create函数:
-
-
-
-
-
-
-
-
-
- static int path_lookup_create(int dfd, const char *name,
- unsigned int lookup_flags, struct nameidata *nd,
- int open_flags, int create_mode)
- {
- return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE,
- nd, open_flags, create_mode);
- }
也封装了__path_lookup_intent_open函数,只是增加了创建标志LOOKUP_CREATE, 在create操作的lookup_flags设置了LOOKUP_PARENT,接下来,将看到这个标志的作用。
继续跟踪__path_lookup_intent_open函数:
static int __path_lookup_intent_open(int dfd, const char *name,
- unsigned int lookup_flags, struct nameidata *nd,
- int open_flags, int create_mode)
- {
-
- struct file *filp = get_empty_filp();
- int err;
- if (filp == NULL)
- return -ENFILE;
-
- nd->intent.open.file = filp;
-
- nd->intent.open.flags = open_flags;
-
- nd->intent.open.create_mode = create_mode;
-
- err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
- if (IS_ERR(nd->intent.open.file)) {
- if (err == 0) {
- err = PTR_ERR(nd->intent.open.file);
- path_release(nd);
- }
- } else if (err != 0)
- release_open_intent(nd);
- return err;
- }
首先调用get_empty_flip函数分配一个空闲的文件对象filp, 设置intent.open的相关域,包括“想要打开的文件”,打开标志和创建模式。最后,调用do_path_lookup对文件路径进行解析,并填充nd。
-
-
-
- static int fastcall do_path_lookup(int dfd, const char *name,
- unsigned int flags, struct nameidata *nd)
- {
- int retval = 0;
- int fput_needed;
- struct file *file;
- struct fs_struct *fs = current->fs;
-
- nd->last_type = LAST_ROOT;
- nd->flags = flags;
- nd->depth = 0;
-
- if (*name=='/') {
- read_lock(&fs->lock);
- if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-
- nd->mnt = mntget(fs->altrootmnt);
-
- nd->dentry = dget(fs->altroot);
- read_unlock(&fs->lock);
- if (__emul_lookup_dentry(name,nd))
- goto out;
- read_lock(&fs->lock);
- }
-
- nd->mnt = mntget(fs->rootmnt);
-
- nd->dentry = dget(fs->root);
- read_unlock(&fs->lock);
-
- } else if (dfd == AT_FDCWD) {
- read_lock(&fs->lock);
-
- nd->mnt = mntget(fs->pwdmnt);
-
- nd->dentry = dget(fs->pwd);
- read_unlock(&fs->lock);
- } else {
- struct dentry *dentry;
-
- file = fget_light(dfd, &fput_needed);
- retval = -EBADF;
- if (!file)
- goto out_fail;
-
- dentry = file->f_path.dentry;
- retval = -ENOTDIR;
- if (!S_ISDIR(dentry->d_inode->i_mode))
- goto fput_fail;
-
- retval = file_permission(file, MAY_EXEC);
- if (retval)
- goto fput_fail;
-
- nd->mnt = mntget(file->f_path.mnt);
-
- nd->dentry = dget(dentry);
- fput_light(file, fput_needed);
- }
- current->total_link_count = 0;
-
- retval = link_path_walk(name, nd);
- out:
- if (likely(retval == 0)) {
- if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
- nd->dentry->d_inode))
- audit_inode(name, nd->dentry->d_inode);
- }
- out_fail:
- return retval;
-
- fput_fail:
- fput_light(file, fput_needed);
- goto out_fail;
- }
第11-14行,设置初始化nd->last_type, flags和depth. 其中depth表示符号链接的深度。由于符号链接可以链接自己,因此需要限制链接的深度。
第16行,如果第一个字符为/,表示从根目录开始解析,设置nd->mnt为根挂载点对象,nd->dentry为根目录项对象,然后增加引用计数。
第34行,如果是从当前目录开始,将nd->mnt设置为当前目录的挂载点对象,nd->dentry设置为当前目录的目录项对象。
第41行,否则,将nd->mnt和nd->dentry分别设置为f_path.mnt和f_pat.dentry.
接下来,第63行,初始化符号链接总数,调用实际文件系统的路径分解函数link_path_walk.
- int fastcall link_path_walk(const char *name, struct nameidata *nd)
- {
- struct nameidata save = *nd;
- int result;
-
-
-
- dget(save.dentry);
- mntget(save.mnt);
-
- result = __link_path_walk(name, nd);
- if (result == -ESTALE) {
- *nd = save;
- dget(nd->dentry);
- mntget(nd->mnt);
- nd->flags |= LOOKUP_REVAL;
- result = __link_path_walk(name, nd);
- }
-
- dput(save.dentry);
- mntput(save.mnt);
- return result;
- }
首先,备份挂载点对象和目录项对象,然后调用__link_path_walk解析.
这个函数也比较复杂,在下一节中继续分析!
/----------------------------------------------------------------------------------------------
http://blog.csdn.net/chenjin_zhong/article/details/8452640
1. 闲言少叙,继续分析__link_path_walk函数:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
- {
- struct path next;
- struct inode *inode;
- int err;
-
- unsigned int lookup_flags = nd->flags;
-
- while (*name=='/')
- name++;
-
- if (!*name)
- goto return_reval;
-
- inode = nd->dentry->d_inode;
-
- if (nd->depth)
- lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
-
-
- for(;;) {
-
- unsigned long hash;
-
- struct qstr this;
- unsigned int c;
-
- nd->flags |= LOOKUP_CONTINUE;
-
- err = exec_permission_lite(inode, nd);
- if (err == -EAGAIN)
- err = vfs_permission(nd, MAY_EXEC);
- if (err)
- break;
-
- this.name = name;
-
- c = *(const unsigned char *)name;
-
- hash = init_name_hash();
- do {
- name++;
-
- hash = partial_name_hash(c, hash);
- c = *(const unsigned char *)name;
- } while (c && (c != '/'));
-
- this.len = name - (const char *) this.name;
-
- this.hash = end_name_hash(hash);
-
-
-
- if (!c)
- goto last_component;
- while (*++name == '/');
-
- if (!*name)
- goto last_with_slashes;
-
-
-
-
-
-
-
- if (this.name[0] == '.') switch (this.len) {
- default:
- break;
- case 2:
- if (this.name[1] != '.')
- break;
-
- follow_dotdot(nd);
- inode = nd->dentry->d_inode;
-
- case 1:
- continue;
- }
-
-
-
-
-
- if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
- err = nd->dentry->d_op->d_hash(nd->dentry, &this);
- if (err < 0)
- break;
- }
-
-
- err = do_lookup(nd, &this, &next);
- if (err)
- break;
-
- err = -ENOENT;
-
- inode = next.dentry->d_inode;
- if (!inode)
- goto out_dput;
- err = -ENOTDIR;
- if (!inode->i_op)
- goto out_dput;
-
- if (inode->i_op->follow_link) {
-
- err = do_follow_link(&next, nd);
- if (err)
- goto return_err;
- err = -ENOENT;
- inode = nd->dentry->d_inode;
- if (!inode)
- break;
- err = -ENOTDIR;
- if (!inode->i_op)
- break;
- } else
-
- path_to_nameidata(&next, nd);
- err = -ENOTDIR;
- if (!inode->i_op->lookup)
- break;
- continue;
-
-
- last_with_slashes:
- lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
- last_component:
-
- nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
-
- if (lookup_flags & LOOKUP_PARENT)
- goto lookup_parent;
- if (this.name[0] == '.') switch (this.len) {
- default:
- break;
- case 2:
- if (this.name[1] != '.')
- break;
- follow_dotdot(nd);
- inode = nd->dentry->d_inode;
-
- case 1:
- goto return_reval;
- }
-
- if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
- err = nd->dentry->d_op->d_hash(nd->dentry, &this);
- if (err < 0)
- break;
- }
-
- err = do_lookup(nd, &this, &next);
- if (err)
- break;
-
- inode = next.dentry->d_inode;
- if ((lookup_flags & LOOKUP_FOLLOW)
- && inode && inode->i_op && inode->i_op->follow_link) {
- err = do_follow_link(&next, nd);
- if (err)
- goto return_err;
- inode = nd->dentry->d_inode;
- } else
-
- path_to_nameidata(&next, nd);
- err = -ENOENT;
- if (!inode)
- break;
- if (lookup_flags & LOOKUP_DIRECTORY) {
- err = -ENOTDIR;
- if (!inode->i_op || !inode->i_op->lookup)
- break;
- }
- goto return_base;
- lookup_parent:
-
- nd->last = this;
-
- nd->last_type = LAST_NORM;
-
- if (this.name[0] != '.')
- goto return_base;
-
- if (this.len == 1)
- nd->last_type = LAST_DOT;
-
- else if (this.len == 2 && this.name[1] == '.')
- nd->last_type = LAST_DOTDOT;
- else
- goto return_base;
- return_reval:
-
-
-
-
- if (nd->dentry && nd->dentry->d_sb &&
- (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
- err = -ESTALE;
-
- if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
- break;
- }
- return_base:
- return 0;
- out_dput:
- dput_path(&next, nd);
- break;
- }
- path_release(nd);
- return_err:
- return err;
- }
这个函数主要做三件事:
(1)解析已经存在的文件路径,即打开标志
(2)解析不存在的文件路径,即创建文件标志,这样,需要得到父目录项对象和安装点对象
(3)解析符号链接,第一次找到符号链接的文件路径,第二次解析路径名
第23-26行,只有/,跳至return_reval. 这里多个根当作一个根处理,如//
第31-32行,设置符号链接标志。
第39行,定义qstr结构,这个结构包括hash值,分量长度和分量名。
第43-46行,进行权限检查,遍厍目录,必须具有执行权限。
第55-60行,计算每个分量的hash值。
第68行,如果解析到最后一个分量,跳至last_component.
第72行,如果遇到类似/proc/的目录,跳至last_with_slashes.
第80行,如果分量的第一个字符是.,但第二个字符不是.,则正常解析。
第88行,当第二个字符也是. ,说明是父目录,调用follow_dotdot进行回溯。
我们分析一下这个函数:
- static __always_inline void follow_dotdot(struct nameidata *nd)
- {
-
- struct fs_struct *fs = current->fs;
-
- while(1) {
- struct vfsmount *parent;
-
- struct dentry *old = nd->dentry;
- read_lock(&fs->lock);
-
- if (nd->dentry == fs->root &&
- nd->mnt == fs->rootmnt) {
- read_unlock(&fs->lock);
- break;
- }
- read_unlock(&fs->lock);
- spin_lock(&dcache_lock);
-
- if (nd->dentry != nd->mnt->mnt_root) {
- nd->dentry = dget(nd->dentry->d_parent);
- spin_unlock(&dcache_lock);
- dput(old);
- break;
- }
- spin_unlock(&dcache_lock);
- spin_lock(&vfsmount_lock);
- parent = nd->mnt->mnt_parent;
- if (parent == nd->mnt) {
- spin_unlock(&vfsmount_lock);
- break;
- }
- mntget(parent);
- nd->dentry = dget(nd->mnt->mnt_mountpoint);
- spin_unlock(&vfsmount_lock);
- dput(old);
- mntput(nd->mnt);
- nd->mnt = parent;
- }
-
- follow_mount(&nd->mnt, &nd->dentry);
- }
第11-16行,如果回溯的是进程的根目录,则不允许,调用follow_mount函数。
第19-23行,如果目录项对象不是根目录,则通过nd->dentry=dget(nd->dentry->d_parent)返回上一级目录项对象。
不管怎么样,最终会调用follow_mount函数。有时,人的好奇心是很强的,同样,对于Linux内核源码,也需要好奇心。哈哈,看一下follow_mount函数:
-
- static void follow_mount(struct vfsmount **mnt, struct dentry **dentry)
- {
- while (d_mountpoint(*dentry)) {
-
- struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
- if (!mounted)
- break;
- dput(*dentry);
- mntput(*mnt);
- *mnt = mounted;
- *dentry = dget(mounted->mnt_root);
- }
- }
这个函数首先判断一下dentry目录项是不是挂载点,如果是,调用lookup_mnt函数返回子挂载点。在第11行,将mnt赋值mounted,接着,寻找子挂载点。最终,找到一个没有其它文件系统安装在其之上的文件系统挂载点。这里,需要解释一下,如果/dev/sda1和/dev/sda2先后挂载在/usr目录下,那么/dev/sda1的相关目录将会被隐藏,而/dev/sda2的父挂载点是/dev/sda1. 而上面的过程是通过父挂载点找子挂载点,直到找到一个没有挂载其它文件系统的挂载点为止。这个,文件系统称暂且称为底层文件系统。也不知道,这么叫对不对,或许是顶层文件系统。总之,follow_dotdot回溯到了上一级目录。
接着__link_path_walk解释,
第97行,如果底层文件系统具有计算hash值的函数,则调用。
第106行,查找分量的目录项对象函数do_lookup,这个函数一会分析。
第119行,判断是否是符号链接,调用do_follow_link处理符号链接,稍后分析。
第142行,处理最后一个分量。
第167行,调用do_lookup函数,找到一个最后分量的目录项对象和挂载点对象。
第172行,如果最后一个分量是符号链接,调用do_follow_link进一步处理。
第190行,当只是建立文件时,跳至lookup_parent.
第192-205行,最后一个分量名和分量类型,此时,nd保存了上一个分量的目录项对象和挂载点对象。
如果正确解析,返回0.
下面,分析一下do_lookup函数:
-
-
-
-
-
-
- static int do_lookup(struct nameidata *nd, struct qstr *name,
- struct path *path)
- {
- struct vfsmount *mnt = nd->mnt;
-
- struct dentry *dentry = __d_lookup(nd->dentry, name);
-
- if (!dentry)
- goto need_lookup;
- if (dentry->d_op && dentry->d_op->d_revalidate)
- goto need_revalidate;
- done:
- path->mnt = mnt;
- path->dentry = dentry;
-
- __follow_mount(path);
- return 0;
-
- need_lookup:
-
- dentry = real_lookup(nd->dentry, name, nd);
- if (IS_ERR(dentry))
- goto fail;
- goto done;
-
- need_revalidate:
-
- dentry = do_revalidate(dentry, nd);
- if (!dentry)
- goto need_lookup;
- if (IS_ERR(dentry))
- goto fail;
- goto done;
-
- fail:
- return PTR_ERR(dentry);
- }
这个函数的主要功能是查询目录项对象,并将挂载点和目录项对象保存在nameidata结构。具体如下:
第10行,nd保存了上一个目录项对象和挂载点对象。
第12行,首先在目录项缓存dentry cache查找,如果缓存不存在,跳转到need_lookup,调用real_lookup在内存分配一个dentry,并将dentry和索引节点关联。
第17行,如果存在,需要验证目录项对象是否有效,跳至34行,如果有效,将mnt和dentry赋值给path. 在__link_path_walk会将path值赋给nd.
继续跟踪__do_lookup函数:
-
- struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
- {
- unsigned int len = name->len;
- unsigned int hash = name->hash;
- const unsigned char *str = name->name;
- struct hlist_head *head = d_hash(parent,hash);
- struct dentry *found = NULL;
- struct hlist_node *node;
- struct dentry *dentry;
-
- rcu_read_lock();
-
- hlist_for_each_entry_rcu(dentry, node, head, d_hash) {
- struct qstr *qstr;
-
- if (dentry->d_name.hash != hash)
- continue;
-
- if (dentry->d_parent != parent)
- continue;
-
- spin_lock(&dentry->d_lock);
-
-
-
-
-
-
- if (dentry->d_parent != parent)
- goto next;
-
-
-
-
-
-
- qstr = &dentry->d_name;
- if (parent->d_op && parent->d_op->d_compare) {
- if (parent->d_op->d_compare(parent, qstr, name))
- goto next;
- } else {
- if (qstr->len != len)
- goto next;
- if (memcmp(qstr->name, str, len))
- goto next;
- }
-
- if (!d_unhashed(dentry)) {
- atomic_inc(&dentry->d_count);
- found = dentry;
- }
- spin_unlock(&dentry->d_lock);
- break;
- next:
- spin_unlock(&dentry->d_lock);
- }
- rcu_read_unlock();
-
- return found;
- }
第4-7行,赋值len,hash和name,并取得head指针,为下面比较做准备。
第14行,判断hash值是是否相同。
第20行,判断父目录项parent是否相同。
第39行,匹配分量名。
如果找到,返回目录项对象。
从这个查找过程,可以看出,是用目录名或是文件名计算hash值,然后返回对应的目录项对象。这也是为什么目录名或文件名不放在索引节点而放在目录项对象的原因。
如果目录项缓存没有,继续跟踪real_lookup函数:
-
-
-
-
-
-
-
-
-
- static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
- {
- struct dentry * result;
-
- struct inode *dir = parent->d_inode;
- mutex_lock(&dir->i_mutex);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- result = d_lookup(parent, name);
- if (!result) {
-
- struct dentry * dentry = d_alloc(parent, name);
- result = ERR_PTR(-ENOMEM);
- if (dentry) {
-
- result = dir->i_op->lookup(dir, dentry, nd);
- if (result)
- dput(dentry);
- else
- result = dentry;
- }
- mutex_unlock(&dir->i_mutex);
- return result;
- }
-
-
-
-
-
- mutex_unlock(&dir->i_mutex);
- if (result->d_op && result->d_op->d_revalidate) {
- result = do_revalidate(result, nd);
- if (!result)
- result = ERR_PTR(-ENOENT);
- }
- return result;
- }
在第33行,重新搜索一下目录项缓存,由于进程在查找过程中可能阻塞,在这期间,目录项可能已经加入了dentry cache,所以需要重新查找一下。
第34行,如果没有找到,调用d_alloc分配一个目录项对象。
第35行,具体的文件系统索引节点查找函数,读取磁盘索引节点信息,并将索引节点和目录项对象关联。在iget索引节点时,将索引节点加入了inode cache. 在关联inode节点时,将目录项对象加入了dentry cache.
在第53行,验证目录项对象是否有效,最终返回目录项对象。
可以看到,此时返回的目录项对象已经加入到了dentry cache,并关联了索引节点。即dentry->d_innode=inode.
我们继续跟踪上面的两个函数,首先跟踪d_alloc函数:
-
-
-
-
-
-
-
-
-
-
- struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
- {
- struct dentry *dentry;
- char *dname;
-
- dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
- if (!dentry)
- return NULL;
-
- if (name->len > DNAME_INLINE_LEN-1) {
- dname = kmalloc(name->len + 1, GFP_KERNEL);
- if (!dname) {
- kmem_cache_free(dentry_cache, dentry);
- return NULL;
- }
- } else {
- dname = dentry->d_iname;
- }
- dentry->d_name.name = dname;
-
- dentry->d_name.len = name->len;
- dentry->d_name.hash = name->hash;
- memcpy(dname, name->name, name->len);
- dname[name->len] = 0;
-
- atomic_set(&dentry->d_count, 1);
- dentry->d_flags = DCACHE_UNHASHED;
- spin_lock_init(&dentry->d_lock);
- dentry->d_inode = NULL;
- dentry->d_parent = NULL;
- dentry->d_sb = NULL;
- dentry->d_op = NULL;
- dentry->d_fsdata = NULL;
- dentry->d_mounted = 0;
- #ifdef CONFIG_PROFILING
- dentry->d_cookie = NULL;
- #endif
- INIT_HLIST_NODE(&dentry->d_hash);
- INIT_LIST_HEAD(&dentry->d_lru);
- INIT_LIST_HEAD(&dentry->d_subdirs);
- INIT_LIST_HEAD(&dentry->d_alias);
-
- if (parent) {
-
- dentry->d_parent = dget(parent);
-
- dentry->d_sb = parent->d_sb;
- } else {
- INIT_LIST_HEAD(&dentry->d_u.d_child);
- }
-
- spin_lock(&dcache_lock);
- if (parent)
- list_add(&dentry->d_u.d_child, &parent->d_subdirs);
- dentry_stat.nr_dentry++;
- spin_unlock(&dcache_lock);
-
- return dentry;
- }
第16行,为目录项对象分配内存。
第29-32行,设置名称,长度和hash值。
第48-51行,初始化相关链表。
第53行,如果父目录项对象存在,就设置父目录项对象和超级块对象。这样,就建立了一个子目录项对象。
接着跟踪lookup函数,以ext3为例,ext3_lookup:
-
- static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
- {
- struct inode * inode;
- struct ext3_dir_entry_2 * de;
- struct buffer_head * bh;
-
- if (dentry->d_name.len > EXT3_NAME_LEN)
- return ERR_PTR(-ENAMETOOLONG);
-
- bh = ext3_find_entry(dentry, &de);
- inode = NULL;
- if (bh) {
- unsigned long ino = le32_to_cpu(de->inode);
- brelse (bh);
- if (!ext3_valid_inum(dir->i_sb, ino)) {
- ext3_error(dir->i_sb, "ext3_lookup",
- "bad inode number: %lu", ino);
- inode = NULL;
- } else
-
- inode = iget(dir->i_sb, ino);
-
- if (!inode)
- return ERR_PTR(-EACCES);
- }
-
- return d_splice_alias(inode, dentry);
- }
第11行,得到ext3_dir_entry_2对象,该对象包含了索引节点号。
第13-16行,判断索引节点号是否合法。
第21行,创建内存索引节点,并填充相关信息,将索引节点加入inode cache.
第28行,将目录项对象和索引节点关联。
首先,跟踪iget函数:
- static inline struct inode *iget(struct super_block *sb, unsigned long ino)
- {
-
- struct inode *inode = iget_locked(sb, ino);
-
- if (inode && (inode->i_state & I_NEW)) {
- sb->s_op->read_inode(inode);
- unlock_new_inode(inode);
- }
-
- return inode;
- }
首先调用iget_locked分配内存索引节点。如果是新分配的,需要调用read_inode调用磁盘上的索引节点填充相关信息。
继续跟踪iget_locked函数:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- struct inode *iget_locked(struct super_block *sb, unsigned long ino)
- {
-
- struct hlist_head *head = inode_hashtable + hash(sb, ino);
- struct inode *inode;
-
- inode = ifind_fast(sb, head, ino);
- if (inode)
- return inode;
-
-
-
-
-
- return get_new_inode_fast(sb, head, ino);
- }
第28行,在inode cache查找,如果没有,调用get_new_inode_fast分配一个索引节点并插入inode cache.
ifind_fast留给读者自行分析吧!
分析一下,get_new_inode_fast函数:
-
-
-
-
- static struct inode * get_new_inode_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino)
- {
- struct inode * inode;
-
- inode = alloc_inode(sb);
- if (inode) {
- struct inode * old;
-
- spin_lock(&inode_lock);
-
- old = find_inode_fast(sb, head, ino);
- if (!old) {
-
- inode->i_ino = ino;
- inodes_stat.nr_inodes++;
-
- list_add(&inode->i_list, &inode_in_use);
-
- list_add(&inode->i_sb_list, &sb->s_inodes);
-
- hlist_add_head(&inode->i_hash, head);
-
- inode->i_state = I_LOCK|I_NEW;
- spin_unlock(&inode_lock);
-
-
-
-
- return inode;
- }
-
-
-
-
-
-
- __iget(old);
- spin_unlock(&inode_lock);
- destroy_inode(inode);
- inode = old;
- wait_on_inode(inode);
- }
- return inode;
- }
第9行,分配索引节点。
第17-28行,索引节点的初始化。包括:
(1)设置索引节点号
(2)加入inode_in_use链表
(3)加入inode_hashtable,即加入inode cache
(4)设置状态为I_NEW
返回索引节点。
接下来,继续分析iget函数中的第二个函数read_inode.
- void ext3_read_inode(struct inode * inode)
- {
- struct ext3_iloc iloc;
- struct ext3_inode *raw_inode;
- struct ext3_inode_info *ei = EXT3_I(inode);
- struct buffer_head *bh;
- int block;
-
- #ifdef CONFIG_EXT3_FS_POSIX_ACL
- ei->i_acl = EXT3_ACL_NOT_CACHED;
- ei->i_default_acl = EXT3_ACL_NOT_CACHED;
- #endif
- ei->i_block_alloc_info = NULL;
-
- if (__ext3_get_inode_loc(inode, &iloc, 0))
- goto bad_inode;
- bh = iloc.bh;
-
- raw_inode = ext3_raw_inode(&iloc);
- inode->i_mode = le16_to_cpu(raw_inode->i_mode);
- inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
- inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
- if(!(test_opt (inode->i_sb, NO_UID32))) {
- inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
- inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
- }
- inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
- inode->i_size = le32_to_cpu(raw_inode->i_size);
- inode->i_atime.tv_sec = le32_to_cpu(raw_inode->i_atime);
- inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->i_ctime);
- inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->i_mtime);
- inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
-
- ei->i_state = 0;
- ei->i_dir_start_lookup = 0;
- ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
-
-
-
-
-
- if (inode->i_nlink == 0) {
- if (inode->i_mode == 0 ||
- !(EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ORPHAN_FS)) {
-
- brelse (bh);
- goto bad_inode;
- }
-
-
-
-
- }
- inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
- ei->i_flags = le32_to_cpu(raw_inode->i_flags);
- #ifdef EXT3_FRAGMENTS
- ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
- ei->i_frag_no = raw_inode->i_frag;
- ei->i_frag_size = raw_inode->i_fsize;
- #endif
- ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
- if (!S_ISREG(inode->i_mode)) {
- ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
- } else {
- inode->i_size |=
- ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
- }
- ei->i_disksize = inode->i_size;
- inode->i_generation = le32_to_cpu(raw_inode->i_generation);
- ei->i_block_group = iloc.block_group;
-
-
-
-
- for (block = 0; block < EXT3_N_BLOCKS; block++)
- ei->i_data[block] = raw_inode->i_block[block];
- INIT_LIST_HEAD(&ei->i_orphan);
-
- if (inode->i_ino >= EXT3_FIRST_INO(inode->i_sb) + 1 &&
- EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE) {
-
-
-
-
-
- ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
- if (EXT3_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
- EXT3_INODE_SIZE(inode->i_sb))
- goto bad_inode;
- if (ei->i_extra_isize == 0) {
-
- ei->i_extra_isize = sizeof(struct ext3_inode) -
- EXT3_GOOD_OLD_INODE_SIZE;
- } else {
- __le32 *magic = (void *)raw_inode +
- EXT3_GOOD_OLD_INODE_SIZE +
- ei->i_extra_isize;
- if (*magic == cpu_to_le32(EXT3_XATTR_MAGIC))
- ei->i_state |= EXT3_STATE_XATTR;
- }
- } else
- ei->i_extra_isize = 0;
-
- if (S_ISREG(inode->i_mode)) {
-
- inode->i_op = &ext3_file_inode_operations;
- inode->i_fop = &ext3_file_operations;
- ext3_set_aops(inode);
- } else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &ext3_dir_inode_operations;
- inode->i_fop = &ext3_dir_operations;
- } else if (S_ISLNK(inode->i_mode)) {
- if (ext3_inode_is_fast_symlink(inode))
- inode->i_op = &ext3_fast_symlink_inode_operations;
- else {
- inode->i_op = &ext3_symlink_inode_operations;
- ext3_set_aops(inode);
- }
- } else {
- inode->i_op = &ext3_special_inode_operations;
- if (raw_inode->i_block[0])
- init_special_inode(inode, inode->i_mode,
- old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));
- else
- init_special_inode(inode, inode->i_mode,
- new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
- }
- brelse (iloc.bh);
- ext3_set_inode_flags(inode);
- return;
-
- bad_inode:
- make_bad_inode(inode);
- return;
- }
简单说一下功能:
第19行,读取磁盘上原始索引节点,用来填充新分配的索引节点。
第20-32行,inode相关域设置。
第104行,如果是文件,则将文件相关操作的指针赋给inode->i_fop,这非常重要,因为,最后将i_fop赋给了文件对象file->f_op. 表示了文件的相关操作。
第109-111行,目录相关操作。
第112-118行,符号链接相关操作。
第119-128行,设备相关操作。具体就不分析了。
到此为止,我们已经得到了一个inode节点,并且填充了相关域。
iget函数返回,ext3_lookup继续往下走,调用d_splice_alias函数:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
- {
- struct dentry *new = NULL;
-
- if (inode && S_ISDIR(inode->i_mode)) {
- spin_lock(&dcache_lock);
- new = __d_find_alias(inode, 1);
- if (new) {
- BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
- fsnotify_d_instantiate(new, inode);
- spin_unlock(&dcache_lock);
- security_d_instantiate(new, inode);
- d_rehash(dentry);
- d_move(new, dentry);
- iput(inode);
- } else {
-
-
- list_add(&dentry->d_alias, &inode->i_dentry);
-
- dentry->d_inode = inode;
- fsnotify_d_instantiate(dentry, inode);
- spin_unlock(&dcache_lock);
- security_d_instantiate(dentry, inode);
-
- d_rehash(dentry);
- }
- } else
- d_add(dentry, inode);
- return new;
- }
第37行,将目录项对象和索引节点相关联。
第42行,将目录项对象加入到目录项缓存。
最后,返回dentry.
如果,你现在仍然很清醒,那么恭喜你,你已经基本了解了整个过程。
lookup函数返回,在__link_path_walk函数调用path_to_nameidata将path->mnt和path->dentry赋给nd->mnt和nd->dentry.表示找到的目录项对象和挂载点对象。
接下来,处理符号链接,调用do_follow_link函数:
-
-
-
-
-
-
-
- static inline int do_follow_link(struct path *path, struct nameidata *nd)
- {
- int err = -ELOOP;
- if (current->link_count >= MAX_NESTED_LINKS)
- goto loop;
-
- if (current->total_link_count >= 40)
- goto loop;
- BUG_ON(nd->depth >= MAX_NESTED_LINKS);
- cond_resched();
- err = security_inode_follow_link(path->dentry, nd);
- if (err)
- goto loop;
- current->link_count++;
- current->total_link_count++;
- nd->depth++;
- err = __do_follow_link(path, nd);
- current->link_count--;
- nd->depth--;
- return err;
- loop:
- dput_path(path, nd);
- path_release(nd);
- return err;
- }
这个函数首先松果符号链接数,不能超过MAX_NESTED_LINKS.
最终调用__do_follow_link进行处理。
- static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
- {
- int error;
- void *cookie;
- struct dentry *dentry = path->dentry;
-
- touch_atime(path->mnt, dentry);
-
- nd_set_link(nd, NULL);
- if (path->mnt != nd->mnt) {
- path_to_nameidata(path, nd);
- dget(dentry);
- }
- mntget(path->mnt);
- cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
- error = PTR_ERR(cookie);
- if (!IS_ERR(cookie)) {
-
- char *s = nd_get_link(nd);
- error = 0;
- if (s)
- error = __vfs_follow_link(nd, s);
- if (dentry->d_inode->i_op->put_link)
- dentry->d_inode->i_op->put_link(dentry, nd, cookie);
- }
- dput(dentry);
- mntput(path->mnt);
-
- return error;
- }
第15行,取出符号链接的路径,放到nd->saved_names可以看出,符号链接有自己的inode节点,并且inode节点保存的内容是真正的文件路径。所以,符号链接可以跨文件系统。
第22行,调用__vfs_follow_link解析路径名。
-
- static __always_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))
-
- goto out;
- }
- res = link_path_walk(link, nd);
- out:
- if (nd->depth || res || nd->last_type!=LAST_NORM)
- return res;
-
-
-
-
-
- name = __getname();
- if (unlikely(!name)) {
- path_release(nd);
- return -ENOMEM;
- }
- strcpy(name, nd->last.name);
- nd->last.name = name;
- return 0;
- fail:
- path_release(nd);
- return PTR_ERR(link);
- }
第15行,调用link_path_walk. 看到这个函数,松了一口气,因为前面已经分析过了。
当__link_path_walk返回时,link_path_walk也跟着返回,之后do_path_lookup也返回了,最终回到open_namei函数。
如果是打开文件,返回即可。
如果是创建文件,还需调用open_namei_create函数:
- static int open_namei_create(struct nameidata *nd, struct path *path,
- int flag, int mode)
- {
- int error;
- struct dentry *dir = nd->dentry;
-
- if (!IS_POSIXACL(dir->d_inode))
- mode &= ~current->fs->umask;
- error = vfs_create(dir->d_inode, path->dentry, mode, nd);
- mutex_unlock(&dir->d_inode->i_mutex);
- dput(nd->dentry);
- nd->dentry = path->dentry;
- if (error)
- return error;
-
- return may_open(nd, 0, flag & ~O_TRUNC);
- }
封装了vfs_create函数:
- int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
- struct nameidata *nd)
- {
- int error = may_create(dir, dentry, nd);
-
- if (error)
- return error;
-
- if (!dir->i_op || !dir->i_op->create)
- return -EACCES;
- mode &= S_IALLUGO;
- mode |= S_IFREG;
- error = security_inode_create(dir, dentry, mode);
- if (error)
- return error;
- DQUOT_INIT(dir);
- error = dir->i_op->create(dir, dentry, mode, nd);
- if (!error)
- fsnotify_create(dir, dentry);
- return error;
- }
调用inode的create方法创建索引节点。以ext3为例,调用ext3_create函数:
-
-
-
-
-
-
-
-
- static int ext3_create (struct inode * dir, struct dentry * dentry, int mode,
- struct nameidata *nd)
- {
- handle_t *handle;
- struct inode * inode;
- int err, retries = 0;
-
- retry:
- handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
- EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
- 2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
- if (IS_ERR(handle))
- return PTR_ERR(handle);
-
- if (IS_DIRSYNC(dir))
- handle->h_sync = 1;
-
- inode = ext3_new_inode (handle, dir, mode);
- err = PTR_ERR(inode);
- if (!IS_ERR(inode)) {
- inode->i_op = &ext3_file_inode_operations;
- inode->i_fop = &ext3_file_operations;
- ext3_set_aops(inode);
- err = ext3_add_nondir(handle, dentry, inode);
- }
- ext3_journal_stop(handle);
- if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
- goto retry;
- return err;
- }
第26行,创建索引节点。
第29-33行,inode->i_op和inode->i_fop赋值。
之后,还会将索引节点标识为脏,需要回写到磁盘上,具体实现就不分析了。
当open_namei函数返回时,open系统调用也就分析完了。
总结:
(1)建立一个struct file结构体,将nameidata相关域填充到这个结构体,最重要的两个域mnt, dentry. 从dentry可得到inode,从而将i_fop赋给文件对象。
(2)在路径查找时,通过父目录项建立子目录项,然后将子目录项关联inode节点。
(3)打开文件和建立文件不同。打开文件,只需要找到目录项对象,然后关联索引节点即可,因为索引节点存在。而建立文件时,由于文件不存在,首先找到目录的目录项对象,然后建立子目录项对象和索引节点对象,最后索引节点对象需要同步到磁盘上。
(4)有两个缓存,dentry cache和inode cache,分别用来缓存目录项对象和索引节点对象。
(5)将文件对象和进程的files_struct相关联。
(6)对于普通文件,不需要打开操作,而对于设备文件,需要打开操作,例如SCSI设备的sg_open函数。
(7)主要处理三种情形:打开文件,建立文件和符号链接
参考文献: <深入理解Linux内核第3版>