首页 > FileSystem > Linux内核写文件流程

Linux内核写文件流程

FileSystem 2013-07-29

接上篇Linux内核读文件流程,写这篇Linux内核写文件流程。文中涉及的内核代码版本是linux内核版本号:3.0.13-0.27 sles11sp2版本。

用户态write函数到内核态的调用是:

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
 size_t, count)

SYSCALL_DEFINE3调用的vfs_write,vfs_write调用rw_verify_area检查当前是否有写权限,然后调用虚拟文件系统(vfs)的f_op指针write函数。f_op指针write函数在ext3_file_operations中定义的,定义为do_sync_write。


ret = rw_verify_area(WRITE, file, pos, count);
if (ret >= 0) {
     count = ret;
     if (file->f_op->write)
           ret = file->f_op->write(file, buf, count, pos);
     else
           ret = do_sync_write(file, buf, count, pos);
}

在write中函数定义了iov和kiocb,调用aio_write完成数据写入,aio_write是在ext3_file_operations中定义为generic_file_aio_write;然后等待wait_on_retry_sync_kiocb将数据刷新到磁盘。

for (;;) {
         ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
         if (ret != -EIOCBRETRY)
         break;
         wait_on_retry_sync_kiocb(&kiocb);
 }

generic_file_aio_write同样在开始检查各种锁等控制开关,直接通过__generic_file_aio_write将功能实现,但大部分检查是在__generic_file_aio_write里面测试的,如vfs_check_frozen检查文件系统是否有冻僵(不能写入),generic_write_checks权限块设备等检查。文件的修改时间也是在此处刷新的:file_update_time。然后检查是否有直接写入(O_DIRECT)的参数,O_DIRECT部分后面再说,如果没有则是generic_file_buffered_write,最后进入到generic_perform_write.

generic_perform_write也是文件写入的主要完成部分,整个部分是由一个do{}while循环完成的。首先是通过write_begin函数进行一些预处理工作,具体指向ext3_write_begin函数。ext3_write_begin首先用grab_cache_page_write_begin获取页高速缓存,调用__block_write_begin,在其中用get_block获取物理块号,同时启用journal机制。

当write_begin已经完成的时候,将用户态数据拷贝到内核态的page上。

 pagefault_disable();
 copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
 pagefault_enable();

通过write_end完成具体写入,由于ext3的journal有3中不同的写入方式,此处介绍默认的order方式,ext3_ordered_write_end函数首先调用block_write_end函数,其中调用__block_commit_write提交写入的数据,但事实上其提交只是对buff的状态做了处理,并没有其他大的操作。


set_buffer_uptodate(bh);
 mark_buffer_dirty(bh);

而后的walk_page_buffers只是通过journal_dirty_data_fn利用journal机制将数据标记为脏,然后用update_file_sizes更新文件所属inode的属性,并置为脏。

static void update_file_sizes(struct inode *inode, loff_t pos, unsigned copied)
{
	/* What matters to us is i_disksize. We don't write i_size anywhere */
	if (pos + copied > inode->i_size)
		i_size_write(inode, pos + copied);
	if (pos + copied > EXT3_I(inode)->i_disksize) {
		EXT3_I(inode)->i_disksize = pos + copied;
		mark_inode_dirty(inode);
	}
}

此时将inode添加到孤儿链表中,具体介绍见链接。

if (pos + len > inode->i_size && ext3_can_truncate(inode))
 ext3_orphan_add(handle, inode);

事实上前面都没有做刷新磁盘的操作,最后还是回到generic_perform_write函数,有balance_dirty_pages_ratelimited函数,顾名思义,平衡脏页比率,在此处进行脏页的刷新。另外一篇文章有详细介绍,点击Linux缓存写回机制

如此才完成了文件的写入过程。


Linux内核写文件流程来自于OenHan,链接为:http://oenhan.com/linux-kernel-write
更多阅读