从leveldb中学编码技巧(6)

leveldb的运行涉及到很多文件,包括manifest文件,WAL log文件,sst文件,日志文件等,为了方便进行文件io,leveldb抽象了几个接口

// A file abstraction for reading sequentially through a file
class SequentialFile {
 public:
  SequentialFile() { }
  virtual ~SequentialFile();

  virtual Status Read(size_t n, Slice* result, char* scratch) = 0;

  // Skip "n" bytes from the file. This is guaranteed to be no
  // slower that reading the same data, but may be faster.
  virtual Status Skip(uint64_t n) = 0;

 private:
  // No copying allowed
  SequentialFile(const SequentialFile&);
  void operator=(const SequentialFile&);
};

// A file abstraction for randomly reading the contents of a file.
class RandomAccessFile {
 public:
  RandomAccessFile() { }
  virtual ~RandomAccessFile();

  // Safe for concurrent use by multiple threads.
  virtual Status Read(uint64_t offset, size_t n, Slice* result,
                      char* scratch) const = 0;

 private:
  // No copying allowed
  RandomAccessFile(const RandomAccessFile&);
  void operator=(const RandomAccessFile&);
};

// A file abstraction for sequential writing.  The implementation
// must provide buffering since callers may append small fragments
// at a time to the file.
class WritableFile {
 public:
  WritableFile() { }
  virtual ~WritableFile();

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

 private:
  // No copying allowed
  WritableFile(const WritableFile&);
  void operator=(const WritableFile&);
};

在具体实现这些接口的时候,除了RandomAccessFile类型外,leveldb都是使用标准IO库来实现,也就是<stdio>提供的一套函数,标准IO库有自己的缓存,leveldb没有对磁盘文件做额外的缓存。
对RandomAccessFile 这个类型,leveldb使用了mmap来进行内存映射,以此提高文件IOD的性能。同时控制了能够同时进行内存映射的文件数目。(为了防止内存消耗过度?)

对于日志打印,leveldb的实现比较简单。首先定义了一个接口:

// An interface for writing log messages.
class Logger {
 public:
  Logger() { }
  virtual ~Logger();

  // Write an entry to the log file with the specified format.
  virtual void Logv(const char* format, va_list ap) = 0;

 private:
  // No copying allowed
  Logger(const Logger&);
  void operator=(const Logger&);
};

然后根据具体环境来实现这个接口,在POSIX下,实现的class是:

class PosixLogger : public Logger {
 private:
  FILE* file_;
  uint64_t (*gettid_)();  // Return the thread id for the current thread
 public:
  PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { }
  virtual ~PosixLogger() {
    fclose(file_);
  }
  virtual void Logv(const char* format, va_list ap);
}

打印日志的过程使用了变长参数,这是日志的基本需求。值得注意的是,对于写日志整个过程没有使用额外的并发控制,主要是因为标准IO库<stdio>提供的读写函数都是线程安全的,多个线程可以对同一个FILE同时调用fwrite()。 每次写完一条日志后,立即调用fflush()将缓存刷到操作系统,但是并没有调用fsync来落盘。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 10,902评论 0 9
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,871评论 1 32
  • 今天看到一位朋友写的mysql笔记总结,觉得写的很详细很用心,这里转载一下,供大家参考下,也希望大家能关注他原文地...
    信仰与初衷阅读 10,175评论 0 30
  • 在父母训练课程,来到三阶段的时候,第一期的父母们开始每天的觉察,觉察什么呢?怎么觉察呢?觉察之后要做什么呢? 这本...
    黄丽浙江阅读 4,208评论 1 7
  • 没什么不好的 我这漂泊的浪儿 出身又不好 注定和你不是一个世界的人 你离开了很久 你离开有多久 我对你的思念便有多...
    会疼这思念阅读 1,342评论 2 1

友情链接更多精彩内容