leveldb源码学习3-env

简述

本系列第三篇文章,分析一下LevelDB中的Env,这个文件主要是为了考虑了移植和灵活性,所以把系统相关的一些处理(文件/进程/时间之类)抽象成Env,用户可以自己实现响应的接口作为options传入。默认使用自带的。

这么封装的好处是显而易见的:跨平台会更加简单。客户端只需要调用接口抽象出来的一致性方法,不同平台下的代码更加一致,值得借鉴。

还是选择代码注释的方式(我只是个翻译工。)

include/leveldb/env.h

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
// Env是leveldb中实现用来访问操作系统功能(如文件系统等)的接口
// 调用者可能希望在打开数据库的时候提供一个自定义的Env对象来获得精细增益控制。比如,要限制文件系统操作的速率。

#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_
#define STORAGE_LEVELDB_INCLUDE_ENV_H_

#include <stdarg.h>
#include <stdint.h>

#include <string>
#include <vector>

#include "leveldb/export.h"
#include "leveldb/status.h"

#if defined(_WIN32)
// Env类提供了一个DeleteFile方法,但同时在windows应用里被广泛应用的<windows.h>里也有一个DeleteFile宏。
// 如果不做任何干预的话,这个不幸的巧合会导致编译器看到的leveldb::Env::DeleteFile方法的名称取决于<windows.h>是否包含在leveldb头文件之前还是之后。
// 为了避免这个问题,在这里undefine DeleteFile方法并且在本文件最后重新定义它,这样可以保证对外的DeleteFile总是leveldb::Env::DeleteFile
#if defined(DeleteFile)
#undef DeleteFile
#define LEVELDB_DELETEFILE_UNDEFINED
#endif // defined(DeleteFile)
#endif // defined(_WIN32)

namespace leveldb {

class FileLock;
class Logger;
class RandomAccessFile;
class SequentialFile;
class Slice;
class WritableFile;

class LEVELDB_EXPORT Env {
public:
Env() = default;

Env(const Env&) = delete;
Env& operator=(const Env&) = delete;

virtual ~Env();

// 返回适合当前操作系统的一个默认Env。
// 返回的结果属于LevelDB并且不能被deleted。
static Env* Default();

// 创建一个对象,该对象按顺序读取具有指定名称的文件。
// 成功时,result存储对应指针并返回OK。
// 失败时,result为空并返回non-OK。
// 如果文件不存在,返回NotFound。
// 返回文件在同一时间只能被一个线程获取。
virtual Status NewSequentialFile(const std::string& fname,
SequentialFile** result) = 0;

// 创建一个对象,该对象支持随机读取具有指定名称的文件。
// 其余同上。
virtual Status NewRandomAccessFile(const std::string& fname,
RandomAccessFile** result) = 0;

// 创建一个对象,该对象对具有指定名称的文件进行写入。
// 若文件已存在,则会删除并创建一个新文件。
// 其余同上。
virtual Status NewWritableFile(const std::string& fname,
WritableFile** result) = 0;


// 创建一个对象,该对象朝已存在的文件追加,或者创建一个新文件开始写入。
// 可能会返回IsNotSupportedError error,这时表示Env不支持对已有文件进行追加。
// 其余同上
virtual Status NewAppendableFile(const std::string& fname,
WritableFile** result);

// 文件存在则返回true
virtual bool FileExists(const std::string& fname) = 0;

// result会保存指定目录下的所有文件名。result中的本来内容会被覆盖。
virtual Status GetChildren(const std::string& dir,
std::vector<std::string>* result) = 0;

// 删除文件
virtual Status DeleteFile(const std::string& fname) = 0;

// 创建目录
virtual Status CreateDir(const std::string& dirname) = 0;

// 删除目录
virtual Status DeleteDir(const std::string& dirname) = 0;

// 获取文件大小
virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0;

// 重命名文件
virtual Status RenameFile(const std::string& src,
const std::string& target) = 0;

// 锁定指定的文件,在多线程时获取锁。
// 失败时lock为空。
// 成功时,保存一个已获得的锁并返回ok。
// 调用方要使用UnlockFile来解锁。
// 若其他人已经持有锁,则立刻返回失败,不会等待。
// 文件不存在时会创建。
virtual Status LockFile(const std::string& fname, FileLock** lock) = 0;

// 释放锁
// 锁必须是已经被成功LockFile的,且没有重复unlock。
virtual Status UnlockFile(FileLock* lock) = 0;

// 在后台线程运行function。
// 添加到同一个Env中的多个方法会在多个线程中并发跑。
// 调用方不应认为后台任务是顺序执行的。
virtual void Schedule(void (*function)(void* arg), void* arg) = 0;

// 开启一个新线程,调用function。方法return时,线程会被销毁。
virtual void StartThread(void (*function)(void* arg), void* arg) = 0;

// *path被设值成一个测试用的临时目录。它不一定是刚刚才被创建的。目录在统一线程的多次运行中不一定不同,但后续调用都会返回同一目录。
// TODO:这个方法有些不懂,回头要测一测。
virtual Status GetTestDirectory(std::string* path) = 0;

// 创建并返回一个用来存储消息的日志文件。
virtual Status NewLogger(const std::string& fname, Logger** result) = 0;

// 返回从一个特定时间节点之后到目前为止经过的毫秒数。
// 只有在计算增量数据时有用。
virtual uint64_t NowMicros() = 0;

// sleep指定的毫秒数。
virtual void SleepForMicroseconds(int micros) = 0;
};

// 一种按顺序读取的文件的抽象
class LEVELDB_EXPORT SequentialFile {
public:
SequentialFile() = default;

SequentialFile(const SequentialFile&) = delete;
SequentialFile& operator=(const SequentialFile&) = delete;

virtual ~SequentialFile();

// 读取文件中的n个字节。scratch[0..n-1]可能会被写入。
// *result指向被读取的数据(即使被成功读取的数据字节数小于n)。
// 可以设置*result指向scratch[0..n-1]中的数据,所以当*result被使用时一定要保证scratch数组可用。
// 如果出错,则返回非ok。
// 要求:外部同步
virtual Status Read(size_t n, Slice* result, char* scratch) = 0;

// 对文件跳过n个字节。
// 该方法保证不比读取相同字节数慢,有时会更快。
// 如果到达文件末尾,skip会停止并返回ok。
// 要求:外部同步。
virtual Status Skip(uint64_t n) = 0;
};

// 一种随机读取文件的抽象。
class LEVELDB_EXPORT RandomAccessFile {
public:
RandomAccessFile() = default;

RandomAccessFile(const RandomAccessFile&) = delete;
RandomAccessFile& operator=(const RandomAccessFile&) = delete;

virtual ~RandomAccessFile();

// 对指定文件从offset偏移处开始读取n个字节。
// *result和*scratch说明同SequentialFile.read。
// 对于多线程并发同步是安全的。
virtual Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const = 0;
};


// 对序列写的一种文件抽象。
// 实现必须要提供缓存,因为调用者们可能会在同一时间对文件添加多个小段落。
class LEVELDB_EXPORT WritableFile {
public:
WritableFile() = default;

WritableFile(const WritableFile&) = delete;
WritableFile& operator=(const WritableFile&) = delete;

virtual ~WritableFile();

virtual Status Append(const Slice& data) = 0;
virtual Status Close() = 0;
virtual Status Flush() = 0;
virtual Status Sync() = 0;
};

// 写日志消息的接口
class LEVELDB_EXPORT Logger {
public:
Logger() = default;

Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;

virtual ~Logger();

// 用指定格式向日志文件写入一个entry
virtual void Logv(const char* format, va_list ap) = 0;
};

// 文件锁,标识一个锁定的文件。
class LEVELDB_EXPORT FileLock {
public:
FileLock() = default;

FileLock(const FileLock&) = delete;
FileLock& operator=(const FileLock&) = delete;

virtual ~FileLock();
};

// 如果*info_log不是null,就记录指定的数据。
void Log(Logger* info_log, const char* format, ...)
#if defined(__GNUC__) || defined(__clang__)
__attribute__((__format__(__printf__, 2, 3)))
#endif
;

// 一个通用例程:写数据到指定文件。
LEVELDB_EXPORT Status WriteStringToFile(Env* env, const Slice& data,
const std::string& fname);

// 一个通用例程:从指定文件中读取数据到*data
LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname,
std::string* data);


