07| OCFS2文件创建和打开

528 阅读5分钟

在文件系统中,在当前进程中,由fd获取struct file对象,进而获得struct inode对象。

current->files->fdt->fd[fd]->f_inode

其中涉及到主要数据结构如下图所示:

struct task_structcurrent-> struct files_struct *files -> struct fdtable fdtab -> struct file __rcu *fd[fd] -> struct file -> struct inode *f_inode

这些数据结构相互嵌套,共同构成了文件系统对文件的描述。

image (27).png

一、mount时建ocfs2根目录和系统目录:

static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb)
    |-struct inode *new = ocfs2_iget(osb, osb->root_blkno, OCFS2_FI_FLAG_SYSFILE, 0);
    |-osb->root_inode = new;
 
    |-new = ocfs2_iget(osb, osb->system_dir_blkno, OCFS2_FI_FLAG_SYSFILE, 0);
    |-osb->sys_root_inode = new;
 
    |-if (osb->root_inode)
        inode = igrab(osb->root_inode);
    |-struct dentry *root = d_make_root(inode);
        |-if (root_inode) {
            res = d_alloc_anon(root_inode->i_sb);//申请目录项dentry,并初始化,返回。
                  |-struct dentry *dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
                  |-dentry初始化
                  |-return dentry;
        if (res)
            d_instantiate(res, root_inode);//为目录项dentry添加inode信息
        else
            iput(root_inode);
    |-}

主要流程:

1、新申请一个inode,并初始化,将其记录到全局inode哈希表inode_hashtable和superblock上&inode->i_sb->s_inodes

2、初始化锁资源ip_inode_lockres、ip_open_lockres

2、初始化ocfs2 inode信息,根据文件类型S_IFREG://普通文件,关联file ops;S_IFDIR://目录文件,关联inode->i_op = &ocfs2_dir_iops,初始化日志journal

3、申请目录项dentry,并初始化,为目录项dentry添加inode信息。

其中ocfs2_iget()是重点:

struct inode *new = ocfs2_iget(osb, osb->root_blkno, OCFS2_FI_FLAG_SYSFILE, 0);
|-struct inode *inode = iget5_locked(sb, args.fi_ino, ocfs2_find_actor, ocfs2_init_locked_inode, &args);//新申请一个inode,加锁并初始化,将其记录到全局inode哈希表inode_hashtable和superblock上&inode->i_sb->s_inodes
    |-struct inode *new = alloc_inode(sb);
    |-inode = inode_insert5(new, hashval, test, set, data);
        |-inode->i_state |= I_NEW;
        |-将inode记录到全局变量inode_hashtable和superblock上&inode->i_sb->s_inodes
|-if (inode->i_state & I_NEW) {
    |-rc = ocfs2_read_locked_inode(inode, &args);
        |-ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_inode_lockres, OCFS2_LOCK_TYPE_META,
                    generation, inode);//初始化meta锁资源
        |-ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_open_lockres, OCFS2_LOCK_TYPE_OPEN, 0, inode);
        |-status = ocfs2_read_blocks_sync(osb, args->fi_blkno, 1, &bh);
            |-submit_bh(REQ_OP_READ, 0, bh);//读磁盘操作
        |-status = ocfs2_xxx_validate_inode_block(osb->sb, bh);//根据flags设置调用函数校验block块
        |-struct ocfs2_dinode *fe = (struct ocfs2_dinode *) bh->b_data;
        |-ocfs2_populate_inode(inode, fe, 0);//初始化ocfs2 inode
    |-unlock_new_inode(inode);
|-}
|-if (journal) {初始化日志journal
    |-transaction_t *transaction;
    |-tid_t tid;
    |-struct ocfs2_inode_info *oi = OCFS2_I(inode);
    |-read_lock(&journal->j_state_lock);
    |-if (journal->j_running_transaction)
        transaction = journal->j_running_transaction;
    |-else
        transaction = journal->j_committing_transaction;
    |-if (transaction)
        tid = transaction->t_tid;
    |-else
        tid = journal->j_commit_sequence;
    |-read_unlock(&journal->j_state_lock);
    |-oi->i_sync_tid = tid;
    |-oi->i_datasync_tid = tid;
|-}
|-return inode;

初始化一个inode,关联地址空间操作ocfs2_aops,根据文件类型S_IFREG://普通文件,关联file ops;S_IFDIR://目录文件,关联inode->i_op = &ocfs2_dir_iops,

