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; }