// 将所有调用转发给另一个Env的Env实现。对于只想覆盖另一个Env的部分功能的客户端可能很有用。
// TODO 传说中的代理模式?
class LEVELDB_EXPORT EnvWrapper : public Env {
public:
// 初始化一个EnvWrapper用来将所有调用委托给*t。
explicit EnvWrapper(Env* t) : target_(t) {}
virtual ~EnvWrapper();

// 返回此Env将所有调用转发给的目标。
Env* target() const { return target_; }

// 下面的文本是将所有方法转发到target()的样板文件。
Status NewSequentialFile(const std::string& f, SequentialFile** r) override {
return target_->NewSequentialFile(f, r);
}
Status NewRandomAccessFile(const std::string& f,
RandomAccessFile** r) override {
return target_->NewRandomAccessFile(f, r);
}
Status NewWritableFile(const std::string& f, WritableFile** r) override {
return target_->NewWritableFile(f, r);
}
Status NewAppendableFile(const std::string& f, WritableFile** r) override {
return target_->NewAppendableFile(f, r);
}
bool FileExists(const std::string& f) override {
return target_->FileExists(f);
}
Status GetChildren(const std::string& dir,
std::vector<std::string>* r) override {
return target_->GetChildren(dir, r);
}
Status DeleteFile(const std::string& f) override {
return target_->DeleteFile(f);
}
Status CreateDir(const std::string& d) override {
return target_->CreateDir(d);
}
Status DeleteDir(const std::string& d) override {
return target_->DeleteDir(d);
}
Status GetFileSize(const std::string& f, uint64_t* s) override {
return target_->GetFileSize(f, s);
}
Status RenameFile(const std::string& s, const std::string& t) override {
return target_->RenameFile(s, t);
}
Status LockFile(const std::string& f, FileLock** l) override {
return target_->LockFile(f, l);
}
Status UnlockFile(FileLock* l) override { return target_->UnlockFile(l); }
void Schedule(void (*f)(void*), void* a) override {
return target_->Schedule(f, a);
}
void StartThread(void (*f)(void*), void* a) override {
return target_->StartThread(f, a);
}
Status GetTestDirectory(std::string* path) override {
return target_->GetTestDirectory(path);
}
Status NewLogger(const std::string& fname, Logger** result) override {
return target_->NewLogger(fname, result);
}
uint64_t NowMicros() override { return target_->NowMicros(); }
void SleepForMicroseconds(int micros) override {
target_->SleepForMicroseconds(micros);
}

private:
Env* target_;
};

} // namespace leveldb

// 重新定义DeleteFile如果有必要的话
#if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
#if defined(UNICODE)
#define DeleteFile DeleteFileW
#else
#define DeleteFile DeleteFileA
#endif // defined(UNICODE)
#endif // defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)

#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_