yfs是一个分布式文件系统,第一步先实现一个锁服务

前言

YFS的架构: 在这里插入图片描述

YFS模块实现核心文件系统逻辑,这个模块作为yfs_client的单个进程运行,该进程支持本地主机上的挂载点,该模块有两部分组成: 1. FUSE接口:在fuse.cc中,它将FUSE操作从FUSE内核模块转换为YES客户端调用。 2. YES客户端:yfs_client.{cc,h},YES实现客户端文件系统逻辑,使用锁和扩展服务器来帮助它。 例如,在创建新文件时,yfs_client`会将目录条目添加到扩展服务器的目录块中。

扩展服务器存储文件系统所有的数据,类似于不同文件系统的硬盘。在将来的实验中,可以在多个主机上运行YES客户端,所有主机都与同一个扩展服务器通信; 效果就是所有主都能看到并共享同一个文件系统。 多个YES客户端可以共享数据的唯一方法就是通过扩展服务器。 扩展服务器由两部分构成: 1. extent_client,使用RPC与extent server进行通信的包装类 2. extent_server,扩展服务器管理一个简单的键值存储,以extent_protocol::extentid_t为键,以string以及对应的属性为值。

Part1:CREATE/MKNOD, LOOKUP, and READDIR

Part2:SETATTR,READ,WRITE

这两个部分完成的代码是类似的,只是实现的功能不同。

  1. 首先完成extent_server.{h,cc},这个服务器要实现一个简单的键值对存储,能实现putgetgetattrremove的功能。这些方法会在之后实现yfs_client时用到,例如要创建一个文件,则需要get一个目录,然后put创建一个文件,最后put将创建的文件放入该目录。
  2. 接着实现yfs_client.{h,cc},要能实现上面标题所显示的六种方法,最重要的是能理解inum是什么及有什么用,每个文件或者目录都有唯一的inum,因此还要实现一个产生唯一的inum的函数。
  3. 最后就是实现fuse.cc了,参考注释很快就能完成了,这里会调用到上一步骤实现的函数。

调试

create错误

test-lab-2-a: cannot create ./yfs1/file-yxfsroxwdfzzniqgxqzgxjpyxtwdievwhenavyda-24012-0 : No such file or directory

第一次create就失败了,检查一下有关create的代码,没发现有什么问题。回去再看一遍实验指导,发现有这样一句话:

FUSE假定根目录的inum是0x00000001。因此,您需要确保在yfs_client启动时,它已准备好导出存储在该inum下的空目录。

因此要在extent_server的构造函数中加上两行:

  int ret;
  put(1, "", ret);

open错误

test-lab-2-a: cannot open ./yfs1/file-nmhxcnnviodnsyfqcasqiorffhpoxtetbrhanteo-30075-198 : No such file or directory

经检查,发现了一个错误,在创建文件时,只是创建了文件,但并没有与目录绑定。 在第二个调用put中要是传入参数的目录的inum

int
yfs_client::create(inum parent, const char* name, inum& inum)
{
  std::string data;
  std::string file_name;
  if(ec->get(parent, data) != extent_protocol::OK)
  {
    return IOERR;
  }

  file_name = "/" + std::string(name) + "/";
  // 文件已经存在
  if (data.find(file_name) != std::string::npos)
  {
    return EXIST;
  }

  inum = random_inum(true);
  if(ec->put(inum, std::string()) != extent_protocol::OK)
  {
    return IOERR;
  }

  data.append(file_name + filename(inum) + "/");
  if(ec->put(parent, data) != extent_protocol::OK)
  {
    return IOERR;
  }

  return OK;
}

测试

$ ./start.sh 
starting ./lock_server 29818 > lock_server.log 2>&1 &
starting ./extent_server 29812 > extent_server.log 2>&1 &
starting ./yfs_client /home/liu/Desktop/yfsCpp11/lab2/yfs1 29812 29818 > yfs_client1.log 2>&1 &
starting ./yfs_client /home/liu/Desktop/yfsCpp11/lab2/yfs2 29812 29818 > yfs_client2.log 2>&1 &
$ ./test-lab-2-a.pl ./yfs1 
create file-cmrivvpetwwtwdazsapiphcemhnfinxrgdzxgqvi-13540-0
create file-wybthquccdqwjexfmngiseabesctrnoyyxtvpbta-13540-1
create file-ncrjwuoozrtdgggmqlxgmwuzpctamfvhhuasvdkr-13540-2
...
...
Passed all tests!
$ ./test-lab-2-b.pl ./yfs1 ./yfs2
Write and read one file: OK
Write and read a second file: OK
Overwrite an existing file: OK
Append to an existing file: OK
Write into the middle of an existing file: OK
Write beyond the end of an existing file: OK
Check that one cannot open non-existant file: OK
Check directory listing: OK
Read files via second server: OK
Check directory listing on second server: OK
Passed all tests

这一部分与上一次实验完成的内容差不多,有了前面的理解,这一部分很简单。

Part4:Locking

最简单的方法就是为整个文件系统提供单个锁,或是锁定整个目录,但这些都不好。

锁应该与每个内容相关联,使用文件或目录的inum作为锁的名称,利用之前在lock_client的实现,每个yfs_client都有一个lock_client

class yfs_client {
  extent_client *ec;
  lock_client *m_lc;
public:
  ...

而在CREATE等方法中如果直接使用m_lc的话会比较繁杂,可以自己实现一个锁类,采用RAII(资源获取即初始化), 类似于标准库中的lock_guard

class LockGuard {
public:
  LockGuard(lock_client *lc, lock_protocol::lockid_t lid) : m_lc(lc), m_lid(lid)
  {
    m_lc->acquire(m_lid);
  }

  ~LockGuard()
  {
    m_lc->release(m_lid);
  }
private:
  lock_client *m_lc;
  lock_protocol::lockid_t m_lid;
};

测试

Part1

$ ./test-lab-3-a.pl ./yfs1
mkdir ./yfs1/d14527
create x-0
delete x-0
create x-1
checkmtime x-1
checkdirmtime
...
...
Passed all tests!

Part2

$ ./test-lab-3-b ./yfs1 ./yfs2
Create then read: OK
Unlink: OK
Append: OK
Readdir: OK
Many sequential creates: OK
Write 20000 bytes: OK
Concurrent creates: OK
Concurrent creates of the same file: OK
Concurrent create/delete: OK
Concurrent creates, same file, same server: OK
Concurrent writes to different parts of same file: OK
test-lab-3-b: Passed all tests.
$ ./test-lab-3-c ./yfs1 ./yfs2
Create/delete in separate directories: tests completed OK