从2.6.15的内核开始,顶层目录下面有了一个叫做block的目录,内核目录结构变成了现在这个样子:
localhost-1:/usr/src/linux-2.6.22.1 # ls
COPYING Documentation MAINTAINERS README arch crypto fs init kernel mm scripts sound CREDITS Kbuild Makefile REPORTING-BUGS block drivers include ipc lib net security usr
进入block目录,用旁光看一下:
localhost:/usr/src/linux-2.6.22.1/block # ls
Kconfig Makefile blktrace.c deadline-iosched.c genhd.c ll_rw_blk.c scsi_ioctl.c
Kconfig.iosched as-iosched.c cfq-iosched.c elevator.c ioctl.c noop-iosched.c
### Linux Block 层详解
#### 一、Block 层简介
在 Linux 内核发展过程中,Block 层作为存储子系统的重要组成部分,在处理磁盘读写请求方面扮演着至关重要的角色。从内核版本 2.6.15 开始,内核源代码的顶级目录下增加了一个名为“block”的子目录,这标志着 Block 层被正式引入到了 Linux 内核的核心架构中。
在 2.6.22.1 版本的内核中,我们可以通过以下命令查看 block 目录的存在:
```
localhost-1:/usr/src/linux-2.6.22.1 # ls
```
执行该命令后,我们可以看到 block 目录与其他核心组件并列存在,如 arch、crypto、fs 等。接下来,进入 block 目录查看其内容:
```
localhost:/usr/src/linux-2.6.22.1/block # ls
```
此时可以看到 block 目录下的主要文件,包括但不限于:
- `Kconfig`: 配置选项文件,用于指定 Block 层的各种配置选项。
- `Makefile`: 编译脚本,用于编译 Block 层相关的模块。
- `blktrace.c`: 用于跟踪块设备上的 I/O 操作。
- `deadline-iosched.c`、`cfq-iosched.c`、`noop-iosched.c`: 这些文件分别实现了不同的 I/O 调度程序,用于管理 I/O 请求的优先级和调度策略。
- `genhd.c`: 通用硬盘驱动程序的实现,用于处理块设备的初始化等操作。
- `ll_rw_blk.c`: 低级别读写块函数,是 Block 层与底层设备交互的核心部分之一。
- `scsi_ioctl.c`: SCSI 设备的用户空间控制接口。
#### 二、Block 层内部结构分析
为了更深入地理解 Block 层的工作原理,我们需要进一步分析 block 目录下的关键文件。我们来看一下 Makefile 文件中的配置选项:
```
obj-$(CONFIG_BLOCK):=elevator.oll_rw_blk.oioctl.ogenhd.oscsi_ioctl.o
obj-$(CONFIG_IOSCHED_NOOP)+=noop-iosched.o
obj-$(CONFIG_IOSCHED_AS)+=as-iosched.o
obj-$(CONFIG_IOSCHED_DEADLINE)+=deadline-iosched.o
obj-$(CONFIG_IOSCHED_CFQ)+=cfq-iosched.o
obj-$(CONFIG_BLK_DEV_IO_TRACE)+=blktrace.o
```
这里通过 CONFIG_BLOCK 等宏来控制 Block 层各个组件是否被编译进内核。例如,CONFIG_IOSCHED_NOOP 代表 No-Op(无操作)I/O 调度程序的启用。
接下来,我们关注 genhd.c 文件中的关键函数 `subsys_initcall(genhd_device_init);`,该函数在内核启动时会被调用来初始化 Block 层设备。具体地,`genhd_device_init` 函数实现如下:
```c
static int __init genhd_device_init(void)
{
int err;
bdev_map = kobj_map_init(base_probe, &block_subsys_lock);
blk_dev_init();
err = subsystem_register(&block_subsys);
if (err < 0)
printk(KERN_WARNING "%s: subsystem_register error: %d\n",
__FUNCTION__, err);
return err;
}
```
其中,`bdev_map` 的初始化通过 `kobj_map_init` 来完成,用于创建块设备映射表;`blk_dev_init` 是 Block 层设备初始化的核心函数,它会在 `genhd_device_init` 被调用时被执行。
继续分析 ll_rw_blk.c 文件中的 `blk_dev_init()` 函数:
```c
int __init blk_dev_init(void)
{
int i;
kblockd_workqueue = create_workqueue("kblockd");
// 更多初始化操作...
}
```
这个函数负责创建一个名为 “kblockd” 的工作队列,用于异步处理块设备相关的任务。
#### 三、Block 层的主要职责
Block 层的主要职责包括:
1. **I/O 调度**: 通过对不同 I/O 调度算法的支持,Block 层能够有效地管理磁盘的读写请求,从而提高系统的整体性能。
2. **错误处理**: Block 层提供了错误检测和恢复机制,确保数据的完整性和一致性。
3. **设备初始化**: Block 层负责初始化各种块设备,为上层应用程序提供统一的访问接口。
4. **性能优化**: 通过对 I/O 请求进行合并、排序等操作,Block 层可以显著提高磁盘 I/O 的效率。
#### 四、Block 层的关键概念
- **Block Device**: 块设备是一种允许按固定大小的数据块进行读写的设备,如硬盘、SSD 等。
- **I/O Scheduler**: I/O 调度器用于决定何时以及如何将 I/O 请求发送到磁盘,不同的调度器有不同的策略,如 CFQ、No-Op 和 Deadline。
- **Queue**: 在 Block 层中,每个设备都有一个与之关联的 I/O 请求队列,用于存储待处理的 I/O 请求。
- **Request**: I/O 请求是操作系统发送给块设备的具体操作指令,包括读取或写入数据。
Block 层是 Linux 内核中的一个重要组成部分,它不仅提供了对块设备的抽象封装,还通过高效的 I/O 调度机制和错误处理策略提高了系统的整体性能和可靠性。对于深入理解 Linux 内核及其存储子系统的开发者来说,掌握 Block 层的原理和实现细节是必不可少的。