RamFS 与 TempFS
Ramfs顾名思义是内存文件系统,它工作于虚拟文件系统(VFS)层。不能格式化,可以创建多个,在创建时可以指定其最大能使用的内存大小。如果你的Linux已经将Ramfs编译进内核,你就可以很容易地使用Ramfs了。创建一个目录,加载Ramfs到该目录即可。 # mkdir -p /RAM1 # mount -t ramfs none /RAM1 缺省情况下,Ramfs被限制最多可使用内存大小的一半。可以通过maxsize (以kbyte为单位)选项来改变。 # mkdir -p /RAM1 # mount -t ramfs none /RAM1 -o maxsize=10000 以上即创建了一个限定了最大使用内存大小为10M的ramdisk。 Tmpfs是一个虚拟内存文件系统,它不同于传统的用块设备形式来实现的ramdisk,也不同于针对物理内存的Ramfs。Tmpfs可以使用物理内 存,也可以使用交换分区。在Linux内核中,虚拟内存资源由物理内存(RAM)和交换分区组成,这些资源是由内核中的虚拟内存子系统来负责分配和管理。 Tmpfs就是和虚拟内存子系统来"打交道"的,它向虚拟内存子系统请求页来存储文件,它同Linux的其它请求页的部分一样,不知道分配给自己的页是在 内存中还是在交换分区中。Tmpfs同Ramfs一样,其大小也不是固定的,而是随着所需要的空间而动态的增减。使用tmpfs,首先你编译内核时得选择 "虚拟内存文件系统支持(Virtual memory filesystem support)" ,然后就可以加载tmpfs文件系统了。 # mkdir -p /mnt/tmpfs # mount tmpfs /mnt/tmpfs -t tmpfs 为了防止tmpfs使用过多的内存资源而造成系统的性能下降或死机,可以在加载时指定tmpfs文件系统大小的最大限制。 # mount tmpfs /mnt/tmpfs -t tmpfs -o size=32m 以上创建的tmpfs文件系统就规定了其最大的大小为32M。不管是使用ramfs还是tmpfs,必须明白的是,一旦系统重启,它们中的内容将会丢失。所以那些东西可以放在内存文件系统中得根据系统的具体情况而定。
RAMFS文件系统
RAMFS是一个非常巧妙的,利用VFS自身结构而形成的内存文件系统. RAMFS没有自已的文件存储结构,它的文件存储于page cache中, 目录结构由dentry链表本身描述,文件则由VFS的inode结构本身描述. 从RAMFS可看出,VFS本质上可看成一种内存文件系统, 它统一了文件在内核中的表示方式并对磁盘文件系统进行缓冲.
代码摘录分析如下:
; fs/ramfs/inode.c static struct address_space_operations ramfs_aops = { 文件的低级页操作接口 readpage: ramfs_readpage, 读文件页块 writepage: ramfs_writepage, 写文件脏页入块设备 prepare_write: ramfs_prepare_write, 准备从用户写文件页块 commit_write: ramfs_commit_write 从用户写文件页块完成 }; static int ramfs_readpage(struct file *file, struct page * page) { ; 将文件页块读入page所描述的页面
if (!Page_Uptodate(page)) { ; 当lseek()在文件中造成空洞时,会运行到这里 memset(kmap(page), 0, PAGE_CACHE_SIZE); 页面清零 kunmap(page); flush_dcache_page(page); SetPageUptodate(page); 标记为最新 } 现在的问题是什么情况下系统调用该函数并且Page_Uptodate(page)为TRUE. UnlockPage(page); return 0; } static int ramfs_writepage(struct page *page) { ; 将page cache中脏页写入块设备. ;对于RAMFS,这里将它重新标记为DIRTY,防止它们被系统丢弃 SetPageDirty(page); UnlockPage(page); return 0; } static int ramfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { ; 系统将用户数据拷贝到page之前调用此函数, ;offset是文件指针在页内的起始位置,to是在页内的终止位置 ;表示系统将要在page从offset到to的位置上拷贝用户数据.
void *addr = kmap(page); 取页面描述结构(page)所描述页面的地址 if (!Page_Uptodate(page)) { ; 对于RAMFS,当lseek()在文件中产生空洞时,运行到这里. memset(addr, 0, PAGE_CACHE_SIZE); flush_dcache_page(page); SetPageUptodate(page); } SetPageDirty(page); return 0; }
static int ramfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { ; 系统将用户数据拷贝到page之后调用此函数
struct inode *inode = page->mapping->host; // 取page所代表的文件,mapping结构是inode的一部分, loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; // page->index表示该page在文件中的页块号,to是该页的终止偏移量 kunmap(page); if (pos > inode->i_size) // inode->i_size为文件的尺寸 inode->i_size = pos; 如果文件的写入的终止位置大于文件原来的尺寸,则更新i_size的值 return 0; }
static struct file_operations ramfs_file_operations = { read: generic_file_read, 数据文件的通用高级读函数 write: generic_file_write, 数据文件的通用高级写函数 mmap: generic_file_mmap,数据文件的通用高级内存映射函数 fsync: ramfs_sync_file, }; static int ramfs_sync_file(struct file * file, struct dentry *dentry, int datasync) { return 0; } static struct file_operations ramfs_dir_operations = { read: generic_read_dir, 返回-EISDIR错误的函数 readdir: dcache_readdir, 目录文件的高级读目录函数 fsync: ramfs_sync_file, }; static struct inode_operations ramfs_dir_inode_operations = { create: ramfs_create, lookup: ramfs_lookup, link: ramfs_link, unlink: ramfs_unlink, symlink: ramfs_symlink, mkdir: ramfs_mkdir, rmdir: ramfs_rmdir, mknod: ramfs_mknod, rename: ramfs_rename, }; static int ramfs_create(struct inode *dir, struct dentry *dentry, int mode) { ; 在目录dir内创建一个以dentry为目录项的普通文件 return ramfs_mknod(dir, dentry, mode | S_IFREG, 0); } static struct dentry * ramfs_lookup(struct inode *dir, struct dentry *dentry) { ; 对于ramfs,所有的目录项都已经在dentry链表中,只有企图寻找VFS中不存在的项时,才会运行到这?br>?br> d_add(dentry, NULL); ; 将dentry加入目录链表,置空的inode,表示No such file or directories,就是说生成一个"虚"的目录项, ;之所以这样,为了加速create过程 return NULL; } static int ramfs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry) { struct inode *inode = old_dentry->d_inode;
; 在目录dir内创建一个以dentry所表示目录项的链接,指向old_entry目录项所表示的文件
if (S_ISDIR(inode->i_mode)) return -EPERM;
inode->i_nlink++; 文件的连接数,为0表示文件已删除 atomic_inc(&inode->i_count); /* New dentry reference */ dget(dentry); /* Extra pinning count for the created dentry */ d_instantiate(dentry, inode); 使目录项dentry指向文件inode return 0; } static int ramfs_unlink(struct inode * dir, struct dentry *dentry) { int retval = -ENOTEMPTY;
; 在目录dir内删除dentry所表示的目录项
if (ramfs_empty(dentry)) { 只能删除空目录 struct inode *inode = dentry->d_inode;
inode->i_nlink--; dput(dentry); /* Undo the count from "create" - this does all the work */ retval = 0; } return retval; } static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname) { int error; ; 在目录dir中创建名称为symname的符号链接文件目录项 ; 符号链接的名称作为符号链接文件的数据体存放在page cache中
error = ramfs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); if (!error) { int l = strlen(symname)+1; struct inode *inode = dentry->d_inode; error = block_symlink(inode, symname, l); } return error; } static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) { ; 在目录dir内创建一个以dentry为目录项的目录 return ramfs_mknod(dir, dentry, mode | S_IFDIR, 0); } static int ramfs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) { int error = -ENOTEMPTY; ; 将目录old_dir内的目录项改名为new_dir目录中的new_dentry所描述的目录项
if (ramfs_empty(new_dentry)) { struct inode *inode = new_dentry->d_inode; if (inode) { inode->i_nlink--; dput(new_dentry); } error = 0; } return error; } ; 在VFS中创建一个节点,用来描述文件,目录或符号链接 static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) { struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC; if (inode) { d_instantiate(dentry, inode); 将目录项dentry与文件描述inode相关联 dget(dentry); /* Extra count - pin the dentry in core */ error = 0; } return error; }
static struct super_operations ramfs_ops = { statfs: ramfs_statfs, put_inode: force_delete, }; static int ramfs_statfs(struct super_block *sb, struct statfs *buf) { ; 取文件系统结构参数 buf->f_type = RAMFS_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; buf->f_namelen = 255; return 0; }
; 获取一个属于ramfs的自由的inode. struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev) { struct inode * inode = new_inode(sb);
if (inode) { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_rdev = to_kdev_t(dev); inode->i_mapping->a_ops = &ramfs_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); 与设备文件相联的inode函数 break; case S_IFREG: 普通文件的inode函数 inode->i_fop = &ramfs_file_operations; break; case S_IFDIR: 目录文件的inode函数 inode->i_op = &ramfs_dir_inode_operations; inode->i_fop = &ramfs_dir_operations; break; case S_IFLNK: 符号链接的inode函数 inode->i_op = &page_symlink_inode_operations; break; } } return inode; } static int ramfs_empty(struct dentry *dentry) 判断目录是否为空 { struct list_head *list;
spin_lock(&dcache_lock); list = dentry->d_subdirs.next; 取子目录链
while (list != &dentry->d_subdirs) { struct dentry *de = list_entry(list, struct dentry, d_child);
if (ramfs_positive(de)) { spin_unlock(&dcache_lock); return 0; 如果dentry目录中存在一个实在的子目录项,则说明该目录非空. } list = list->next; } spin_unlock(&dcache_lock); return 1; } static inline int ramfs_positive(struct dentry *dentry) { ; 判断dentry是否实在,即dentry指向某个文件并且被dentry的散列表索引 return dentry->d_inode && !d_unhashed(dentry); } ; fs/devices.c void init_special_inode(struct inode *inode, umode_t mode, int rdev) { inode->i_mode = mode; if (S_ISCHR(mode)) { 如果为字符设备文件,提供字符设备的inode函数 inode->i_fop = &def_chr_fops; inode->i_rdev = to_kdev_t(rdev); } else if (S_ISBLK(mode)) { 如果为块设备文件,提供块设备的inode函数 inode->i_fop = &def_blk_fops; inode->i_rdev = to_kdev_t(rdev); inode->i_bdev = bdget(rdev); 指向块设备描述结构 } else if (S_ISFIFO(mode)) 如果为FIFO文件,提供FIFO的inode函数 inode->i_fop = &def_fifo_fops; else if (S_ISSOCK(mode)) 如果为SOCK文件,提供SOCK的inode函数 inode->i_fop = &bad_sock_fops; else printk(KERN_DEBUG "init_special_inode: bogus imode (%o)
", mode); } ; fs/buffer.c int block_symlink(struct inode *inode, const char *symname, int len) { struct address_space *mapping = inode->i_mapping; struct page *page = grab_cache_page(mapping, 0); int err = -ENOMEM; char *kaddr; ; 符号链接中的符号名存放在page cache中.
if (!page) goto fail; err = mapping->a_ops->prepare_write(NULL, page, 0, len-1); if (err) goto fail_map; kaddr = page_address(page); memcpy(kaddr, symname, len-1); mapping->a_ops->commit_write(NULL, page, 0, len-1); /* * Notice that we are _not_ going to block here - end of page is * unmapped, so this will only try to map the rest of page, see * that it is unmapped (typically even will not look into inode - * ->i_size will be enough for everything) and zero it out. * OTOH it's obviously correct and should make the page up-to-date. */ err = mapping->a_ops->readpage(NULL, page); wait_on_page(page); page_cache_release(page); 释放grab状态 if (err < 0) goto fail; mark_inode_dirty(inode); return 0; fail_map: UnlockPage(page); page_cache_release(page); fail: return err; }
实际例子: 在XOS NPM6上, / # mount -t ramfs none /tmp / # cd /tmp/ /tmp # ls /tmp # dd if=/dev/zero of=a bs=1M count=230 分配了230MB的内存,不过都存在cached上了,cached会增大230MB,内存减小230MB。 / # rm -f /tmp/a / # umount /tmp 这里/dev/zero 是当你读它时,产生 0x00 输出的设备。 dd if=/dev/zero of=initrd.img bs=512 count=8192 产生出来一个4兆文件,内容全都是零。
bs = 单元大小 count = 单元的数量 eg : dd if=/dev/hda of=/boot.bin bs=512 count=1 就是拷贝hda的前512个字节(一般都是主引导纪录)
|