全部博文(2759)
分类: LINUX
2015-07-18 10:07:33
原文地址:ext4文件系统分析 之 创建文件 作者:zsoftdevelop
创建文件时,代码并没有主动调用log commit提交日志,只是将需要修改的元数据信息写到日志handle。等handle所在的事务超时,log会自动提交,内存transaction内容会刷写到磁盘日志中。写入磁盘日志中的内容为新建文件的inode所在的数据块内容、新建文件父目录文件的inode所在的数据块内容、新建文件父目录文件相关数据块的内容。
static int ext4_create(struct
inode *dir, struct dentry *dentry, umode_t mode,
bool excl)
{
handle_t
*handle;
struct
inode *inode;
int err,
credits, retries = 0;
dquot_initialize(dir);/* 无操作,可忽略 */
/* 原子操作预期修改的缓冲区个数 */
credits
= (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
retry:
/* 创建inode,主要的函数,里面有保存日志内容的过程,需深入分析====>
*/
inode =
ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
NULL, EXT4_HT_DIR, credits);
handle = ext4_journal_current_handle();/* 获得当前进程的原子操作数据结构handle */
err =
PTR_ERR(inode);
if
(!IS_ERR(inode)) {
inode->i_op
= &ext4_file_inode_operations;
inode->i_fop
= &ext4_file_operations;
/* 根据日志模式,设置文件系统挂载选项参数;
* 设置address_space_operations操作函数;
====>
*/
ext4_set_aops(inode);
/* 将inode和dentry建立关联,并且保存日志信息====>
*/
err
= ext4_add_nondir(handle, dentry, inode);
if
(!err && IS_DIRSYNC(dir))
ext4_handle_sync(handle);
}
/* 断开handle和transaction的链接,当事务提交时,缓冲区数据刷到磁盘 */
if
(handle)
ext4_journal_stop(handle);
if (err
== -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
goto
retry;
return
err;
}
ext4_create是创建文件功能的函数。
首先设置原子操作handle需要的缓冲区个数;
然后使用函数ext4_new_inode_start_handle创建文件inode,其中包括记录日志内容等,但是比较复杂,所以日后在做分析;
然后设置文件系统日志模式和相关操作函数指针;
然后使用函数ext4_add_nondir将inode和dentry建立关联,并且保存日志信息,这个函数下面再做解析;
最后调用ext4_journal_stop断开handle和事务transaction的链接,此时,当该事务transaction提交时,事务缓冲区中的数据会刷写到磁盘。
ext4_add_nondir将inode和dentry建立关联,并且保存日志信息。
首先调用ext4_add_entry函数将文件的dentry加入到哈希目录树,并且将文件父目录的inode和父目录文件的相关数据块置为dirty;
然后调用函数ext4_mark_inode_dirty将新建文件的inode置为dirty;
最后将新建文件的dentry和inode建立关联。
static int ext4_add_nondir(handle_t *handle, struct dentry *dentry, struct inode *inode) { int err = ext4_add_entry(handle, dentry, inode);/* 文件entry加入哈希目录树,并且修改日志====> */ if (!err) { ext4_mark_inode_dirty(handle, inode);/* 修改日志,将文件inode置为dirty====> */ unlock_new_inode(inode); d_instantiate(dentry, inode);/* 将dentry和inode建立关联,主要是设置对应的成员变量 */ return 0; } drop_nlink(inode); unlock_new_inode(inode); iput(inode);/* 将inode放入lru缓存链表,供以后使用 */ return err; } |
static int ext4_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode) { struct inode *dir = dentry->d_parent->d_inode; struct buffer_head *bh; struct ext4_dir_entry_2 *de; struct ext4_dir_entry_tail *t; struct super_block *sb; int retval; int dx_fallback=0; unsigned blocksize; ext4_lblk_t block, blocks; int csum_size = 0;
if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext4_dir_entry_tail);
sb = dir->i_sb; blocksize = sb->s_blocksize; if (!dentry->d_name.len) return -EINVAL;
/* inode结构体中是否具有数据,ext4支持inline data功能 */ if (ext4_has_inline_data(dir)) { retval = ext4_try_add_inline_entry(handle, dentry, inode); if (retval < 0) return retval; if (retval == 1) { retval = 0; return retval; } }
if (is_dx(dir)) { /* 将目录添加到哈希目录树 * 同时文件父目录的inode置为dirty,父目录的相关数据块置为dirty * ====> */ retval = ext4_dx_add_entry(handle, dentry, inode); 。。。。。。省略代码 } |
static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode) { struct dx_frame frames[2], *frame; struct dx_entry *entries, *at; struct dx_hash_info hinfo; struct buffer_head *bh; struct inode *dir = dentry->d_parent->d_inode; struct super_block *sb = dir->i_sb; struct ext4_dir_entry_2 *de; int err;
frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, &err); if (!frame) return err; entries = frame->entries; at = frame->at; bh = ext4_read_dirblock(dir, dx_get_block(frame->at), DIRENT); if (IS_ERR(bh)) { err = PTR_ERR(bh); bh = NULL; goto cleanup; }
BUFFER_TRACE(bh, "get_write_access"); err = ext4_journal_get_write_access(handle, bh); if (err) goto journal_error;
err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); 。。。。。。省略代码 } |
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) { struct ext4_iloc iloc; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); static unsigned int mnt_count; int err, ret;
might_sleep(); trace_ext4_mark_inode_dirty(inode, _RET_IP_);
/* 得到inode所在的磁盘块bh,然后jbd2获得对bh的写权限 */ err = ext4_reserve_inode_write(handle, inode, &iloc); if (ext4_handle_valid(handle) && EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize && !ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) { /* * We need extra buffer credits since we may write into EA block * with this same handle. If journal_extend fails, then it will * only result in a minor loss of functionality for that inode. * If this is felt to be critical, then e2fsck should be run to * force a large enough s_min_extra_isize. */ if ((jbd2_journal_extend(handle, EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) { ret = ext4_expand_extra_isize(inode, sbi->s_want_extra_isize, iloc, handle); if (ret) { ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); if (mnt_count != le16_to_cpu(sbi->s_es->s_mnt_count)) { ext4_warning(inode->i_sb, "Unable to expand inode %lu. Delete" " some EAs or run e2fsck.", inode->i_ino); mnt_count = le16_to_cpu(sbi->s_es->s_mnt_count); } } } } if (!err) err = ext4_mark_iloc_dirty(handle, inode, &iloc); return err; } |