清华课题 Octopus 源码分析(二)
前言
由于项目工作的需要,我们团队阅读了清华在文件系统方面的一个比较新颖的工作:Octopus。Octopus是一个基于持久内存 NVM 和远程直接内存访问 RDMA 技术的分布式内存文件系统。清华的陆游游老师现已将代码开源,可 点击此处 阅读。
这一工作中的是 ATC-17 (CCF A类),可 点击此处 阅读论文。
我们团队希望通过学习清华的这个优秀的同行工作,来进一步开展自己对于分布式持久内存文件系统的研究。关于论文的分析,虽然有做PPT给同伴们介绍过,但具体的博客分析可能会晚些才放上来。这一系列的内容主要是分析Octopus的源码设计(少许会结合论文内容来讲,希望有兴趣的同学可以自己先读一读),总结Octopus的框架、结构、设计组件及代码创新点等。
系列分析总共包括 个部分。第一部分是 论文摘要,相当于Octopus系统的一个简介;第二部分是 设计框架,在这一部分我们会宏观地介绍Octopus的组成体系及各部分的功能及相互间的联系;第三部分是 代码分析,也是本博客的重中之重。在这一部分我们首先介绍头文件体系(在include文件夹中),了解Octopus的存储结构,表结构,主要数据结构,内存池划分等等。接下来我们介绍方法实现代码(在src文件夹中),我们会通过比对头文件内的函数名称来看每一个方法是采用何种方式实现,好处是什么,取舍考虑是什么。进一步地,我们会通过代码文件间的依赖关系,函数依赖关系去深入探讨Octopus的创新性、局限性并留出进一步讨论的空间。
论文摘要
(内容请见系列上一篇博客)
设计框架
(内容请见系列上一篇博客)
源码分析
include 头文件
Configuration.hpp
这是一个配置头文件,主要涉及文件系统的配置信息。
加了个人注释的代码如下所示:
#ifndef CONFIGURATION_HEADER
#define CONFIGURATION_HEADER
#include <iostream>
/* ptree 性质与功能可参考链接 http://www.boost.org/doc/libs/1_65_1/doc/html/property_tree.html */
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/typeof/typeof.hpp>
#include <unordered_map>
#include <string>
using namespace std;
using namespace boost::property_tree;
class Configuration {
private:
ptree pt;
/* ip(表征节点)与id(表征文件系统持久区域)的映射对关系 */
unordered_map<uint16_t, string> id2ip;
unordered_map<string, uint16_t> ip2id;
/* 服务器数量信息 */
int ServerCount;
public:
Configuration();
~Configuration();
string getIPbyID(uint16_t id);
uint16_t getIDbyIP(string ip);
unordered_map<uint16_t, string> getInstance();
int getServerCount();
};
#endif
common.hpp
这一个头文件主要定义了基本的元数据结构。
加了个人注释的代码如下所示:
#ifndef COMMON_HEADER
#define COMMON_HEADER
#include <stdint.h>
#include <time.h>
/* Data type alias: nrfs->int, nrfsFile->char* */
typedef int nrfs;
typedef char* nrfsFile;
#define MAX_MESSAGE_BLOCK_COUNT 10 /* Max count of block index in a message. */
/* 文件位置三元组:
* 1. 节点id(在哪个服务器上)
* 2. 偏移量(决定起始地址)
* 3. 大小(决定结束地址)
*/
typedef struct
{
uint16_t node_id;
uint64_t offset;
uint64_t size;
} file_pos_tuple;
/* 文件位置信息(批量)
* 1. 文件长度
* 2. 以文件位置三元组为单位组成的数组
*/
struct file_pos_info
{
uint32_t len;
file_pos_tuple tuple[MAX_MESSAGE_BLOCK_COUNT];
};
/* 文件元数据
* 1. 模式(文件/目录)
* 2. 大小
* 3. 时间(访问/创建)
*/
struct nrfsfileattr
{
uint32_t mode; /* 0 - file, 1 - directory */
uint64_t size;
uint32_t time;
};
/** Definitions. **/
#define MAX_PATH_LENGTH 255 /* Max length of path. */
/** Definitions. **/
#define MAX_FILE_EXTENT_COUNT 20 /* Max extent count in meta of a file. */
#define BLOCK_SIZE (1 * 1024 * 1024) /* Current block size in bytes. */
#define MAX_FILE_NAME_LENGTH 50 /* Max file name length. */
#define MAX_DIRECTORY_COUNT 60 /* Max directory count. */
/** Classes and structures. **/
typedef uint64_t NodeHash; /* Node hash. */
/* 文件元数据三元组
* 1. 哈希数
* 2. 检索起始块
* 3. 占用块数量
*/
typedef struct
{
NodeHash hashNode; /* Node hash array of extent. */
uint32_t indexExtentStartBlock; /* Index array of start block in an extent. */
uint32_t countExtentBlock; /* Count array of blocks in an extent. */
} FileMetaTuple;
/* 文件元数据
* 1. 最后修改时间
* 2. extent数量
* 3. extent大小
* 4. 以文件元数据三元组为单位的数组
*/
typedef struct /* File meta structure. */
{
time_t timeLastModified; /* Last modified time. */
uint64_t count; /* Count of extents. (not required and might have consistency problem with size) */
uint64_t size; /* Size of extents. */
FileMetaTuple tuple[MAX_FILE_EXTENT_COUNT];
} FileMeta;
/* 目录元数据二元组:
* 1. 目录名
* 2. 是否为目录(石乐志)
*/
typedef struct {
char names[MAX_FILE_NAME_LENGTH];
bool isDirectories;
} DirectoryMetaTuple;
/* 目录元数据:
* 1. 目录名数量
* 2. 以目录元数据二元组为单位的数组
*/
typedef struct /* Directory meta structure. */
{
uint64_t count; /* Count of names. */
DirectoryMetaTuple tuple[MAX_DIRECTORY_COUNT];
} DirectoryMeta;
typedef DirectoryMeta nrfsfilelist;
static inline void NanosecondSleep(struct timespec *preTime, uint64_t diff) {
struct timespec now;
uint64_t temp;
temp = 0;
while (temp < diff) {
clock_gettime(CLOCK_MONOTONIC, &now);
temp = (now.tv_sec - preTime->tv_sec) * 1000000000 + now.tv_nsec - preTime->tv_nsec;
temp = temp / 1000;
}
}
#endif
bitmap.hpp
这一个头文件给出了位图类的定义。位图将用来记录文件inode的占用空间(元数据或数据所占用的持久内存块)。
加了个人注释的代码如下所示。
/*** Bitmap header. ***/
/** Version 1 + modification for table class. **/
/** Redundance check. **/
#ifndef BITMAP_HEADER
#define BITMAP_HEADER
/** Included files. **/
#include <stdint.h> /* Standard integers. E.g. uint8_t */
#include <stdlib.h> /* Standard library for memory allocation and exit function. */
#include <stdio.h> /* Standard I/O operations. E.g. fprintf() */
#include <string.h> /* Header for memory operations. E.g. memset() */
/* 类成员:
* 1. 指向字节数组的指针;
* 2. 空闲比特位的数量;
* 3. 总比特位数。
* 类方法:
* 1. 获取特定位置比特位状态(空闲/占用);
* 2. 设置特定位置比特位状态(空闲->占用);
* 3. 清除特定位置比特位状态(占用->空闲);
* 4. 寻找空闲比特位位置;
* 5. 计算空闲比特位数量;
* 6. 计算总比特位数量;
* 构造和析构函数
*/
/** Classes. **/
class Bitmap
{
private:
uint8_t *bytes; /* Byte array to hold bitmap. */
uint64_t varCountFree; /* Count variable of free bits. */
uint64_t varCountTotal; /* Count variable of total bits. */
public:
bool get(uint64_t pos, bool *status); /* Get status of a bit. */
bool set(uint64_t pos); /* Set a bit. */
bool clear(uint64_t pos); /* Clear a bit. */
bool findFree(uint64_t *pos); /* Find first free bit. */
uint64_t countFree(); /* Count of free bits. */
uint64_t countTotal(); /* Count of total bits. */
Bitmap(uint64_t count, char *buffer); /* Constructor of bitmap. Read buffer to initialize current status. */
~Bitmap(); /* Destructor of bitmap. Do not free buffer. */
};
/** Redundance check. **/
#endif
debug.hpp
这一头文件主要包含了调试(逻辑测试)函数。
代码如下所示:
/*** Debug header. ***/
/** Version 1 + Functional Model Modification **/
/** Redundance check. **/
#ifndef DEBUG_HEADER
#define DEBUG_HEADER
/** Included files. **/
#include <stdio.h> /* Standard I/O operations. E.g. vprintf() */
#include <stdarg.h> /* Standard argument operations. E.g. va_list */
#include <sys/time.h> /* Time functions. E.g. gettimeofday() */
/** Defninitions. **/
#define MAX_FORMAT_LEN 255
#define DEBUG false
#define TITLE false
#define TIMER false
#define CUR false
/** Classes. **/
class Debug
{
private:
static long startTime; /* Last start time in milliseconds. */
public:
static void debugTitle(const char *str); /* Print debug title string. */
static void debugItem(const char *format, ...); /* Print debug item string. */
static void debugCur(const char *format, ...); /* Print debug item string. */
static void notifyInfo(const char *format, ...); /* Print normal notification. */
static void notifyError(const char *format, ...); /* Print error information. */
static void startTimer(const char*); /* Start timer and display information. */
static void endTimer(); /* End timer and display information. */
};
/** Redundance check. **/
#endif
lock.h
这一头文件的主要作用是提供文件读写的锁服务,以避免数据访问冲突。
源码如下所示:
#ifndef LOCK_HEADER
#define LOCK_HEADER
#include <stdint.h>
#include <unistd.h>
class LockService {
private:
uint16_t WriteID;
uint16_t ReadID;
uint64_t MetaDataBaseAddress;
public:
LockService(uint64_t MetaDataBaseAddress);
~LockService();
uint64_t WriteLock(uint16_t NodeID, uint64_t Address);
bool WriteUnlock(uint64_t key, uint16_t NodeID, uint64_t Address);
uint64_t ReadLock(uint16_t NodeID, uint64_t Address);
bool ReadUnlock(uint64_t key, uint16_t NodeID, uint64_t Address);
};
#endif
table.hpp
这一头文件定义了模板表结构。
带个人注释的代码如下所示:
/*** Table header in file system. ***/
/** Version 1. **/
/* FIXME: Ignore malloc() failure. */
/** Redundance check. **/
#ifndef TABLE_HEADER
#define TABLE_HEADER
/** Included files. **/
#include <stdint.h> /* Standard integers. E.g. uint16_t */
#include <stdlib.h> /* Standard library. E.g. exit() */
#include <string.h> /* String operations. E.g. memcmp() */
#include <stdio.h> /* Standard I/O. */
#include <mutex> /* Mutex operations. */
#include "bitmap.hpp" /* Bitmap class. */
#include "debug.hpp" /* Debug class. */
/** Design. **/
/*
+------------+---------------------------+
| Bitmap | Items |
+------------+---------------------------+
- Memory overview of buffer -
Use bitmap to check if a position is occupied.
+---+---+---+---+---+---+------+---+---+---+---+
| 0 | 0 | 0 | 1 | 0 | 1 | .... | 0 | 0 | 0 | 0 |
+---+---+---+---+---+---+------+---+---+---+---+
- Structure of bitmap -
Use free bit chain to allocate a new item. (The free bit chain is generated from bitmap.)
+--------------+-------------------------+ +--------------+----------+
| Position | Next free bit pointer -|--> .... --->| Position | NULL |
+--------------+-------------------------+ +--------------+----------+
- Free bit chain -
index
+--------------+
0 | Item 0 |
+--------------+
1 | Item 1 |
+--------------+
- item array - 2 | Item 2 |
+--------------+
| .... |
+--------------+
n | Item n |
+--------------+
*/
/* 空闲比特单元:
* 1. 位置(相对偏移);
* 2. 下一比特位指针。
*/
/** Structures. **/
typedef struct { /* Free bit structure. */
uint64_t position; /* Position of bit. */
void *nextFreeBit; /* Next free bit. */
} FreeBit;
/* 模板表结构:
* 数据成员:
* 1. 指向位图的指针;
* 2. 位图访问互斥锁;
* 3. 文件(元)数据指针;
* 4. 指向空闲比特链的头指针;
* 5. 指向空闲比特链的尾指针。
* 6. buffer使用量。
* 成员方法:
* 1. 创建数据域(分配空间);
* 2. 获取数据域位置(pos/index)及地址;
* 3. 向指定位置(index/pos)存入数据;
* 4. 删除数据域(并重设空闲比特链)
*/
/** Classes. **/
template<typename T> class Table
{
private:
Bitmap *bitmapItems; /* Bitmap for items. */
std::mutex mutexBitmapItems; /* Mutex for bitmap for items. */
T *items; /* Items of table. */
FreeBit *headFreeBit; /* Head free bit in the chain. */
FreeBit *tailFreeBit; /* Tail free bit in the chain. */
public:
uint64_t sizeBufferUsed; /* Size of used bytes in buffer. */
bool create(uint64_t *index, T *item); /* Create an item. */
bool create(uint64_t *index); /* Create an item. No item will be assigned. */
bool get(uint64_t index, T *item); /* Get an item. */
bool get(uint64_t index, T *item, uint64_t *address);
bool put(uint64_t index, T *item); /* Put an item. */
bool put(uint64_t index, T *item, uint64_t *address);
bool remove(uint64_t index); /* Remove an item. */
uint64_t countSavedItems(); /* Saved items count. */
uint64_t countTotalItems(); /* Total items count. */
Table(char *buffer, uint64_t count); /* Constructor of table. */
~Table(); /* Destructor of table. */
};
/* Create an item in table. (no item will be assigned)
@param index Index of created item in table.
@return If creation failed return false. Otherwise return true. */
template<typename T> bool Table<T>::create(uint64_t *index)
{
if (index == NULL) {
return false; /* Fail due to null index. */
} else {
bool result;
mutexBitmapItems.lock(); /* Lock table bitmap. */
{
if (headFreeBit == NULL) { /* If there is no free bit in bitmap. */
return false; /* Fail due to out of free bit. */
} else {
*index = headFreeBit->position; /* Get index from free bit. */
FreeBit *currentFreeBit = headFreeBit; /* Get current free bit. */
headFreeBit = (FreeBit *)(headFreeBit->nextFreeBit); /* Move current free bit out of free bit chain. */
free(currentFreeBit); /* Release current free bit as used. */
if (bitmapItems->set(*index) == false) { /* Occupy the position first. Need not to roll back. */
result = false; /* Fail due to bitmap set error. No recovery here. */
} else {
result = true; /* Succeed. */
}
}
}
mutexBitmapItems.unlock(); /* Unlock table bitmap. */
//printf("Table::create (block): *index = %lu, &(items[*index]) = %lx\n", *index, &(items[*index]));
return result; /* Return specific result. */
}
}
/* Create an item in table.
@param index Index of created item in table.
@param item Item to put in table.
@return If creation failed return false. Otherwise return true. */
template<typename T> bool Table<T>::create(uint64_t *index, T *item)
{
if ((index == NULL) || (item == NULL)) {
return false; /* Fail due to null index or null item. */
} else {
bool result;
mutexBitmapItems.lock();