void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, int create_ino)
|-初始化ocfs2 inode信息
|-inode->i_mapping->a_ops = &ocfs2_aops;
|-switch (inode->i_mode & S_IFMT) {
    |-case S_IFREG://普通文件
       |-if (use_plocks)
           inode->i_fop = &ocfs2_fops;
        |-else
           inode->i_fop = &ocfs2_fops_no_plocks;
        |-inode->i_op = &ocfs2_file_iops;
        |-i_size_write(inode, le64_to_cpu(fe->i_size));
        |-break;
    |-case S_IFDIR://目录文件
       |-inode->i_op = &ocfs2_dir_iops;
       |-if (use_plocks)
           inode->i_fop = &ocfs2_dops;
       |-else
           inode->i_fop = &ocfs2_dops_no_plocks;
       |-i_size_write(inode, le64_to_cpu(fe->i_size));
       |-OCFS2_I(inode)->ip_dir_lock_gen = 1;
       |-break;
    |-case S_IFLNK://链接文件
       |-inode->i_op = &ocfs2_symlink_inode_operations;
       |-inode_nohighmem(inode);
       |-i_size_write(inode, le64_to_cpu(fe->i_size));
       |-break;
    |-default:
       |-inode->i_op = &ocfs2_special_file_iops;
       |-init_special_inode(inode, inode->i_mode, inode->i_rdev);
       |-break;
    |-}
    |-ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_rw_lockres, //初始化读写RW锁资源
                                OCFS2_LOCK_TYPE_RW, inode->i_generation, inode);

这样根目录便从磁盘上读到内存,初始化并挂接了ocfs2_dir_iops,如下所示:

const struct inode_operations ocfs2_dir_iops = {
    .create     = ocfs2_create,
    .lookup     = ocfs2_lookup,
    .link       = ocfs2_link,
    .unlink     = ocfs2_unlink,
    .rmdir      = ocfs2_unlink,
    .symlink    = ocfs2_symlink,
    .mkdir      = ocfs2_mkdir,
    .mknod      = ocfs2_mknod,
    .rename     = ocfs2_rename,
    .setattr    = ocfs2_setattr,
    .getattr    = ocfs2_getattr,
    .permission = ocfs2_permission,
    .listxattr  = ocfs2_listxattr,
    .fiemap         = ocfs2_fiemap,
    .get_acl    = ocfs2_iop_get_acl,
    .set_acl    = ocfs2_iop_set_acl,
};

二、在用户态调用open打开文件:

image (28).png

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
    if (force_o_largefile())
        flags |= O_LARGEFILE;
    return do_sys_open(AT_FDCWD, filename, flags, mode);
}
SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode)
{
    int flags = O_CREAT | O_WRONLY | O_TRUNC;
    if (force_o_largefile())
        flags |= O_LARGEFILE;
    return do_sys_open(AT_FDCWD, pathname, flags, mode);
}

主要流程:

1、获取一个未使用的fd;

2、申请一个未使用的 struct file对象,关联文件inode:f->f_inode = inode及操作f->f_op = fops_get(inode->i_fop),调vfs_open() -> 具体文件系统提供的open()函数;

3、报文件已打开事件;

4、将fd记录到fdt数组中,把fd与file关联起来;

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
    struct open_how how = build_open_how(flags, mode);
    return do_sys_openat2(dfd, filename, &how);
           |-int fd = get_unused_fd_flags(how->flags);//获取一个未使用的fd
           |-struct file *f = do_filp_open(dfd, tmp, &op);//申请一个未使用的file struct,调vfs_open() -> 具体文件系统提供的open()函数
               |-set_nameidata(&nd, dfd, pathname);
               |-struct file *filp = path_openat(&nd, op, flags | LOOKUP_RCU);
                   |-struct file *file = alloc_empty_file(op->open_flag, current_cred());//申请一个未使用的file struct,并返回指针。
                       |-struct file *f = __alloc_file(flags, cred);
                   |-const char *s = path_init(nd, flags);
                   |-error = do_open(nd, file, op);
                       |-if (!error && !(file->f_mode & FMODE_OPENED))
                           |-error = vfs_open(&nd->path, file); //call vfs_open - open the file at the given path
                               |-file->f_path = *path;
                               |-return do_dentry_open(file, d_backing_inode(path->dentry), NULL);
                                   |-f->f_op = fops_get(inode->i_fop);
                                   |-open = f->f_op->open;
                                   |-error = open(inode, f); //这里就调打开文件open函数
                   |-return filp;
           |-fsnotify_open(f); //file was opened
           |-fd_install(fd, f); //Install a file pointer in the fd array.
               |-struct files_struct *files = current->files;
               |-struct fdtable *fdt = files_fdtable(files);
               |-smp_rmb();
               |-fdt = rcu_dereference_sched(files->fdt);
               |-BUG_ON(fdt->fd[fd] != NULL);
               |-rcu_assign_pointer(fdt->fd[fd], file);//把fd与file关联起来。
           |-return fd;
}

ocfs2内核open:初始化文件私有数据:初始化flock锁资源。

