文件系统最重要的就是读和写,在进行读和写之前,得索引到该文件,即知道要读写的文件的inode。怎么去获得到要读写文件的inode,就要用到lookup。在客户端这边,也就是发送请求,然后处理请求。但是这里涉及到元数据缓存,就稍微复杂点。
lookup流程(没命中缓存)
命中缓存逻辑
inode中有一个dir成员,如下
Dir *dir; // if i'm a dir.
Dir里面有一个dentries的map,此map就存有子目录、文件的名字和Dentry键值对
ceph::unordered_map<string, Dentry*> dentries;
Dentry中存有该目录/文件的Inode指针
InodeRef inode;
是否命中缓存,举个例子来说明,比如lookup /test/test1,就得先看test的inode->dir->dentries中是否有"test1"的key。
if (dir->dir && dir->dir->dentries.count(dname))
当然,光有"test1"的Dentry缓存也不行,也得看Dentry中Inode是否可用,即Inode中包含CEPH_STAT_CAP_INODE_ALL权限,且Inode中的caps没有过期。
dn->inode->caps_issued_mask(mask, true)
最后判断父目录"test"的inode是否有CEPH_CAP_FILE_SHARED权限,以及在缓存完"test1"的Dentry后,是否有其他客户端更改"test"目录的inode
if (dir->caps_issued_mask(CEPH_CAP_FILE_SHARED, true)) {
if (dn->cap_shared_gen == dir->shared_gen
当上面的条件满足时,才算命中缓存,这时"test"的inode只需从缓存里拿,无需再发请求找mds拿。
发送请求的内容
MetaRequest
struct MetaRequest {
private:
InodeRef _inode, _old_inode, _other_inode; // _inode为父目录(test)的inode指针
Dentry *_dentry; //associated with path, _dentry->dir是父目录的Dir,_dentry->name = "test1"
public:
ceph_mds_request_head head; // head.op = CEPH_MDS_OP_LOOKUP
// head.args.getattr.mask = CEPH_STAT_CAP_INODE_ALL
filepath path, path2; // path.ino = 0x1000012(父目录test的inode号), path.path = "test1"
......
ceph::cref_t<MClientReply> reply; // the reply
InodeRef target; // target是创建的目录的Inode指针,从mds的回复中组装而成。
}
MClientRequest
class MClientRequest : public Message {
public:
mutable struct ceph_mds_request_head head; // head.op = CEPH_MDS_OP_LOOKUP
// head.args.getattr.mask = CEPH_STAT_CAP_INODE_ALL
// path arguments
filepath path, path2; // path.ino = 0x1000012, path.path = "test1"
......
}
收到的回复内容
class MClientReply : public Message {
public:
// reply data
struct ceph_mds_reply_head head {};
/* client reply */
struct ceph_mds_reply_head {
......
__u8 is_dentry, is_target; /* true if dentry, target inode records are included with reply; is_dentry = 1, is_target = 1*/
}
bufferlist trace_bl; // trace_bl里面存着真正的信息,用于更新目的inode和父目录inode
}
处理回复的流程和之前mkdir的差不多。