mkdir就是创建目录,客户端并不直接创建目录,而是将mkdir的请求(op为CEPH_MDS_OP_MKDIR)发给MDS,然后MDS执行mkdir的操作,并返回创建的目录的元数据。客户端无非就是发送请求和处理回复。例子 mkdir /mnt/ceph-fuse/test
发送请求的流程
![5bd4f22b6811c1070818f35612cedae1.png](https://i-blog.csdnimg.cn/blog_migrate/57ac1e73c2e7227ddc3f763f4e3c72e7.jpeg)
发送请求的内容
两类请求:MetaRequest,MClientRequest。
MetaRequest的大部分内容都是在make_request和send_request中填充,所以各种op填充的内容都差不多,只研究不同的地方。
struct MetaRequest {
private:
InodeRef _inode, _old_inode, _other_inode; // _inode为创建目录的父目录的inode指针
// 这里_inode->ino = 1
Dentry *_dentry; //associated with path, _dentry->dir是父目录的Dir,_dentry->name = "test"
public:
ceph_mds_request_head head; // head.op = CEPH_MDS_OP_MKDIR
filepath path, path2; // path.ino = 0x1(父目录的inode号), path.path = "test"
......
int dentry_drop, dentry_unless; // dentry_drop = CEPH_CAP_FILE_SHARED = "Fs",在send_request过程中,会释放掉父目录的Inode的caps的"Fs"权限
// dentry_unless = CEPH_CAP_FILE_EXCL = "Fx"
vector<MClientRequest::Release> cap_releases; // cap_releases.push_back(MClientRequest::Release(rel,""))。?
......
ceph::cref_t<MClientReply> reply; // the reply
//possible responses
bool got_unsafe; // 收到unsafe的回复时,got_unsafe为true
xlist<MetaRequest*>::item item; // 插入到session的requests链表中
xlist<MetaRequest*>::item unsafe_item; // 收到unsafe回复后,插入到session的unsafe_requests链表中。
xlist<MetaRequest*>::item unsafe_dir_item; // 收到unsafe回复且涉及到父目录操作(在父目录下创建/删除文件/目录),插入到父目录Inode的unsafe_ops链表中
xlist<MetaRequest*>::item unsafe_target_item; // 收到unsafe回复且请求需要获取目的inode信息,插入到自己Inode的unsafe_ops链表中
// 上述4个链表节点,都在收到safe回复后,会将链表节点从各自的链表中删除
InodeRef target; // target是创建的目录的Inode指针,从mds的回复中组装而成。
}
MClientRequest的内容是在通用函数build_client_request和send_request函数中填充的,所以大部分内容都差不多
class MClientRequest : public Message {
public:
mutable struct ceph_mds_request_head head; // head.op = CEPH_MDS_OP_MKDIR
// head.flags = CEPH_MDS_FLAG_WANT_DENTRY
// path arguments
filepath path, path2; // path.ino = 0x1(父目录的inode号), path.path = "test"
......
}
从代码可以看出,发送给mds的请求最重要的就是两个:op,不同的op,处理机制不同;filepath path,path.ino是父目录的inode号,path.path就是需要创建的目录名。通过这两个,mds就知道在哪个目录下创建目录。
处理请求的流程
![318a163c060002ed23b531f8ba244d8f.png](https://i-blog.csdnimg.cn/blog_migrate/3f91653949445a0afcff7f2dd11cdb35.jpeg)
class MClientReply : public Message {
public:
// reply data
struct ceph_mds_reply_head head {};
/* client reply */
struct ceph_mds_reply_head {
__le32 op;
__le32 result;
__le32 mdsmap_epoch;
__u8 safe; /* true if committed to disk; 用来判断是否已经下刷了disk,或者不需要下刷时,safe就为1*/
__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
}
最后"test"目录inode的cap.issued == "pAsxLsXsxFsx", cap.implemented == "pAsxLsXsxFsx"
后记
在linux中同一目录下的子目录和文件名是不能相同的,如test/目录下就不能有"test1"的目录和"text1"的文件。这是为啥,在看过lookup之后,就知道答案了,举例说明:比如我们要mkdir /test/test1: 先进行索引,即lookup 0x1/test,获得test的inode,这里假设test的inode号为0x2, 接下来再lookup 0x2/test1, 即获取test目录下"test1"的Dentry,然后从Dentry中获得Inode,假设在mkdir /test/test1之前,已经有了一个test1的文件,那么这时lookup 0x2/test1会获得test1文件的Inode,lookup返回的结果是0,这是mkdir就报错:文件或目录已存在。