Linux VFS中do_sys_open


do_sys_open(AT_FDCWD, filename, flags, mode)

函数原long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)

static struct file *path_openat(int dfd, struct filename *pathname,
  struct nameidata *nd, const struct open_flags *op, int flags)
{
 struct file *base = NULL;
 struct file *file;
 struct path path;
 int opened = 0;
 int error;

 file = get_empty_filp();
 if (IS_ERR(file))
  return file;

 file->f_flags = op->open_flag;

 error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);
 if (unlikely(error))
  goto out;

 current->total_link_count = 0;
 error = link_path_walk(pathname->name, nd);
 if (unlikely(error))
  goto out;

 error = do_last(nd, &path, file, op, &opened, pathname);
 while (unlikely(error > 0)) { /* trailing symlink */
  struct path link = path;
  void *cookie;
  if (!(nd->flags & LOOKUP_FOLLOW)) {
   path_put_conditional(&path, nd);
   path_put(&nd->path);
   error = -ELOOP;
   break;
  }
  error = may_follow_link(&link, nd);
  if (unlikely(error))
   break;
  nd->flags |= LOOKUP_PARENT;
  nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
  error = follow_link(&link, nd, &cookie);
  if (unlikely(error))
   break;
  error = do_last(nd, &path, file, op, &opened, pathname);
  put_link(nd, &link, cookie);
 }
out:
 if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
  path_put(&nd->root);
 if (base)
  fput(base);
 if (!(opened & FILE_OPENED)) {
  BUG_ON(!error);
  put_filp(file);
 }
 if (unlikely(error)) {
  if (error == -EOPENSTALE) {
   if (flags & LOOKUP_RCU)
    error = -ECHILD;
   else
    error = -ESTALE;
  }
  file = ERR_PTR(error);
 }
 return file;
}

struct file *do_filp_open(int dfd, struct filename *pathname,
  const struct open_flags *op, int flags)
{
 struct nameidata nd;
 struct file *filp;

 filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
 if (unlikely(filp == ERR_PTR(-ECHILD)))
  filp = path_openat(dfd, pathname, &nd, op, flags);
 if (unlikely(filp == ERR_PTR(-ESTALE)))
  filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
 return filp;
}

struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
  const char *name, const struct open_flags *op, int flags)
{
 struct nameidata nd;
 struct file *file;
 struct filename filename = { .name = name };

 nd.root.mnt = mnt;
 nd.root.dentry = dentry;

 flags |= LOOKUP_ROOT;

 if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)
  return ERR_PTR(-ELOOP);

 file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_RCU);
 if (unlikely(file == ERR_PTR(-ECHILD)))
  file = path_openat(-1, &filename, &nd, op, flags);
 if (unlikely(file == ERR_PTR(-ESTALE)))
  file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_REVAL);
 return file;
}

1.    找到一个本进程没有使用的文件描述符fdint型)

 

(1)在当前进程打开的文件位图表中,找到第一个为0的位,返回这个位在位图表里面的下标,这个下标就是将用分配的未使用的文件描述符fd

 

(2)把当前进程的文件表扩展一个文件(即尝试添加一个struct file到当前

进程的文件列表中),进程task_struct-> files_struct-> fd_array[NR_OPEN_DEFAULT]是一个struct file 数组,而NR_OPEN_DEFAULT在64位系统中等于64(因为一般进程打开的文件数大多都用这个数组就可以直接放下了),如果扩展操作导致当前进程的这个存放struct file的数组放不下了,如要装第65个struct flie结构体,那么将重新分配一片内存区专门用来存放struct file结构体,并且这个内存区的大小为128个struct file结构体,然后将当前进程的task_struct->files_struct->fdtable->fd指针指向这片内存的首地址,然后把之前数组里面的内容复制到这片内存区里面来。下次添加如果超过了128个了,则分配256个大小直到256个也装满,超过256则分配512,依次类推,总是2的幂次方,且会把之前的复制到新分配的内存里面去

 

注意:这里只是更新了进程的这个file table,新的进程描述符对应的struct file还没有生成进去。

 

(3)设置进程的文件位图中新分配的这个文件描述符位为(1)中找到的下标,并更新下一次该分配的进程描述符起点

 

2.    分配一个全新的 struct file结构体

Struct file =kmem_cache_zalloc(filp_cachep, GFP_KERNEL);

 

3.    根据传人的 pathname查找或建立对应的dentry

并设置此dentry对应的inode。内核做这个事情借助于一个nameidata数据结构

(1)    如果pathname中第一个字符是“/”,那么说明使用绝对路径,设置nameidata为更目录对应的dentry和当前目录的inode,mount点等

 

(2)    如果不是“/”,则使用相对路径,设置nameidata为当前目录对应的dentry,inode,mount点等

 

(3)    一层一层往下查找,直到找到最终的那个文件或者目录分量,注意:如/usr/bin/make,先找“/”(这是3.1就做了的),再找“/”下面的usr,再找bin,最后找make。

 

