【write--->vfs_write--->do_sync_write--->generic_file_aio_write--->__generic_file_aio_write--->generic_file_buffered_write】
ssize_t generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos, loff_t *ppos,size_t count, ssize_t written)
{
struct file *file = iocb->ki_filp;
ssize_t status;
struct iov_iter i;
iov_iter_init(&i, iov, nr_segs, count, written);
status = generic_perform_write(file, &i, pos);
if (likely(status >= 0)) {
written += status;
*ppos = pos + status;
}
return written ? written : status;
}
在函数iov_iter_init中用待写入数据段初始化iov_iter对象并跳过已写入数据段,然后调用函数generic_perform_write将剩余数据段写入设备缓存。
【write--->vfs_write--->do_sync_write--->generic_file_aio_write--->__generic_file_aio_write--->generic_file_buffered_write--->generic_perform_write】
static ssize_t generic_perform_write(struct file *file,struct iov_iter *i, loff_t pos)
{
......
do {
......
status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
if (unlikely(status))
break;
if (mapping_writably_mapped(mapping))
flush_dcache_page(page);
pagefault_disable();
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
pagefault_enable();
flush_dcache_page(page);
mark_page_accessed(page);
status = a_ops->write_end(file, mapping, pos, bytes, copied,
page, fsdata);
......
} while (iov_iter_count(i));
return written ? written : status;
}
首先通过调用函数a_ops->write_begin为数据写入分配页缓存并为这个page准备一组buffer_head结构用于描述组成这个page的数据块,在函数 iov_iter_copy_from_user_atomic中将用户空间数据拷入该页缓存,然后调用函数a_ops->write_end将page中的每一个buffer_head标记为dirty。
file->f_mapping是从对应的inode->f_mapping而来,inode->f_mapping->a_ops是由对应的文件系统类型在生成这个inode时赋予的。在ext3文件系统中file->f_mapping->a_ops->write_begin和file->f_mapping->a_ops->write_end定义如下:
static const struct address_space_operations ext3_ordered_aops = {
.....
.write_begin = ext3_write_begin,
.write_end = ext3_ordered_write_end,
......
};
【write--->vfs_write--->do_sync_write--->generic_file_aio_write--->__generic_file_aio_write--->generic_file_buffered_write--->generic_perform_write--->ext3_write_begin】
static int ext3_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,struct page **pagep, void **fsdata)
{
......
index = pos >> PAGE_CACHE_SHIFT;
from = pos & (PAGE_CACHE_SIZE - 1);
to = from + len;
retry:
page = grab_cache_page_write_begin(mapping, index, flags);
if (!page)
return -ENOMEM;
*pagep = page;
......
ret = __block_write_begin(page, pos, len, ext3_get_block);
if (ret)
goto write_begin_failed;
......
return ret;
}
在函数grab_cache_page_write_begin中首先通过函数find_lock_page在radix树里面查找要被写的page,如果没找到就通过函数__page_cache_alloc分配一个page。函数__block_write_begin为这个page准备一组buffer_head结构,用于描述组成这个page的数据块。
【write--->vfs_write--->do_sync_write--->generic_file_aio_write--->__generic_file_aio_write--->generic_file_buffered_write--->generic_perform_write--->ext3_ordered_write_end】
static int ext3_ordered_write_end(struct file *file,struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,struct page *page, void *fsdata)
{
......
copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
......
if (ret == 0)
update_file_sizes(inode, pos, copied);
......
return ret ? ret : copied;
}
函数ext3_ordered_write_end就是函数block_write_end的封装。函数block_write_end的工作就是调用函数__block_commit_write将page的每一个buffer_head标记为dirty。然后更新文件的大小inode->i_size = i_size;
/**************************************************************/
当数据写入缓存之后等到合适的时候缓存中的数据将被写入设备,这写入设备的工作都将交给具体文件系统的writepage函数来完成。在ext3中函数实现如下:
【ext3_ordered_writepage】
static int ext3_ordered_writepage(struct page *page,
struct writeback_control *wbc)
{
......
ret = block_write_full_page(page, ext3_get_block, wbc);
......
return ret;
}
【ext3_ordered_writepage--->block_write_full_page_endio】
int block_write_full_page_endio(struct page *page, get_block_t *get_block,
struct writeback_control *wbc, bh_end_io_t *handler)
{
struct inode * const inode = page->mapping->host;
loff_t i_size = i_size_read(inode);
const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
unsigned offset;
if (page->index < end_index)
return __block_write_full_page(inode, page, get_block, wbc,handler);
offset = i_size & (PAGE_CACHE_SIZE-1);
if (page->index >= end_index+1 || !offset) {
do_invalidatepage(page, 0);
unlock_page(page);
return 0;
}
zero_user_segment(page, offset, PAGE_CACHE_SIZE);
return __block_write_full_page(inode, page, get_block, wbc, handler);
}
如果要写入的页在文件容量之内则直接将缓存页写入文件,如果缓存页超出了文件边界则将超出部分清零,然后将整页写入文件。文件大小在上面write_end中更新。
【ext3_ordered_writepage--->block_write_full_page_endio--->__block_write_full_page】
static int __block_write_full_page(struct inode *inode, struct page *page,
get_block_t *get_block, struct writeback_control *wbc,
bh_end_io_t *handler)
{
......
do {
struct buffer_head *next = bh->b_this_page;
if (buffer_async_write(bh)) {
submit_bh(write_op, bh);
nr_underway++;
}
bh = next;
} while (bh != head);
......
}
将缓存页中的每一个缓存块转化为bio,然后将bio添加到请求,等待数据请求得以处理。