块设备文件的readpage方法总是相同的。都是有blkdev_readpage( )函数实现的,它实际调用block_read_full_page( ):
static int blkdev_readpage(struct file * file, struct page * page)
{
return block_read_full_page(page, blkdev_get_block);
}
page参数代表的含义是 要将块设备文件的内容读到page对应的页框中去,你可能好奇从块设备文件的哪个地方开始读?读多少?这些信息其实全在page里。
block_read_full_page的第二个参数指向一个函数,该函数把相对于文件开始处的文件块号转换为块设备开始处的逻辑块号。对于块设备文件来说,这两个数是一样的。
static int blkdev_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh, int create)
{
bh->b_bdev = I_BDEV(inode);
bh->b_blocknr = iblock;
//可以看出对于块设备文件来说,相对于文件开始处的文件块号和相对块设备开始处的逻辑块号是一样的
set_buffer_mapped(bh);
return 0;
}
可能读者对相对于文件开始处的文件块号和相对块设备开始处的逻辑块号有点迷惑,我来具体解释下:
相对于文件开始处的文件块号:我们知道块是vfs和文件系统传送数据的基本单位。每个文件都是又很多个块组成的。我们要读写一个文件的某一个块,就得知道这个块是这个文件的第几个块,这就叫相对于文件开始处的文件块号。
对设备文件直接读写是一种“原始”访问,它绕过了文件系统,内核通过使用最大的块(4096)执行该操作。
每个块都需要自己的块缓冲区(块缓存),它是内核用来存放块内容的内存区。每个缓冲区对应一个缓冲区首部,用buffer_head描述(相当块缓存的元数据信息)。相对块设备开始处的逻辑块号:文件都是存在具体的物理设备上如磁盘,我们要往修改文件某块时,光知道该块相对文件是第几块是不够的,需要知道该块是在物理设备的第几个块才行。block_read_full_page的第二个参数就是一个实现块号转换的函数。对于块设备文件,它就是块设备的意思,所以两种块号相同,但对于普通文件就不同了。
言归正传,block_read_full_page代表从块设备读取一整页,看看它的实现:
int block_read_full_page(struct page *page, get_block_t *get_block)
{
struct inode *inode = page->mapping->host; //找到inode我们才能找到文件
sector_t iblock, lblock;