这里细说一下第一层怎么在“/“下面找到”usr“的:

第一层查找先找“/”下面的usr对应的dentry,内核通过“/”对应的dentry和usr字符串两个参数进行hash运算获取一个dentry的链表

然后逐个看这个链表里面有没有parent dentry为“/”对应的dentry的,以及dentry对应的名字的hash值是否与“usr”对应的hash值相同

前面条件都满足这里再看一下parent dentry是否有DCACHE_OP_COMPARE标识,如果有此标识且文件系统实现了dentry->dentry_operations->d_compare函数,那么就调用文件系统的这个函数来判断

如果条件都符合,那么说明内存中usr对应的这个dentry是存在的,如果这个dentry->d_flags中包含DCACHE_OP_REVALIDATE,那么就会调用此dentry->dentry_operatoin->d_revalidate来进行一次核对(网络文件系统此函数都实现了,以便于远程的便跟,在这里会得到更新)

如果最终usr对应的dentry不存在,那么内核就在内存中直接分配一个dentry结构体并且把dentry的name和“usr”对应起来,并且设置这个dentry的parent为“/”对应的dentry,然后还要调用/对应的dentry->d_inode这个inode的inode_operation->lookup(/的inode,新建的dentry,flags),如果返回了新的dentry,那么就把dentry结构体指针指向新返回的dentry,否则还是返回刚刚新创建的那个dentry。(一般的文件系统都实现了inode_operation->lookup,我猜他们会在这个函数里面如果/usr存在则把dentry对应的inode给设置好。。如果/usr不存在则返回一个NULL之类的,以一个错误跳出整个路径执行)

到这里,无论是dentry已经存在于内存中找到的,还是新创建的dentry,总之,对应于“usr”结构的dentry在内存中已经存在了。然后调用follow_managed()函数找到“usr”最新的vfsmount(这里有一点点麻烦,后续会专门讲,这里只需要指定如果/dev/sda mount 到了/mnt,/dev/sdb 也mount到了/mnt,那么这里返回的是最新的这条/dev/sdb mount到/mnt这个vfsmount)。

然后把这个已经找到的或创建的dentry(已经存在于内存中的dentry已经有了inode和它绑定,新建立的dentry也通过inode_operation->lookup建立起来了inode和dentry的联系(此函数会在操作真正的磁盘介质吧inode读出来))和这个最新的vfsmount存到struct path中

然后把这个含有dentry,vfsmount的path结构体存入nameidata数据结构中,到这里,“usr“对应的dentry,inode,vfsmount都准备好了,且存到了nameidata中了

 

(4)    接着(3)里面,一层一层的往下找,依次会找到usr,bin,最后到了make

这里就不调用一层一层往下找的函数了,进入另外一个函数do_last()函数来

处理。在dolast,在dolast里面如果此dentry不存在则创建它,如果有O_CREATE

标识则创建这个文件的inode(这里会调用vfs_create函数,继续调用dentry->inode_operation->create来建立inode,文件系统实现的此函数会操作正在的磁盘介质去创建inode),并且建立inode和dentry的联系,并且建立make对应的vfsmount为最新的mount结构,至此,“/usr/bin/make”中最后一个分量“make”的dentry,inode,vfsmount都存到nameidata中去了。

接着还会把2中分配的file结构体的path(包含dentry和vfsmount)的dentry分量设置为nameidata的这个dentry(dentry结构体中已经有inode的指针),vfsmount也设置为nameidata的vfsmount,并且设置file结构体的file->f_mapppin为nameidata中dentry的inode的i_mapping.并且设置file->f_pos指针为0。

至此,make对应的新分配的这个struct file结构体中的dentry,inode,vfsmount都为nameidata中的了,并且struct file映射到内存的address_space也设置为了inode对应的address_space,struct file的当前位置指针设置为了0,“make”分量的这个struct file结构体准备好了。

接着还会把这个struct file结构体加入其inode对应的super_block超级块的s_files链表中,即struct file结构体会加入其自身inode所在超级块的所有文件链表中。

并且如果自身inode的file_operations不为空则还会设置这个struct file的file_operation等于inode的这个file_operations,即公用inode的file的操作方法。如果inode的file_operations没有实现,则设置为空。

设置此文件标识符为FILE_OPENED.

 

4.    建立 fd到这个struct file结构体的联系

 

调用fd_install(fd,f)来把1中分配的文件描述符和3中的struct file建立联系。

过程简单描述一下,先获取当前进程的fdtable(简单可理解为进程的关联的所有文件数组)的所有文件数组fdtable=current->files->fdt,(current为当前进程task_struct),设置fdtable->fd[fd]=file,(下标fd即新分配的文件描述符,file即为3中创建的struct file结构体)。

这样,进程和文件描述符,struct file,dentry,inode,vfsmount就全部关联起来了

你可能感兴趣的:(Linux VFS中do_sys_open)