在产品中使用了ramdisk, 看了一部分的源码,分析和共享一下。内核源码 2.6.18
安装ramdisk
ramdisk 在linux里面被认为是个内存的块设备,通常以rm0,rm1... 挂在dev下,首先需要格式化块设备成linux的文件系统,然后在将想使用的目录mount 到dev/rm0...下,这样操作目录,在目录里操作文件就在内存里。
ramdisk是一个块设备,需要格式化成linux能认识的文件系统(ext2,ext3),那么对ramdisk的 IO的操作 ,就绕不过page cache,也就是无意中多了一次内存复制。
通常在linux中,由于块设备的随机读写是急剧降低性能的,linux 系统中会有一些合并算法,我们将每次设备的请求认为是一个操作,那么在内核中不会将每一次的请求都提交给块设备,而是在一定的时间内把请求合并成一个队列,然后 调整队列里请求的顺序,尽量让请求(比如块设备中地址相近的)排序在相邻的位置,这样可以提高磁头访问的效率。
最后将队列提交给块设备,相见常用的四种电梯调度算法,就不描述了。
ramdisk是以内存作为访问的块设备,随机访问的速度非常块,调度算法不能提高IO的效率,反而因为合并请求而导致访问性能下降,所以ramdisk没有使用调度算法,在初始化ramdisk的时候,重新设置了queue的调度算法。
drivers/block/rd.c
static int __init rd_init(void) { ... blk_queue_make_request(rd_queue[i], &rd_make_request); .... }
在函数里rd_make_request,核心就是rd_blkdev_pagecache_IOstatic int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector, struct address_space *mapping) { pgoff_t index = sector >> (PAGE_CACHE_SHIFT - 9); unsigned int vec_offset = vec->bv_offset; int offset = (sector << 9) & ~PAGE_CACHE_MASK; int size = vec->bv_len; int err = 0; do { int count; struct page *page; char *src; char *dst; count = PAGE_CACHE_SIZE - offset; if (count > size) count = size; size -= count; page = grab_cache_page(mapping, index); if (!page) { err = -ENOMEM; goto out; } if (!PageUptodate(page)) make_page_uptodate(page); index++; if (rw == READ) { src = kmap_atomic(page, KM_USER0) + offset; dst = kmap_atomic(vec->bv_page, KM_USER1) + vec_offset; } else { src = kmap_atomic(vec->bv_page, KM_USER0) + vec_offset; dst = kmap_atomic(page, KM_USER1) + offset; } offset = 0; vec_offset += count; memcpy(dst, src, count); kunmap_atomic(src, KM_USER0); kunmap_atomic(dst, KM_USER1); if (rw == READ) flush_dcache_page(vec->bv_page); else set_page_dirty(page); unlock_page(page); put_page(page); } while (size); out: return err; }
a. Grab_Cache_Page
grab_cache_page -> find_or_create_page