今天中午美美的睡了一觉,借着突破readdir的基础,迅速看懂了NFSD端的read代码。想想之前看不懂时候,面对满屏幕的繁杂数据结构,完全失去了思考能力。
分析代码之前,先总结一下,希望经过这次拖沓的一个月 读代码,以后面对稍稍复杂庞大的代码,能够迅速理出头绪。
1.首先要从宏观理解代码的结构和流程,这一点其实很快就做到了。
2.面对较多的变量和数据结构定义以及较多的函数调用,可以通过做笔记,从函数调用和数据传递两条线索整理出整个流程。进度之所以非常缓慢,就是因为被众多的定义和函数调用吓着了,不能一口吃掉,但是也没有耐心一点点蝉食。
3.用source insight方便了代码追踪,大大加快了进度。
结论:下次看代码,用source insight,复杂的流程整理笔记。
言归正传,下面说read流程。
struct nfsd3_readargs {
struct svc_fh fh;
__u64 offset; 指的是在文件中的偏移量
__u32 count; 指的是要读的字节数量,其实在rqst中vec中也包含了个读多少的数量
int vlen; 指明在rqst中,vec向量的个数。
};
resp->count = argp->count;
if (max_blocksize < resp->count)
resp->count = max_blocksize;
svc_reserve_auth(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4);
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_read(rqstp, &resp->fh, NULL,
argp->offset,
rqstp->rq_vec, argp->vlen,
&resp->count);
if (nfserr == 0) {
struct inode *inode = resp->fh.fh_dentry->d_inode;
resp->eof = (argp->offset + resp->count) >= inode->i_size;
}
nfsd_read()完成权限检查等工作,然后调用 nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count);
nfsd_vfs_read函数 先获取预读策略,这些不关心。。。
函数调用host_err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset);
*count = host_err; 此处count是resp中的值,也就是说根据实际读出多少,修改了count
ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
unsigned long vlen, loff_t *pos)
{
if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!file->f_op || (!file->f_op->aio_read && !file->f_op->read))
return -EINVAL;
return do_readv_writev(READ, file, vec, vlen, pos);
}
直接看 do_readv_writev()函数。
static ssize_t do_readv_writev(int type, struct file *file,
const struct iovec __user * uvector,
unsigned long nr_segs, loff_t *pos)
{
size_t tot_len;
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov = iovstack;
ssize_t ret;
io_fn_t fn;
iov_fn_t fnv;
ret = rw_copy_check_uvector(type, uvector, nr_segs,ARRAY_SIZE(iovstack), iovstack, &iov);
rw_copy_check_uvector这个函数完成把调用VFS层时,开辟好的buffer指针(在NFS中,具体指的是rqst中kvec[]数组,其中有指针和长度len)拷贝到内核空间(由于内核不能直接访问用户空间),注意,此处拷贝的是内存地址值,而不是内存空间。nr_segs就是argp中的vlen。
转换之后,就是用iov表征具体的buffer地址了。
之后就是
ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn);
struct iovec *vector = iov;
ssize_t ret = 0;
while (nr_segs > 0) {
void __user *base;
size_t len;
ssize_t nr;
base = vector->iov_base;
len = vector->iov_len;
vector++;
nr_segs--;
nr = fn(filp, base, len, ppos);
if (nr < 0) {
if (!ret)
ret = nr;
break;
}
ret += nr;
if (nr != len)
break;
}
return ret;
}
上述代码中,while()循环,每次填充向量数组中的一个元素,完成填充。