static int ocfs2_file_open(struct inode *inode, struct file *file)
    |-struct ocfs2_inode_info *oi = OCFS2_I(inode);
    |-if (file->f_mode & FMODE_WRITE) {
        status = dquot_initialize(inode);
    |-if (mode & O_DIRECT)
        oi->ip_flags |= OCFS2_INODE_OPEN_DIRECT;
  |-oi->ip_open_count++;
  |-status = ocfs2_init_file_private(inode, file);
        |-struct ocfs2_file_private *fp = kzalloc(sizeof(struct ocfs2_file_private), GFP_KERNEL);
    |-ocfs2_file_lock_res_init(&fp->fp_flock, fp);//初始化flock锁资源
            |-ocfs2_lock_res_init_once(lockres);
        |-ocfs2_build_lock_name(OCFS2_LOCK_TYPE_FLOCK, oi->ip_blkno, inode->i_generation, lockres->l_name);
        |-ocfs2_lock_res_init_common(OCFS2_SB(inode->i_sb), lockres, OCFS2_LOCK_TYPE_FLOCK, &ocfs2_flock_lops, fp);
        |-lockres->l_flags |= OCFS2_LOCK_NOCACHE;
    |-file->private_data = fp; //记录到文件file私有数据
    |-file->f_mode |= FMODE_NOWAIT;

文件打开成功。

三、通过mknod创建文件、块设备

SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev)
{
    return do_mknodat(AT_FDCWD, filename, mode, dev);
}

static long do_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned int dev)
|-struct dentry *dentry = user_path_create(dfd, filename, &path, lookup_flags);
|-error = security_path_mknod(&path, dentry, mode, dev);
    switch (mode & S_IFMT) {
        case 0: case S_IFREG: //普通文件
            error = vfs_create(path.dentry->d_inode,dentry,mode,true);
                   |-error = dir->i_op->create(dir, dentry, mode, want_excl); //调用目录dir inode的create方法
            break;
        case S_IFCHR: case S_IFBLK: //字符设备文件/块设备文件
            error = vfs_mknod(path.dentry->d_inode,dentry,mode, new_decode_dev(dev));
                   |-error = dir->i_op->mknod(dir, dentry, mode, dev); //调用目录dir inode的mknod方法
            break;
        case S_IFIFO: case S_IFSOCK:
            error = vfs_mknod(path.dentry->d_inode,dentry,mode,0);
            break;
    }
|-done_path_create(&path, dentry);

ocfs2_create/ocfs2_mkdir均调用了ocfs2_mknod

.create        = ocfs2_create,
static int ocfs2_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
    |-ret = ocfs2_mknod(dir, dentry, mode | S_IFREG, 0);
 
.mkdir     = ocfs2_mkdir,
static int ocfs2_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
    |-ret = ocfs2_mknod(dir, dentry, mode | S_IFDIR, 0);

OCFS2内核mknode处理:

.mknod      = ocfs2_mknod,
static int ocfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|-struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);//获取ocfs2 super block
|-status = ocfs2_inode_lock(dir, &parent_fe_bh, 1);//对ip_inode_lockres锁资源加EX锁
|-status = ocfs2_prepare_dir_for_insert(osb, dir, parent_fe_bh, dentry->d_name.name, dentry->d_name.len, &lookup);//get a spot inside the dir.
|-status = ocfs2_reserve_new_inode(osb, &inode_ac); //reserve an inode spot, 获取分配inode alloc context上下文
|-struct inode *inode = ocfs2_get_init_inode(dir, mode);
|-status = ocfs2_reserve_new_metadata_blocks(osb, want_meta, &meta_ac); //预留meta
|-status = ocfs2_reserve_clusters(osb, want_clusters, &data_ac); //预留data
|-handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb, S_ISDIR(mode), xattr_credits));//启动日志journal:jbd2
|-status = ocfs2_mknod_locked(osb, dir, inode, dev, &new_fe_bh, parent_fe_bh, handle, inode_ac);//do the real work now.
    |-status = ocfs2_claim_new_inode(handle, dir, parent_fe_bh, inode_ac, &suballoc_loc, &suballoc_bit, &fe_blkno);
    |-status = __ocfs2_mknod_locked(dir, inode, dev, new_fe_bh, parent_fe_bh, handle, inode_ac, fe_blkno, suballoc_loc, suballoc_bit);
        |-初始化ocfs2_dinode *fe
        |-ocfs2_populate_inode(inode, fe, 0);
            |-ocfs2_inode_lock_res_init(&OCFS2_I(inode)->ip_rw_lockres, //初始化读写RW锁资源
                         OCFS2_LOCK_TYPE_RW, inode->i_generation, inode);
        |-status = ocfs2_create_new_inode_locks(inode);
            |-ret = ocfs2_create_new_lock(osb, &OCFS2_I(inode)->ip_rw_lockres, 1, 1);
            |-ret = ocfs2_create_new_lock(osb, &OCFS2_I(inode)->ip_inode_lockres, 1, 0);
            |-ret = ocfs2_create_new_lock(osb, &OCFS2_I(inode)->ip_open_lockres, 0, 0);
|-status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, parent_fe_bh, meta_ac, data_ac);
|-status = ocfs2_add_entry(handle, dentry, inode, OCFS2_I(inode)->ip_blkno, parent_fe_bh, &lookup);
|-insert_inode_hash(inode);//插入inode哈希表inode_hashtable
|-d_instantiate(dentry, inode);

一个文件inode就创建完成。