在block_dev.c程序中,主要包括块设备的读写操作,之前的文章中也说过,磁盘的读写都是需要通过内核中高速缓冲区,让高速缓冲管理程序决定合适读写。很直接的感觉就是用户态数据先到内核缓冲区,然后再决定何时写,今天看到0.11版本的源代码后,发现块设备写磁盘不是想像的那么简单。

函数签名是 int block_write(int dev, long * pos, char *buf, int count),内核是这样处理写磁盘的:

首先根据pos计算出开始读写磁盘块序号block,并求出需读第1字节在该块中的偏移位置offset。然后把需要写入的磁盘块读如内核缓冲区,如果写入的数据小于一个磁盘块的大小,则需要读入将被修改的数据块并且预读接下来的两块磁盘块,如果写入的数据刚好为一个磁盘块,则不需从磁盘读任何数据,只需申请一块内核缓冲区即可。

为什么初衷是写磁盘,可是程序中没有出现任何从内核缓冲区写磁盘的语句呢?

这是延迟写策略的体现。块设备的操作是以块为单位的,如果写的字节数小于一块磁盘数据块的大小,如果不把数据读入缓冲区操作的话,则所写的数据中多余的字节将会覆盖了原先正确的数据,只有读入内存了程序才能控制写的大小。而当所写的字节数是一个磁盘块大小时(程序中不会出现跨两个磁盘的情况),就不需要从磁盘读整块数据了,因为即使读了数据进缓冲,这些数据也会被即将从用户态写如内核缓存块的数据整块的覆盖了,所以就没有任何必要去做这件没有意义的事了。

当用户的数据全部写到缓冲区后,设置缓冲区的“脏”标志,然后就可调用brelea()释放这些缓冲区,这样其他进程需要缓冲区的时候就可以使用这些缓冲区了,使用前,检测到这些缓存区是脏的,于是就把数据写回磁盘,实现了延迟写。