open

open

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
  ......
  return do_sys_open(AT_FDCWD, filename, flags, mode);
}

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
  ......
  fd = get_unused_fd_flags(flags); // 1. 获取一个没有使用的文件描述符
  if (fd >= 0) {
    struct file *f = do_filp_open(dfd, tmp, &op); // 2. 创建struct file结构
    if (IS_ERR(f)) {
      put_unused_fd(fd);
      fd = PTR_ERR(f);
    } else {
      fsnotify_open(f);
      fd_install(fd, f); // 3. 将文件描述符和struct file关联起来
    }
  }
  putname(tmp);
  return fd;
}

如何获取这个文件描述符呢?

// 在每一个进程的 task_struct 中,有一个指针 files,类型是 files_struct
struct files_struct {
  ......
  // 文件描述符列表,每打开一个文件,就会在这个列表中分配一项,下标就是文件描述符,都会有一个 struct file 对应
  struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};

对于任何一个进程,默认情况下,fd=0 表示 stdin 标准输入,fd=1 表示 stdout 标准输出,fd=2 表示 stderr 标准错误输出。

struct file *do_filp_open(int dfd, struct filename *pathname,
    const struct open_flags *op)
{
  ......
  set_nameidata(&nd, dfd, pathname); 
  filp = path_openat(&nd, op, flags | LOOKUP_RCU);
  ......
  restore_nameidata();
  return filp;
}

static struct file *path_openat(struct nameidata *nd, const struct open_flags *op, unsigned flags) { ...... file = get_empty_filp(); // 1. 生成一个 struct file 结构 ...... s = path_init(nd, flags); // 2. 初始化 struct nameidata,准备开始节点路径查找 ...... // 3.1 对于路径名逐层进行节点路径查找,这里面有一个大的循环,用“/”分隔逐层处理 while (!(error = link_path_walk(s, nd)) && // 3.2 获取文件对应的 inode 对象,并且初始化 file 对象 (error = do_last(nd, file, op, &opened)) > 0) { ...... } terminate_walk(nd); ...... return file; }
// 在 struct nameidata 里面有一个关键的成员变量 struct path struct path { struct vfsmount *mnt; // 和文件系统的挂载有关 struct dentry *dentry; // 除用于标识目录之外,还可以表示文件名,还会建立文件名及其 inode 之间的关联 } __randomize_layout;

例如,文件“/root/hello/world/data”,link_path_walk 会解析前面的路径部分“/root/hello/world”。

解析完毕的时候 nameidata 的 dentry = “/root/hello/world”,而 nameidata 的 filename = “data”。

 

static int do_last(struct nameidata *nd,
       struct file *file, const struct open_flags *op,
       int *opened)
{
  ......
  // 1. 先从缓存中查找dentry
  error = lookup_fast(nd, &path, &inode, &seq);
  ......
  // 2. 如果缓存中没有找到,就需要到文件系统里面去找
    error = lookup_open(nd, &path, file, op, got_write, opened);
  ......
  // 3. 调用 f_op->open,比如ext4_file_open打开文件;将打开文件的所有信息,填写到 struct file 里面
  error = vfs_open(&nd->path, file, current_cred());
  ......
}

static int lookup_open(struct nameidata *nd, struct path *path,
      struct file *file,
      const struct open_flags *op,
      bool got_write, int *opened)
{
    ......
    dentry = d_alloc_parallel(dir, &nd->last, &wq); // 1. 创建一个新的 dentry
    ......
    // 2. 调用上一级目录inode的 lookup 函数,比如 ext4_lookup,到文件系统里面去找 inode 
    struct dentry *res = dir_inode->i_op->lookup(dir_inode, dentry,
                   nd->flags);
    ......
    path->dentry = dentry; // 3. 将新生成的 dentry 赋给 path 变量
    path->mnt = nd->path.mnt;
}

int vfs_open(const struct path *path, struct file *file,
       const struct cred *cred)
{
  struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags, 0);
  ......
  file->f_path = *path;
  return do_dentry_open(file, d_backing_inode(dentry), NULL, cred);
}

static int do_dentry_open(struct file *f,
        struct inode *inode,
        int (*open)(struct inode *, struct file *),
        const struct cred *cred)
{
......
  f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
        FMODE_PREAD | FMODE_PWRITE;
  path_get(&f->f_path);
  f->f_inode = inode;
  f->f_mapping = inode->i_mapping;
......
  f->f_op = fops_get(inode->i_fop);
......
  open = f->f_op->open;
......
  error = open(inode, f);
......
  f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
  file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
  return 0;
......
}

 

const struct inode_operations ext4_dir_inode_operations = {
  .create    = ext4_create,
  .lookup    = ext4_lookup,
  ......
}

const struct file_operations ext4_file_operations = {
  ......
  .open    = ext4_file_open,
  ......
};

struct file {
  union {
    struct llist_node  fu_llist;
    struct rcu_head   fu_rcuhead;
  } f_u;
  struct path    f_path;
  struct inode    *f_inode;  /* cached value */
  const struct file_operations  *f_op;
  spinlock_t    f_lock;
  enum rw_hint    f_write_hint;
  atomic_long_t    f_count;
  unsigned int     f_flags;
  fmode_t      f_mode;
  struct mutex    f_pos_lock;
  loff_t      f_pos;
  struct fown_struct  f_owner;
  const struct cred  *f_cred;
  ......
  struct address_space  *f_mapping;
  errseq_t    f_wb_err;
}

 

 

上一篇:[转载]linux下利用inode(i节点号)删除指定文件


下一篇:Linux 基础