/fs/cramfs/inode.c
应该知道的:当前只支持页为4096如果出现错误可以检查这里。
目录:
0...........cramfs文件系统需要的操作结构体
1...........数据结构
2...........初始化
3...........总结
4...........附录[一] 数据的组织形式:(另有一文章详细介绍了)
5...........附录[二] cramfs文件系统
6...........附录[三] cramfs_read函数
7...........附录[四] cramfs_readdir函数
##操作结构体
static struct file_operations cramfs_directory_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.readdir = cramfs_readdir,
};
static struct inode_operations cramfs_dir_inode_operations = {
.lookup = cramfs_lookup,
};
static struct super_operations cramfs_ops = {
.put_super = cramfs_put_super,
.remount_fs = cramfs_remount,
.statfs = cramfs_statfs,
};
##cramfs数据结构
#cramfs 私有数据
struct cramfs_sb_info {
unsigned long magic;
unsigned long size;
unsigned long blocks;
unsigned long files;
unsigned long flags;
};
#
struct cramfs_super {
u32 magic; /* 0x28cd3d45 - random number */
u32 size; /* length in bytes */
u32 flags; /* feature flags */
u32 future; /* reserved for future use */
u8 signature[16]; /* "Compressed ROMFS" */
struct cramfs_info fsid; /* unique filesystem info */
u8 name[16]; /* user-defined name */
struct cramfs_inode root; /* root inode data */
};
#
struct cramfs_inode {
u32 mode:CRAMFS_MODE_WIDTH, uid:CRAMFS_UID_WIDTH;
/* SIZE for device files is i_rdev */
u32 size:CRAMFS_SIZE_WIDTH, gid:CRAMFS_GID_WIDTH;
/* NAMELEN is the length of the file name, divided by 4 and
rounded up. (cramfs doesn't support hard links.) */
/* OFFSET: For symlinks and non-empty regular files, this
contains the offset (divided by 4) of the file data in
compressed form (starting with an array of block pointers;
see README). For non-empty directories it is the offset
(divided by 4) of the inode of the first file in that
directory. For anything else, offset is zero. */
u32 namelen:CRAMFS_NAMELEN_WIDTH, offset:CRAMFS_OFFSET_WIDTH;
};
初始化:
static struct file_system_type cramfs_fs_type = {
.owner = THIS_MODULE,
.name = "cramfs",
.get_sb = cramfs_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
register_filesystem(&cramfs_fs_type);
unregister_filesystem(&cramfs_fs_type);
·所有的文件系统类型的初始化都只是将一个用于描述对应文件系统的file_system_type 结构
注册到内核中用于维护文件系统的连表中,此例就是结构,cramfs_fs_type
·应该已经注意到sp.get_sb的重要性了把。。
总结:
·//有待增加 cramfs物理介质中存在结构介绍。见附录[一]
·cramfs_get_sb(cramfs_fs_type.get_sb)中调用了get_sb_bdev(是获得一个外设备存在
的sb,而ramfs中调用的是get_sb_nodev)
··cramfs_fill_super:
::维护cramfs_sb_info私有数据结构其成员值是通过读取如flash中的开始区
中的super_block来获得 sb->s_fs_info = sbi;
::检查来确定是否是cramfs文件系统,检查了cramfs的flash
··get_cramfs_inode
::注意了这里一定是返回了一个初始化了inode->i_op,inode->i_fop
的inode指针。
::通过返回的root_inode动态申请root_dentry并把它的指针给sb->s_root.
·cramfs因为和其他的存储介质的不同,因而造成了读取IO的不同但和内存类似的norflash,所以其低层
读是要独立实现的,但从文件系统的管理来说,就只需要处理好下面的3个函数指针组就OK了。
所以说下面三个函数指针是各种不同文件系统实现内核接口。
static struct file_operations cramfs_directory_operations = {
};
static struct inode_operations cramfs_dir_inode_operations = {
};
static struct super_operations cramfs_ops = {
};
-----------------------------------------------------------------------------------
附录[一]
底层数据的组织形式:
首先鄙视一下英语,吗的都是英语写的在baidu.com上跟本没发现有发现中文介绍的。
在鄙视下。介绍的时候,没有用到一张图。
以后给老外的汉语6级考试都要用周杰伦的歌曲做听力而且只能听一次。
总体:由3部分构成。
:
·第一部分是cramfs文件系统独有的数据结构cramfs_sb.具体看上面代码部分。
·第二部分是目录组织结构。相应的数据结构描述为cramfs_inode.
目录的等级顺序是按照横向优先的原则。 在吸收了台湾牛工程师的文章后,我也可以在
次瞎吹一下了。
例如:你要找到/etc/profile这个文件。。程序流程是:
在cramfs_super_block下可以找到cramfs_root.如果你运行ls 的话。那么程序就
去搜索从flash开始偏移cramfs_root.offset*4的地方开始查找cramfs_inode节点以及其后的文件
名字。在这一系列的cramfs_inode中找到etc这个名字。在在etc_cramfs_inode.offset*4的偏移
地址上找到文件profile_cramfs_inode.在其中可以找到它的大小和偏移地址。
这个就是横向优先的原则一个例子把。
需要知道的:
·如果用到递归,那么需要确定判断inode +name 结束的标志。
·cramfs_inode.offset是非常重要的。相对flash开始的偏移,所有的数据结构都是通过这个找到的。
·第三部分是数据区。
附录[一] 有详细介绍。
台湾科技如此发达是有理由的:
象LINUX kernel中稳当介绍都很少用图的,但是我门台湾的工程师就是非常详细的介绍了 cramfs
并用,图文例子并用的方法,让你看不懂都不行的态度完成了附录[一]的杰作。。
附录[二]
cramfs文件系统 1、cramfs的特点
在嵌入式的环境之下,内存和外存资源都需要节约使用。如果使用RAMDISK方式来使用文件系统,那么在
系 统运行之后,首先要把外存(Flash)上的映像文件解压缩到内存中,构造起RAMDISK环境,才可以开
始运行程序。但是它也有很致命的弱点。在正常情 况下,同样的代码不仅在外存中占据了空间(以压缩
后的形式存在),而且还在内存中占用了更大的空间(以解压缩之后的形式存在),这违背了嵌入式环境
下尽量 节省资源的要求。
使用cramfs就是一种解决这个问题的方式。cramfs是一个压缩式的文件系统,它并不需要一次性地将文
件系统中的 所有内容都解压缩到内存之中,而只是在系统需要访问某个位置的数据的时侯,马上计算出
该数据在cramfs中的位置,将其实时地解压缩到内存之中,然后通 过对内存的访问来获取文件系统中需
要读取的数据。cramfs中的解压缩以及解压缩之后的内存中数据存放位置都是由cramfs文件系统本身进
行维护的, 用户并不需要了解具体的实现过程,因此这种方式增强了透明度,对开发人员来说,既方便,
又节省了存储空间。
cramfs拥有以下一些特性:
采用实时解压缩方式,但解压缩的时侯有延迟。
cramfs的数据都是经过处理、打包的,对其进先写操作有一定困难。所以cramfs不支持写操作,这个特性
刚好适合嵌入式应用中使用Flash存储文件系统的场合。
在cramfs中,文件最大不能超过16MB。
支持组标识(gid),但是mkcramfs只将gid的低8位保存下来,因此只有这8位是有效的。
支持硬链接。但是cramfs并没有完全处理好,硬链接的文件属性中,链接数仍然为1.
cramfs的目录中,没有“.”和“..”这两项。因此,cramfs中的目录的链接数通常也仅有一个。
cramfs中,不会保存文件的时间戳(timestamps)信息。当然,正在使用的文件由于inode保存在内存中,
因此其时间可以暂时地变更为最新时间,但是不会保存到cramfs文件系统中去。
当前版本的cramfs只支持PAGE_CACHE_SIZE为4096的内核。因此,如果发现cramfs不能正常读写的时侯,
可以检查一下内核的参数设置。
2、使用cramfs
可以从下载cramfs-1.1.tar.gz。然后执行
tar zxvf cramfs-1.1.tar.gz
进入解包之后生成cramfs-1.1目录,执行编译命令:
make
编译完成之后,会生成mkcramfs和cramfsck两个工具,其中cramfsck工具是用来创建cramfs文件系统的,
而mkcramfs工具则用来进行cramfs文件系统的释放以及检查。
下面是mkcramfs的命令格式:
mkcramfs [-h] [-e edition] [-i file] [-n name] dirname outfill
mkcramfs的各个参数解释如下:
-h:显示帮助信息
-e edition:设置生成的文件系统中的版本号
-i file:将一个文件映像插入这个文件系统之中(只能在Linux2.4.0以后的内核版本中使用)
-n name:设定cramfs文件系统的名字
dirname:指明需要被压缩的整个目录树
outfile:最终输出的文件
cramfsck的命令格式:
cramfsck [-hv] [-x dir] file
cramfsck的各个参数解释如下:
-h:显示帮助信息
-x dir:释放文件到dir所指出的目录中
-v:输出信息更加详细
file:希望测试的目标文件
附录[三]
static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len)
分析它的原因:
cramfs的性能和这个函数有着 密切的关系,同时也可以展现出内存与程序之间的关系。
它存在的历史使命:
通过参数struct super_block *sb, unsigned int offset, unsigned int len,来复制
cramfs中指定的数据到内建内存池中,并返回对应数据在内存池中的指针。
它设计角度:
·读时解压,因此每次读数据时候从cramfs分区中读到内存的数据不适合太大和太小,
如太大,解压时间延长会让人无法接受,如太小,就会降低读cramfs文件系统文件
的性能,太过频繁的进行系统page的读取,也是降低了性能,虽然增加了读取的精度
(现在精度是4K*4)。
·双缓冲。 对于一个较大文件,临近读取的可能性更大的情况下,那么在读了一个16K后在次
读这个16K的可能性是很大的,所以用双缓冲,当第二次读取完其他文件后,如果在次读取第
一次的文件的内容,那么这种双缓冲的设计就更增加了系统的性能。
例如:当我门 ls /use/a_user
ls /use/b_user
ls /use/a_user
·因此建立一定大小的缓冲池是必要的,而双缓冲的设计更增加了系统处理文件的性能。
它的代码:
····缓冲池恒定量
#define READ_BUFFERS (2) //双缓冲池
/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */
#define NEXT_BUFFER(_ix) ((_ix) ^ 1) //双缓冲池标志交换函数
/*
* BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed"
* data that takes up more space than the original and with unlucky
* alignment.
*/
#define BLKS_PER_BUF_SHIFT (2)
#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) //每个缓冲池4个块或者页
#define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE)4k*4 //快和页相等
static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; //双缓冲池工2*4k*4
static unsigned buffer_blocknr[READ_BUFFERS]; //缓冲池中对应的cramfs文
//件系统中起始块号
static struct super_block * buffer_dev[READ_BUFFERS]; //对应cramfs分区中
//设备内存sb数据结构
static int next_buffer; //双缓冲交换标志
/*
read_buffers是缓冲池,就是cramfs_read致死都要维护和追求的对象,也是数据存储的地方。
buffer_blocknr是记录缓冲池对应flash中cramfs分区的块号。 -----------------用于管理
buffer_dev是记录当前读cramfs文件系统中的super_block的内存数据结构指针。--------用于管理
代码中只用它来做识别用----在确定是否打算读的数据已经在缓冲池的时候用到这个变量。
*/
/*
* Returns a pointer to a buffer containing at least LEN bytes of
* filesystem starting at byte offset OFFSET into the filesystem.
*/
static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len)
{
/*
定义一写局部变量
mapping 变量用于norflash页读取的时候用。
*/
struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
struct page *pages[BLKS_PER_BUF];
unsigned i, blocknr, buffer, unread;
unsigned long devsize;
char *data;
/*
形成一个良好的习惯。对于存储类的设备的读写时候,用页来管理而程序中就是用偏移量来表示是很科学
而又容易被人接受。
确定快号和起始块内的偏移量
*/
if (!len)
return NULL;
blocknr = offset >> PAGE_CACHE_SHIFT; //块号。
offset &= PAGE_CACHE_SIZE - 1; //块内偏移地址。
/* Check if an existing buffer already has the data.. */
/* 检查已经存在的缓冲池中的数据是不是现在要读取的数据? */
for (i = 0; i < READ_BUFFERS; i++) { //第一次运行会无任何执行。
unsigned int blk_offset;
/* buffer_dev用于识别要读的数据和已经存在的数据不是在同一个cramfs 的分区中 */
if (buffer_dev[i] != sb)
continue;
/* 如果在同一个分区中,那么存在的数据是和我门现在要读的数据是一样的么?*/
if (blocknr < buffer_blocknr[i])
continue;
blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT;
blk_offset += offset;
if (blk_offset + len > BUFFER_SIZE)
continue;
/* 确定是 并返回地址 */
return read_buffers[i] + blk_offset;
}
/*上面在第一次读cramfs 分区是不会运行的 */
/*下面着句代码 更体现了super_block是一个集合了文件系统管理信息数据结构
sb中已经包含了它描述分区的地址空间和分区的大小。单位是块。
*/
devsize = mapping->host->i_size >> PAGE_CACHE_SHIFT; //i_size以字节为单位的文件大小
//这里就是块里描述文件大小
//这里就是表示cramfs分区的大小了
/* Ok, read in BLKS_PER_BUF pages completely first. */
/*用系统的内存页读取我门的数据*/
unread = 0;
for (i = 0; i < BLKS_PER_BUF; i++) {
struct page *page = NULL;
if (blocknr + i < devsize) {
page = read_cache_page(mapping, blocknr + i,
(filler_t *)mapping->a_ops->readpage,
NULL);
/* synchronous error? */
if (IS_ERR(page))
page = NULL;
}
pages[i] = page;
}
for (i = 0; i < BLKS_PER_BUF; i++) {
struct page *page = pages[i];
if (page) {
wait_on_page_locked(page);
if (!PageUptodate(page)) {
/* asynchronous error */
page_cache_release(page);
pages[i] = NULL;
}
}
}
buffer = next_buffer;
next_buffer = NEXT_BUFFER(buffer);
buffer_blocknr[buffer] = blocknr; //记录块号
buffer_dev[buffer] = sb; //记录super_block数据结构指针
/* 把数据读到我门的缓冲池中,并释放系统内存页指针 */
data = read_buffers[buffer];
for (i = 0; i < BLKS_PER_BUF; i++) {
struct page *page = pages[i];
if (page) {
memcpy(data, kmap(page), PAGE_CACHE_SIZE);
kunmap(page);
page_cache_release(page);
} else
memset(data, 0, PAGE_CACHE_SIZE);
data += PAGE_CACHE_SIZE;
}
return read_buffers[buffer] + offset;
}
附录[四]
static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
分析它的原因:
为了揭示出file_operations.readdir在cramfs中的内部实现,以及和norflash有着什么样子的联系呢。
它存在的历史使命:
返回应用程序对系统调用read_dir的dirent结构体。就是在cramfs 找到对应的cramfs_inode和name .
需要知道的:
·对于一个cramfs目录来说,它的inode->i_size 应该就是其目录下目录和目录名字的总和?
***还需要验证。
·因为dirent是用户空间的指针,所以提供了filldir这样一个函数指针用来处理这个返回指针内容的函数。
代码:
static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
struct super_block *sb = inode->i_sb;
char *buf;
unsigned int offset;
int copied;
/* Offset within the thing. */
/*offset和cramfs_inode中的offset是一个意思。所以可见各种不同文件系统中的file.f_pos都具有
因地制宜的思想*/
offset = filp->f_pos;
/*这个目录的inode->i_size应该是用来描述这个目录包括的文件和目录用数据结构cram_inode和name
在内存中的大小。同时也体现了做为文件和目录管理信息的数据结构inode是多么的尽职啊。这里就是说
如果需要的dirent信息在cramfs of norflash中没有就返回*/
if (offset >= inode->i_size)
return 0;
/* Directory entries are always 4-byte aligned */
/*这个是cramfs组织结构的规定,详细请看附录文件[一]台湾工程师的文章*/
if (offset & 3)
return -EINVAL;
用于暂寸目录的名字大小和dirent中的名字数组大小一样
buf = kmalloc(256, GFP_KERNEL);
if (!buf)
return -ENOMEM;
copied = 0;
while (offset < inode->i_size) {
struct cramfs_inode *de;
unsigned long nextoffset;
char *name;
ino_t ino;
mode_t mode;
int namelen, error;
down(&read_mutex);
de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+256);
name = (char *)(de+1);
/*
* Namelengths on disk are shifted by two
* and the name padded out to 4-byte boundaries
* with zeroes.
*/
namelen = de->namelen << 2;
memcpy(buf, name, namelen);
ino = CRAMINO(de);
mode = de->mode;
up(&read_mutex);
/*计算方法:因为cramfs是行向优先算法。所以当前目录和目录名字在norflash存储的后面就是
同级目录的下个目录的cramfs_inode和目录名 */
nextoffset = offset + sizeof(*de) + namelen;
/*检查目录名是否有错误*/
for (;;) {
if (!namelen) {
kfree(buf);
return -EIO;
}
if (buf[namelen-1])
break;
namelen--;
}
error = filldir(dirent, buf, namelen, offset, ino, mode >> 12);
if (error)
break;
offset = nextoffset;
filp->f_pos = offset;
copied++;
}
}
kfree(buf);
return 0;
}