yfs-源码剖析(3)--基本文件服务2

文章目录
  1. 1. mkdir&unlink
  2. 2. Locking
  3. 3. 参考资料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
int
yfs_client::mkdir(inum parent, const char *name, mode_t mode, inum &inum)
{
LockGuard lg(m_lc, parent);
std::string data;
std::string dir_name;
if (ec->get(parent, data) != extent_protocol::OK)
{
return IOERR;
}

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

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

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

return OK;
}

int yfs_client::unlink(inum parent, const char* name)
{
LockGuard lg(m_lc, parent);
std::string data;
std::string file_name = "/" + std::string(name) + "/";
size_t pos, end, len;
inum inum;

if(ec->get(parent, data) != extent_protocol::OK)
{
return IOERR;
}

// 没有这个文件
if((pos = data.find(file_name)) == std::string::npos)
{
return NOENT;
}

end = data.find_first_of("/", pos+file_name.size());
if(end == std::string::npos)
{
return NOENT;
}
len = end - file_name.size() - pos;
inum = n2i(data.substr(pos+file_name.size(), len));
if(!isfile(inum))
{
return IOERR;
}

// 从目录中移除文件
data.erase(pos, end-pos+1);
if(ec->put(parent, data) != extent_protocol::OK)
{
return IOERR;
}

// 删除文件
if(ec->remove(inum) != extent_protocol::OK)
{
return IOERR;
}

return OK;
}

在 fuse 中对应如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void
fuseserver_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode)
{
struct fuse_entry_param e;
// In yfs, timeouts are always set to 0.0, and generations are always set to 0
e.attr_timeout = 0.0;
e.entry_timeout = 0.0;
e.generation = 0;
// Suppress compiler warning of unused e.
(void) e;

#if 1
yfs_client::inum inum = 0;
yfs_client::status ret;
ret = yfs->mkdir(parent, name, mode, inum);
if(ret == yfs_client::OK)
{
e.ino = inum;
getattr(inum, e.attr);
}
else if(ret == yfs_client::EXIST)
{
fuse_reply_err(req, EEXIST);
return;
}

fuse_reply_entry(req, &e);
#else
fuse_reply_err(req, ENOSYS);
#endif
}

// Remove the file named @name from directory @parent.
// Free the file's extent.
// If the file doesn't exist, indicate error ENOENT.
//
// Do *not* allow unlinking of a directory.
//
void
fuseserver_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{

// You fill this in for Lab 3
// Success: fuse_reply_err(req, 0);
// Not found: fuse_reply_err(req, ENOENT);
yfs_client::status ret = yfs->unlink(parent, name);
if(ret == yfs_client::OK)
{
fuse_reply_err(req, 0);
return;
}
fuse_reply_err(req, ENOENT);
}

Locking

  • What to lock?

    At one extreme you could have a single lock for the whole file system, so that operations never proceed in parallel. At the other extreme you could lock each entry in a directory, or each field in the attributes structure. Neither of these is a good idea! A single global lock prevents concurrency that would have been okay, for example CREATEs in different directories. Fine-grained locks have high overhead and make deadlock likely, since you often need to hold more than one fine-grained lock.

    You should associate a lock with each inumber. Use the file or directory’s inum as the name of the lock (i.e. pass the inum to acquire and release). The convention should be that any yfs_client operation should acquire the lock on the file or directory it uses, perform the operation, finish updating the extent server (if the operation has side-effects), and then release the lock on the inum. Be careful to release locks even for error returns from yfs_client operations.

    You’ll use your lock server from Lab 1. yfs_client should create and use a lock_client in the same way that it creates and uses its extent_client.

  • Things to watch out for:

    This is the first lab that creates files using two different YFS-mounted directories. If you were not careful in earlier labs, you may find that the components that assign inum for newly-created files and directories choose the same identifiers. One possible way to fix this may be to seed the random number generator differently depending on the process’s pid. The provided code has already done such seeding for you in the main function of fuse.cc.

    This is also the first lab that writes null (‘\0’) characters to files. The std::string(char*) constructor treats ‘\0’ as the end of the string, so if you use that constructor to hold file content or the written data, you will have trouble with this lab. Use the std::string(buf, size) constructor instead..

需要对之前的 yfs_client 进行修改:

1
2
3
4
5
class yfs_client {
extent_client *ec;
lock_client *m_lc;
public:
...

并且添加 lockGuard 类,实现 RAII,并且在 yfs_client 中需要加锁的地方添加 lockGuard。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
};

参考资料