void DiskManager::write_page(int fd, page_id_t page_no, const char *offset, int num_bytes) {
// Todo:
// 1.lseek()定位到文件头,通过(fd,page_no)可以定位指定页面及其在磁盘文件中的偏移量
// 2.调用write()函数
// 注意处理异常
//off_t 文件偏移量 long 类型
off_t offset = static_cast<off_t>(page_no) * PAGE_SIZE;
if (lseek(fd, offset, SEEK_SET) == -1) {
throw UnixError();
}
int num = write(fd, offset, num_bytes);
if (num != num_bytes) {
throw UnixError();
}
off_t类型用于指示文件的偏移量,常就是long类型,其默认为一个32位的整数,在gcc编译中会被编译为long int类型,在64位的Linux系统中则会被编译为long long int,这是一个64位的整数,其定义在unistd.h头文件中可以查看。
fseek(FILE *stream, long offset, int whence);
1.offset表示文件指针的偏移量SEEK_SET:基准位置为文件开头,即offset表示距离文件开头的偏移量。
SEEK_CUR:基准位置为文件当前位置,即offset表示距离文件当前位置的偏移量。
SEEK_END:基准位置为文件末尾,即offset表示距离文件末尾的偏移量。
2.whence表示偏移量的基准位置当whence为SEEK_SET时,offset表示距离文件开头的偏移量;
当whence为SEEK_CUR时,offset表示距离文件当前位置的偏移量;
当whence为SEEK_END时,offset表示距离文件末尾的偏移量。
void DiskManager::read_page(int fd, page_id_t page_no, char *offset, int num_bytes) {
// Todo:
// 1.lseek()定位到文件头,通过(fd,page_no)可以定位指定页面及其在磁盘文件中的偏移量
// 2.调用read()函数
// 注意处理异常
off_t offset_in_file = static_cast<off_t>(page_no) * PAGE_SIZE;
if (lseek(fd, offset_in_file, SEEK_SET) == -1) {
throw UnixError();
}
if (read(fd, offset, num_bytes) == -1) {
throw UnixError();
}
page_id_t DiskManager::AllocatePage(int fd) {
// Todo:
// 简单的自增分配策略,指定文件的页面编号加1
fd2pageno_[fd]++;
return fd2pageno_[fd] - 1;
}
bool DiskManager::is_file(const std::string &path) {
// Todo:
// 用struct stat获取文件信息
struct stat st;
return stat(path.c_str(), &st) == 0;
}
c_str是string类的一个函数,可以把string类型变量转换成char*变量
open()要求的是一个char*字符串
void DiskManager::destroy_file(const std::string &path) {
// Todo:
// 调用unlink()函数
// 注意不能删除未关闭的文件,不存在的文件也不能删
if (!is_file(path)) {
throw FileNotFoundError(path);
}
if (path2fd_.count(path)) {
throw FileNotClosedError(path);
}
unlink(path.c_str())
首先判断文件是否被创建,如果路径不存在抛出异常再判断文件是否已经打开,不能删除未关闭的文件,然后用unlink()函数关闭文件
int DiskManager::open_file(const std::string &path) {
// Todo:
// 调用open()函数,使用O_RDWR模式
// 注意不能重复打开相同文件,并且需要更新文件打开列表
if (path2fd_.count(path))
return -1;
int fd = open(path.c_str(), O_RDWR);
path2fd_.emplace(path,fd);
fd2path_.emplace(fd,path);
return fd;
}
首先判断文件是否打开,然后打开文件,更新打开文件的列表
c.emplace(p,t)在迭代器p所指向的元素之前创建一个值为t的元素,返回指定新添加元素的迭代器
O_RDWR:以读写方式打开文件
void DiskManager::close_file(int fd) {
// Todo:
// 调用close()函数
// 注意不能关闭未打开的文件,并且需要更新文件打开列表
if (!fd2path_.count(fd)) {
throw FileNotOpenError(fd);
}
close(fd)
auto it = fd2path_.find(fd);
path2fd_.erase(it->first);
fd2path_.erase(fd);
}
bool LRUReplacer::Victim(frame_id_t *frame_id) {
// C++17 std::scoped_lock
// 它能够避免死锁发生,其构造函数能够自动进行上锁操作,析构函数会对互斥量进行解锁操作,保证线程安全。
std::scoped_lock lock{latch_};
// Todo:
// 利用lru_replacer中的LRUlist_,LRUHash_实现LRU策略
// 选择合适的frame指定为淘汰页面,赋值给*frame_id
*frame_id = LRUlist_.back();
LRUlist_.pop_back();
LRUhash_.erase(*frame_id);
return true;
}
bool LRUReplacer::Victim(frame_id_t *frame_id) {
// C++17 std::scoped_lock
// 它能够避免死锁发生,其构造函数能够自动进行上锁操作,析构函数会对互斥量进行解锁操作,保证线程安全。
std::scoped_lock lock{latch_};
// Todo:
// 利用lru_replacer中的LRUlist_,LRUHash_实现LRU策略
// 选择合适的frame指定为淘汰页面,赋值给*frame_id
*frame_id = LRUlist_.back();
LRUlist_.pop_back();
LRUhash_.erase(*frame_id);
return true;
}
/**
* @brief 固定一个frame, 表明它不应该成为victim(即在replacer中移除该frame_id)
* @param frame_id the id of the frame to pin
*/
void LRUReplacer::Pin(frame_id_t frame_id) {
std::scoped_lock lock{latch_};
// Todo:
// 固定指定id的frame
// 在数据结构中移除该frame
auto it = LRUhash_.find(frame_id);
LRUlist_.erase(it);
LRUhash_.erase(it);
}
}
/**
* 取消固定一个frame, 表明它可以成为victim(即将该frame_id添加到replacer)
* @param frame_id the id of the frame to unpin
*/
void LRUReplacer::Unpin(frame_id_t frame_id) {
// Todo:
// 支持并发锁
// 选择一个frame取消固定
std::scoped_lock lock{latch_};
LRUlist_.push_front(frame_id);
LRUhash_[frame_id] = LRUlist_.begin();
}
/** @return replacer中能够victim的数量 */
size_t LRUReplacer::Size() {
// Todo:
// 改写return size
std::scoped_lock lock{latch_};
return LRUlist_.size();
}