文件在磁盘的存储不同于程序员所看到的文件,主要表现在两个方面:块可以分散在磁盘上(尽管文件系统尽力保持块连续存放以提高访问速度),以及程序员看到的文件似乎比实际的文件大,这是因为程序可以把洞引人文件(通过lseek ()系统调用)。
从本篇博文开始,我们将介绍Ext2文件系统如何管理磁盘空间,也就是说,如何分配和释放索引节点和数据块。有两个主要的问题必须考虑:
(1)空间管理必须尽力避免文件碎片,也就是说,避免文件在物理上存放于几个小的、不相邻的盘块上。文件碎片增加了对文件的连续读操作的平均时间,因为在读操作期间,磁头必须频繁地重新定位。这个问题类似于在内存管理中的“伙伴系统算法”博文中所讨论的RAM的外部碎片问题。
(2)空间管理必须考虑效率,也就是说,内核应该能从文件的偏移量快速地导出Ext2分区上相应的逻辑块号。为了达到此目的,内核应该尽可能地限制对磁盘上寻址表的访问次数,因为对该表的访问会极大地增加文件的平均访问时间。
1 创建索引节点
ext2_new_inode()函数创建Ext2磁盘的索引节点,返回相应的索引节点对象的地址(或失败时为NULL)。该函数谨慎地选择存放该新索引节点的块组;它将无联系的目录散放在不同的组,而且同时把文件存放在父目录的同一组。为了平衡普通文件数与块组中的目录数,Ext2为每一个块组引入“债(debt) ”参数。
ext2_new_inode函数有两个参数:dir,所创建索引节点父目录对应的索引节点对象的地址,新创建的索引节点必须插入到这个目录中,成为其中的一个目录项;mode,要创建的索引节点的类型。后一个参数还包含一个MS_SYNCHRONOUS标志,该标志请求当前进程一直挂起,直到索引节点被分配成功或失败。该函数代码如下:
struct inode *ext2_new_inode(struct inode *dir, int mode)
{
struct super_block *sb;
struct buffer_head *bitmap_bh = NULL;
struct buffer_head *bh2;
int group, i;
ino_t ino = 0;
struct inode * inode;
struct ext2_group_desc *gdp;
struct ext2_super_block *es;
struct ext2_inode_info *ei;
struct ext2_sb_info *sbi;
int err;
sb = dir->i_sb;
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
ei = EXT2_I(inode);
sbi = EXT2_SB(sb);
es = sbi->s_es;
if (S_ISDIR(mode)) {
if (test_opt(sb, OLDALLOC))
group = find_group_dir(sb, dir);
else
group = find_group_orlov(sb, dir);
} else
group = find_group_other(sb, dir);
if (group == -1) {
err = -ENOSPC;
goto fail;
}
for (i = 0; i < sbi->s_groups_count; i++) {
gdp = ext2_get_group_desc(sb, group, &bh2);
brelse(bitmap_bh);
bitmap_bh = read_inode_bitmap(sb, group);
if (!bitmap_bh) {
err = -EIO;
goto fail;
}
ino = 0;
repeat_in_this_group:
ino = ext2_find_next_zero_bit((unsigned long *)bitmap_bh->b_data,
EXT2_INODES_PER_GROUP(sb), ino);
if (ino >= EXT2_INODES_PER_GROUP(sb)) {
/*
* Rare race: find_group_xx() decided that there were
* free inodes in this group, but by the time we tried
* to allocate one, they're all gone. This can also
* occur because the counters which find_group_orlov()
* uses are approximate. So just go and search the
* next block group.
*/
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
if (ext2_set_bit_atomic(sb_bgl_lock(sbi, group),
ino, bitmap_bh->b_data)) {
/* we lost this inode */
if (++ino >= EXT2_INODES_PER_GROUP(sb)) {
/* this group is exhausted, try next group */
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
/* try to find free inode in the same group */
goto repeat_in_this_group;
}
goto got;
}
/*
* Scanned all blockgroups.
*/
err = -ENOSPC;
goto fail;
got:
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & MS_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
brelse(bitmap_bh);
ino += group * EXT2_INODES_PER_GROUP(sb) + 1;
if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "ext2_new_inode",
"reserved inode or inode > inodes count - "
"block_group = %d,inode=%lu", group,
(unsigned long) ino);
err = -EIO;
goto fail;
}
percpu_counter_mod(&sbi->s_freeinodes_counter, -1);
if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter);
spin_lock(sb_bgl_lock(sbi, group));
gdp->bg_free_inodes_count =
cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
if (S_ISDIR