裸盘存储引擎的实现(一)

导读:如何打开、读写块设备。如何知道块设备大小以及设备属性?

什么是裸盘存储引擎

单机存储引擎负责高效的组织数据、索引数据、保存数据,为上层应用提供易用的接口。有一类存储引擎为了得到更高的性能,会跨过文件系统这一层调用,直接操作裸盘。那么如何实现这类存储引擎呢?本文希望以 Ceph BlueStore 为例子,介绍一下其中的实现方法。

怎么实现一个裸盘存储引擎

读写块设备

裸盘对于操作系统来说,就是一个类型为block的文件,也称之为块设备。在 BlueStore 的实现中,对于块设备作了一个抽象,声明了一个基类 BlockDevice,并实现了两个子类 KernelDevice 和 NVMEDevice,来分别对应普通的块设备以及 NVMe 块设备。

在 KernrlDevice::open 方法中,包含了打开块设备以供读写的实现。

1
open(path.c_str(), O_RDWR |  O_DIRECT);

通过阅读代码可以知道,BlueStore 使用 DirectIO 加上 libaio 的方式来进行读写操作。使用 O_DIRECT 是为了跨过 page cache 的影响,所有的存储与缓存逻辑都由我们自己实现。使用 aio 是因为用了 O_DIRECT 直接操作块设备,操作肯定会被这些IO操作阻塞。通过 libaio 来避免数据IO阻塞前台操作。

1
2
3
4
5
6
7
8
9
10
11
O_DIRECT (since Linux 2.4.10)
Try to minimize cache effects of the I/O to and from this
file. In general this will degrade performance, but it is
useful in special situations, such as when applications do
their own caching. File I/O is done directly to/from user-
space buffers. The O_DIRECT flag on its own makes an effort
to transfer data synchronously, but does not give the
guarantees of the O_SYNC flag that data and necessary metadata
are transferred. To guarantee synchronous I/O, O_SYNC must be
used in addition to O_DIRECT. See NOTES below for further
discussion.

aio 简单介绍

说到 aio,会有三个东西:

  1. posix aio,在用户态使用 glic 实现,维护一个线程池来模拟异步IO。接口为 aio_read/aio_write/aio_xxxx。性能较差。
  2. linux aio,linux 特有的 aio 实现,接口为 aio_submit/aio_cancel 等5个函数。
  3. libaio,oracle 对 linux aio 的包装。

取块设备大小

作为一个存储引擎,应当为上层调用方提供当前存储空间利用率接口。那么如何获取一个块设备有多大呢?

可以使用 ioctl 函数的 BLKGETSIZE/BLKGETSIZE64 来获取。BLKGETSIZE 返回的是有多少块(每块是512字节)。所以最多获取 2TB 块设备的大小(2*32 512 byte = 2TB)。为了支持更大的设备,在较新的内核上支持了 BLKGETSIZE64,返回一个 int64 类型的数字,表示块设备有多少字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <fcntl.h>
#include <linux/fs.h>

main(int argc, char **argv)
{
int fd;
unsigned long numblocks=0;

fd = open(argv[1], O_RDONLY);
ioctl(fd, BLKGETSIZE, &numblocks);
close(fd);
printf("Number of blocks: %lu, this makes %.3f GB\n",
true numblocks,
true (double)numblocks * 512.0 / (1024 * 1024 * 1024));
}

microHOWTO: Get the size of a Linux block special device in C

如何获取设备的属性

属性包括块设备是否是SSD?是否支持 discard?在 linux 环境下提供了 procfs 的方式来获取这些信息。设备的属性在 /sys/block/{device name}/queue/ 目录下,例如我们可以通过 /sys/block/sda/queue/rotational 文件来获取块设备是不是SSD。

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
➜  ~ ll /sys/block/sda/queue/                         
total 0
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 add_random
-r--r--r-- 1 root root 4.0K 8月 28 19:41 chunk_sectors
-r--r--r-- 1 root root 4.0K 8月 28 19:41 dax
-r--r--r-- 1 root root 4.0K 8月 28 19:41 discard_granularity
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 discard_max_bytes
-r--r--r-- 1 root root 4.0K 8月 28 19:41 discard_max_hw_bytes
-r--r--r-- 1 root root 4.0K 8月 28 19:41 discard_zeroes_data
-r--r--r-- 1 root root 4.0K 8月 28 19:41 hw_sector_size
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 io_poll
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 io_poll_delay
drwxr-xr-x 2 root root 0 8月 28 19:41 iosched
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 iostats
-r--r--r-- 1 root root 4.0K 8月 28 19:41 logical_block_size
-r--r--r-- 1 root root 4.0K 8月 28 19:41 max_discard_segments
-r--r--r-- 1 root root 4.0K 8月 28 19:41 max_hw_sectors_kb
-r--r--r-- 1 root root 4.0K 8月 28 19:41 max_integrity_segments
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 max_sectors_kb
-r--r--r-- 1 root root 4.0K 8月 28 19:41 max_segments
-r--r--r-- 1 root root 4.0K 8月 28 19:41 max_segment_size
-r--r--r-- 1 root root 4.0K 8月 28 19:41 minimum_io_size
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 nomerges
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 nr_requests
-r--r--r-- 1 root root 4.0K 8月 28 19:41 optimal_io_size
-r--r--r-- 1 root root 4.0K 8月 28 19:41 physical_block_size
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 read_ahead_kb
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 rotational
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 rq_affinity
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 scheduler
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 wbt_lat_usec
-rw-r--r-- 1 root root 4.0K 8月 28 19:41 write_cache
-r--r--r-- 1 root root 4.0K 8月 28 19:41 write_same_max_bytes
-r--r--r-- 1 root root 4.0K 8月 28 19:41 write_zeroes_max_bytes
-r--r--r-- 1 root root 4.0K 8月 28 19:41 zoned
➜ ~ cat /sys/block/sda/queue/rotational
1