NFS系统write调用过程(一)

1.nfs_file_write

NFS文件系统中write操作过程和read操作过程类似,区别在于read操作从服务器读取数据到客户端,write操作将数据从客户端写到服务器中。NFS文件系统中write操作的处理函数是nfs_file_write()。

ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
		       unsigned long nr_segs, loff_t pos)
{
	struct dentry * dentry = iocb->ki_filp->f_path.dentry;	// 找出文件的目录项结构
	struct inode * inode = dentry->d_inode;		// 这是文件的索引节点
	unsigned long written = 0;
	ssize_t result;
	size_t count = iov_length(iov, nr_segs);	// 计算用户态缓存的总长度
	struct nfs_inode *nfsi = NFS_I(inode);

	// 步骤1  判断是否是直接IO
	if (iocb->ki_filp->f_flags & O_DIRECT)		// 这是直接写
		return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);

	// 下面就是缓存写了
	dprintk("NFS: write(%s/%s, %lu@%Ld)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name,	// 父目录名称,文件名称
		(unsigned long) count, (long long) pos);	// 需要写的数据量,数据在文件中的位置

	result = -EBUSY;
	if (IS_SWAPFILE(inode))
		goto out_swapfile;
	/*
	 * O_APPEND implies that we must revalidate the file length.
	 */
	// 步骤2  追加写时需要先更新文件长度
	if (iocb->ki_filp->f_flags & O_APPEND) {	// 追加写
		// 追加写操作之前需要先获取文件长度,因为文件长度已经发生了变化,
		// 需要重新确定文件长度。这个函数向服务器发起了GETATTR请求。
		result = nfs_revalidate_file_size(inode, iocb->ki_filp);
		if (result)
			goto out;	// 获取文件属性过程出错了
	}

	result = count;
	if (!count)	// 没有需要写的数据,不处理了,直接返回.
		goto out;

	// 步骤3  这是缓存写的处理函数
	// 这就是通用的文件写操作方法,这个函数会调用write_begin和write_end。
	result = generic_file_aio_write(iocb, iov, nr_segs, pos);
	if (result > 0)
		written = result;	// 这是写入的数据量

	/* Return error values for O_DSYNC and IS_SYNC() */
	// 步骤4  将数据刷新到服务器中
	if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) {
		int err = vfs_fsync(iocb->ki_filp, 0);		// 同步数据,并且同步元数据
		if (err < 0)
			result = err;
	}

	// 步骤5  更新统计信息
	if (result > 0)
		nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
out:
	return result;

out_swapfile:
	printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
	goto out;
}

这个函数的总体流程还是比较清晰的,分为下面5个步骤

步骤1:判断是否是直接IO,如果是直接IO则调用nfs_file_direct_write()处理。

步骤2:追加写时先更新文件长度。因为多个客户端可以挂载同一个文件系统,其他客户端可能已经修改了文件,追加写时需要先确定现在文件结尾的位置,从这个位置写入数据。更新文件长度是由nfs_revalidate_file_size()完成的,这个函数最终发起了GETATTR请求,不仅更新了文件长度,还更新了文件其他的属性信息。写操作中只有追加写之前更新了文件属性,如果不是追加写就没有更新文件属性。估计这样设计的初衷是本地可能已经修改了文件数据,但是还没有提交到服务器中。如果这时更新文件属性的话,以前修改的数据就丢失了。

步骤3:调用generic_file_aio_write()将数据写入缓存页中。这是VFS层的函数,调用了文件系统的write_begin和write_end函数,后面会分析这两个函数。

步骤4:将数据刷新到服务器中。步骤3只是将数据写入到了客户端的缓存页中,并没有传输到服务器中。步骤4中检查是否需要马上就数据传输到服务器中,如果需要马上传送则向服务器发起WRITE请求,否则由客户端缓存管理进程定期刷新脏的缓存页。

步骤5:更新统计信息,这就没什么说的了。

2.nfs_write_begin

generic_file_aio_write()的作用是将用户态缓冲区中的数据写入到内核态缓存页中,这是VFS层的通用函数,这个函数包含三个步骤:

步骤1:准备内核态缓存页,比如分配新的缓存页,这是由文件系统的write_begin函数完成的,NFS中这个函数是nfs_write_begin()。

步骤2:将数据从用户态缓冲区中拷贝到内核态缓存页中,这是由函数iov_iter_copy_from_user_atomic()实现的,这是一个通用函数,就不讲了。

步骤3:进行一些后续处理,比如将缓存页标记为脏,这是由文件系统的write_end函数完成的,NFS中这个函数是nfs_write_end()。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值