SQLite克隆|第四步:磁盘持久化


实现了在内存中储存硬编码的数据表之后,我们需要更近一步,将内存中的数据保存在文件中,即实现数据持久化。
为了使这一点变得容易,我们将创建一个称为页面缓存的抽象概念。我们向页面缓存询问页码x,它返回了一块内存。它首先在其缓存中查找。在发生高速缓存未命中时,它将数据从磁盘复制到内存中(通过读取数据库文件)。

页面缓存(Pager)

页面缓存器结构

首先我们来明确一下页面缓存器的结构:

typedef struct {
   
	int file_descriptor;//文件描述
	uint32_t file_length;//文件长度
	void* pages[TABLE_MAX_PAGES];//所在页
} Pager;

数据表结构

其次,由于确定页面缓存器结构,我们的数据表结构也要发生改变,使其能够包含页面缓存器结构。

 typedef struct {
   
-  void* pages[TABLE_MAX_PAGES];
+  Pager* pager;
   uint32_t num_rows;
 } Table;

new_table()

原本我们的数据表结构中含有pages字段,然而pages字段的存在只是单纯的为了给数据表数据按页分配内存,现在我们引入页面缓存器,它的作用是将文件数据复制到内存中。
由于数据表结构体的变化,我们创建新数据表的函数new_table()也会发生相应的变化:

-Table* new_table() {
   
+Table* db_open(const char* filename) {
   
+  Pager* pager = pager_open(filename);
+  uint32_t num_rows = pager->file_length / ROW_SIZE;
+  Table* table = malloc(sizeof(Table));
-  table->num_rows = 0;
+  table->pager = pager;
+  table->num_rows = num_rows;

   return table;
 }

get_page()

我们定义该函数需要实现如下几个功能:

  • 打开数据库文件
  • 初始化页面缓存器数据结构
  • 初始化表结构

按照顺序先来讲一下打开数据库文件的open()函数。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int oflag, ... /* mode_t mode */);

open函数用来打开或创建一个文件,若成功返回文件描述符,打开失败返回-1。pathname是要打开或创建文件的名字。
oflag参数是下列一个或多个常量执行按位或运算的结果

  • O_RDONLY  只读打开
  • O_WRONLY  只写打开
  • O_RDWR 读写打开

上面三个常量必须指定一个并且只能指定一个,下面一些常量则是可选的,配合使用时使用|链接:

  • O_APPEND  将写入追加到文件的尾端
  • O_CREAT 若文件不存在,则创建它。使用该选项时,需要第三个参数mode,用来指定新文件的访问权限位
  • O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则会出错
  • O_TRUNC 如果此文件存在,而且为只写或读写模式成功打开,则将其长度截短为0
  • O_NOCTTY 如果pathname指的是终端设备,则不将该设备分配作为此进程的控制终端
  • O_NONBLOCK 如果pathname指的是一个FIFO文件、块设备文件或字符设备文件,则此选项将文件的本次打开操作和后续的I/O操作设置为非阻塞模式

第二个我们需要知道的函数是lseek():

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fildes, off_t offset, int whence);

每一个已打开的文件都有一个读写位置, 当打开文件时通常其读写位置是指向文件开头, 若是以附加的方式打开文件(如O_APPEND), 则读写位置会指向文件尾. 当read()或write()时, 读写位置会随之增加,lseek()便是用来控制该文件的读写位置. 参数fildes 为已打开的文件描述词, 参数offset 为根据参数whence来移动读写位置的位移数.

参数 whence 为下列其中一种:

    SEEK_SET 参数offset 即为新的读写位置.
    SEEK_CUR 以目前的读写位置往后增加offset 个位移量.
    SEEK_END 将读写位置指向文件尾后再增加offset 个位移量. 当whence 值为SEEK_CUR 或
    SEEK_END 时, 参数offet 允许负值的出现. 

下列是教特别的使用方式:

1) 欲将读写位置移到文件开头时:lseek(int fildes, 0, SEEK_SET);
2) 欲将读写位置移到文件尾时:lseek(int fildes, 0, SEEK_END);
3) 想要取得目前文件位置时:lseek(int fildes, 0, SEEK_CUR);

返回值:当调用成功时则返回目前的读写位置, 也就是距离文件开头多少个字节. 若有错误则返回-1, errno 会存放错误代码.

+Pager* pager_open(const char* filename) {
   
+  int fd = open(filename,
+                O_RDWR |      // Read/Write mode
+                    O_CREAT,  // Create file if it does not exist
+                S_IWUSR |     // User write permission
+                    S_IRUSR   // User read permission
+                );
+
+  if (fd == -
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值