EXT2檔案系統實現原理
目錄
EXT2檔案系統結構概覽
1.1 EXT2檔案系統結構框圖
每一個檔案或者目錄在磁碟上都有一個inode用於管理檔案本身屬性資訊,還有資料塊用於存放檔案內容。其inode'和資料塊關係如下圖:
如果檔案比較小,其資料塊少於12個,其資料塊索引就放在inode->i_blocks中,如果檔案比較大,操作12個數據塊就需要分配間接塊來儲存資料塊索引
1.2 EXT2重要資料結構
super_block是VFS中的標準結構,通過成員s_fs_info與特定檔案系統相連
truct super_block |
|
struct list_head s_list |
用於將超級塊掛到全域性連結串列super_blocks中 |
dev_t s_dev |
檔案系統所在裝置的裝置號 |
unsigned long s_blocksize |
檔案系統塊大小 |
struct file_system_type *s_type |
檔案系統型別,比如ext2_fs_type |
const struct super_operations *s_op |
服裝inode的分配inode元資料的同步等等 |
struct dentry *s_root |
檔案系統根目錄的dentry |
struct block_device *s_bdev |
檔案系統所在塊裝置對應的block_device |
struct hlist_node s_instances |
用於掛到連結串列file_system_type ->fs_supers |
void *s_fs_info |
指向儲存特定檔案系統的結構,比如ext2_sb_info |
struct list_head s_inodes |
檔案系統所有開啟檔案的inode連結串列 |
…… |
…… |
結構體ext2_sb_info包含特定檔案系統的所有資訊,包含超級塊,組描述符等等:
struct ext2_sb_info |
|
unsigned long s_inodes_per_block |
每個block中可以存放多少個inode描述符 |
unsigned long s_blocks_per_group |
每個塊組中包含的資料塊數 |
unsigned long s_inodes_per_group |
每個塊組中包含的inode數 |
unsigned long s_itb_per_group |
一個塊組中用於存放inode的塊數 |
unsigned long s_gdb_count |
用於存放組描述符的塊數 |
unsigned long s_desc_per_block |
一個塊存放組描述符的的數量 |
unsigned long s_groups_count |
組描述符的數量 |
struct buffer_head * s_sbh |
指向存放原始超級塊的快取 |
struct ext2_super_block * s_es |
指向 s_sbh中的超級塊結構 |
struct buffer_head ** s_group_desc |
讀取超級塊的時候也會將組描述符讀入記憶體 |
int s_first_ino |
檔案系統中第一個非保留的inode號 |
struct rb_root s_rsv_window_root |
預留視窗的紅黑樹 |
struct ext2_reserve_window_node s_rsv_window_head |
紅黑樹的第一個節點 |
…… |
…… |
ext2_super_block儲存在磁碟上的原始超級塊
struct ext2_super_block |
|
__le32 s_inodes_count |
檔案系統中Inode的數量 |
__le32 s_blocks_count |
檔案系統中塊數 |
__le32 s_r_blocks_count |
保留的塊數 |
__le32 s_free_blocks_count |
空閒的塊數 |
__le32 s_free_inodes_count |
空閒的inode數 |
__le32 s_first_data_block |
第一個資料塊號 |
__le32 s_log_block_size |
塊大小 |
__le32 s_blocks_per_group |
每個塊組的塊數 |
__le32 s_inodes_per_group |
每個塊組的inode數 |
__le32 s_first_ino |
第一個沒有保留的inode |
__le16 s_inode_size |
Inode結構體的大小 |
__le16 s_block_group_nr |
當前 ext2_super_block所在塊組編號,超級塊在磁碟中每個塊組中都有備份 |
…… |
…… |
結構體ext2_inode_info連結VFS inode和原始inode
struct ext2_inode_info |
|
__le32 i_data[15]; |
Inode的直接塊 |
__u32 i_block_group; |
Inode所屬的塊組 |
struct inode vfs_inode; |
VFS inode |
…… |
…… |
結構體ext2_inode是存放於磁碟上的原始inode
struct ext2_inode |
|
__le16 i_mode |
檔案模式, |
__le32 i_size |
檔案大小(bytes) |
__le32 i_blocks |
檔案大小(塊) |
__le32 i_block[EXT2_N_BLOCKS] |
直接索引塊 |
…… |
…… |
結構體ext2_dir_entry_2也是磁碟上的一個結構,它表示目錄下面的一個目錄項。也就是目錄的內容(子目錄或者檔案),目錄也有一個inode,它也有資料塊,其資料塊上的存放的每一項都是用ext2_dir_entry_2來表示,例如:
[email protected]:~/workspace/kernel_4.12/linux-4.12.3/mm$ ls ~/workspace/
1496324869gf_common.h androidJ6 aosp dumpe2fs.txt gf_common.h kernel_4.12 log readme
struct ext2_dir_entry_2 |
|
__le32 inode |
這個目錄項對應的inode編號 |
__le16 rec_len |
rec_len欄位的末尾到下一個 rec_len的偏移,方便在資料塊上查詢下一個目錄項 |
__u8 name_len |
目錄項名的長度 |
__u8 file_type |
檔案型別,目錄、普通檔案、管道、連結等等 |
char name[] |
目錄項名字 |
…… |
…… |
struct ext2_group_desc |
|
__le32 bg_block_bitmap |
資料塊點陣圖的塊號 |
__le32 bg_inode_bitmap |
Inode點陣圖的塊號 |
__le32 bg_inode_table |
Inode表的塊號 |
__le16 bg_free_blocks_count |
塊組中空閒塊的數量 |
__le16 bg_free_inodes_count |
塊組中空閒inode的數量 |
__le16 bg_used_dirs_count |
塊組中目錄的數量 |
…… |
…… |
用命令dumpe2fs可以dump出文件系統的資訊:
[email protected]:~/workspace/kernel_4.12/linux-4.12.3/fs$ sudo dumpe2fs -h /dev/sda1
dumpe2fs 1.43.3 (04-Sep-2016)
Filesystem volume name: <none>
Last mounted on: /
Filesystem UUID: 22af4caf-a05b-4d8f-8004-30d531867b55
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 16252928
Block count: 65011456
Reserved block count: 3250572
Free blocks: 23809796
Free inodes: 14213383
First block: 0
Block size: 4096
Fragment size: 4096
Group descriptor size: 64
Reserved GDT blocks: 1024
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8192
Inode blocks per group: 512
Flex block group size: 16
Filesystem created: Fri Feb 17 21:00:51 2017
Last mount time: Sat Aug 12 10:26:31 2017
Last write time: Sat Aug 12 10:26:26 2017
Mount count: 32
塊快取
在深入ext2實現邏輯之前我們先插一節塊快取,下面先看快快取結構:
struct buffer_head |
|
unsigned long b_state |
快取狀態點陣圖,例如 BH_Mapped關聯到磁碟塊; BH_Dirty:髒塊; BH_Uptodate:塊中資料可用等 |
struct buffer_head *b_this_page |
緩衝區環形連結串列 |
struct page *b_page |
緩衝區對映到的頁 |
sector_t b_blocknr |
對應到磁碟上的塊號 |
size_t b_size |
快取大小 |
char *b_data |
快取起始地址 |
struct block_device *b_bdev |
塊裝置,指定了資料的來源 |
…… |
…… |
塊快取主要用在兩個地方,頁快取和塊裝置原始資料讀取(獨立塊快取),例如超級塊,組描述符塊等等。在頁快取中塊快取依附於頁,頁釋放之後塊快取就釋放。獨立塊快取由一個lru快取來管理,這個時候頁依附於塊快取,塊快取釋放頁就釋放。
struct bh_lru {
struct buffer_head *bhs[BH_LRU_SIZE];
};
這兩種塊快取都是用下面函式建立,他們的不同在於管理的視角不同。
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
int retry)
EXT2檔案系統掛載
3.1 註冊ext2檔案系統型別
靜態定義EXT2檔案系統型別ext2_fs_type,並通過register_filesystem將其新增到全域性連結串列file_systems上
static struct file_system_type ext2_fs_type = {
.owner = THIS_MODULE,
.name = "ext2",
.mount = ext2_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext2");
static int __init init_ext2_fs(void)
{
int err;
err = init_inodecache();
if (err)
return err;
err = register_filesystem(&ext2_fs_type);//將ext2_fs_type掛到全域性連結串列file_systems上
if (err)
goto out;
return 0;
out:
destroy_inodecache();
return err;
}
3.2 ext2檔案系統掛載
blkdev_get_by_path:根據dev_name從bdev檔案系統中獲取塊裝置對應的block_device
sget:分配super_block並且將super_block新增到全域性連結串列super_blocks和file_system_type ->fs_supers
fill_super:函式指標,這裡指向ext2_fill_super,用於從檔案系統中讀取super_block,下面細講:
static int ext2_fill_super(struct super_block *sb, void *data, int silent)
{
struct buffer_head * bh;
struct ext2_sb_info * sbi;
struct ext2_super_block * es;
struct inode *root;
unsigned long sb_block = get_sb_block(&data);
unsigned long logic_sb_block;
unsigned long offset = 0;
int blocksize = BLOCK_SIZE;
int db_count;
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); //分配ext2_sb_info結構
if (!sbi)
goto failed;
sb->s_fs_info = sbi; //VFS中的super_block通過sb->s_fs_info與ext2_sb_info相連線
sbi->s_sb_block = sb_block;
blocksize = sb_min_blocksize(sb, BLOCK_SIZE);
......
if (!(bh = sb_bread(sb, logic_sb_block))) { //從磁碟中讀取原始的超級塊結構ext2_super_block
ext2_msg(sb, KERN_ERR, "error: unable to read superblock");
goto failed_sbi;
}
es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
sbi->s_es = es;
......
sb->s_magic = le16_to_cpu(es->s_magic);
blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
/*如果超級塊的實際塊大小與假設的大小不一致就重新讀取超級塊,因為超級塊佔用一個塊大小,函式sb_bread也是從指定塊號讀取一個塊大小,如果實際塊與假設的塊大小不一致就重新讀取一個準確的塊
大小*/
if (sb->s_blocksize != blocksize) {
brelse(bh);
if (!sb_set_blocksize(sb, blocksize)) {
ext2_msg(sb, KERN_ERR,
"error: bad blocksize %d", blocksize);
goto failed_sbi;
}
logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
offset = (sb_block*BLOCK_SIZE) % blocksize;
bh = sb_bread(sb, logic_sb_block);
es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
sbi->s_es = es;
}
......
sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size;
sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb);
sbi->s_itb_per_group = sbi->s_inodes_per_group /
sbi->s_inodes_per_block;
sbi->s_desc_per_block = sb->s_blocksize /
sizeof (struct ext2_group_desc);
sbi->s_sbh = bh; //讓s_sbh指向原始超級塊資料
sbi->s_mount_state = le16_to_cpu(es->s_state);
sbi->s_addr_per_block_bits =
ilog2 (EXT2_ADDR_PER_BLOCK(sb));
sbi->s_desc_per_block_bits =
ilog2 (EXT2_DESC_PER_BLOCK(sb));
......
sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) -
le32_to_cpu(es->s_first_data_block) - 1)
/ EXT2_BLOCKS_PER_GROUP(sb)) + 1;
db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
EXT2_DESC_PER_BLOCK(sb);
sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
......
for (i = 0; i < db_count; i++) { //讀出所有組描述符
block = descriptor_loc(sb, logic_sb_block, i);
sbi->s_group_desc[i] = sb_bread(sb, block);
if (!sbi->s_group_desc[i]) {
for (j = 0; j < i; j++)
brelse (sbi->s_group_desc[j]);
ext2_msg(sb, KERN_ERR,
"error: unable to read group descriptors");
goto failed_mount_group_desc;
}
}
sbi->s_gdb_count = db_count; //設定組描述符所佔用的塊數
......
/*初始化預分配視窗*/
sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
sbi->s_rsv_window_head.rsv_alloc_hit = 0;
sbi->s_rsv_window_head.rsv_goal_size = 0;
ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
......
sb->s_op = &ext2_sops; //設定super_operations
......
root = ext2_iget(sb, EXT2_ROOT_INO);
if (IS_ERR(root)) {
ret = PTR_ERR(root);
goto failed_mount3;
}
sb->s_root = d_make_root(root); //建立根目錄的dentry
......
ext2_write_super(sb);
......
3.3檔案系統操作
例如:read--->vfs_read--->__vfs_read---> ext2_file_read_iter("file->f_op->read_iter")---> generic_file_read_iter---> do_generic_file_read---> ext2_readpage("mapping->a_ops->readpage")
inode包含了檔案操作的全部資訊,檔案開啟時候的file結構初始化資訊頁都是來源於inode,下面是inode建立時的主要邏輯:
struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{
struct ext2_inode_info *ei;
struct buffer_head * bh;
struct ext2_inode *raw_inode;
struct inode *inode;
inode = iget_locked(sb, ino); //建立VFS inode和ext2_inode_info
ei = EXT2_I(inode);
ei->i_block_alloc_info = NULL;
raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); //到inode塊表中去讀取原始inode
......
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext2_file_inode_operations;
if (test_opt(inode->i_sb, NOBH)) {
inode->i_mapping->a_ops = &ext2_nobh_aops;
inode->i_fop = &ext2_file_operations;
} else {
inode->i_mapping->a_ops = &ext2_aops; //頁快取操作函式集
inode->i_fop = &ext2_file_operations; //設定file_operations
}
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &ext2_dir_inode_operations;
......
}
檔案資料讀取
具體程式碼實現如下 :
int mpage_readpages(struct address_space *mapping, struct list_head *pages,
unsigned nr_pages, get_block_t get_block)
{
……
for (page_idx = 0; page_idx < nr_pages; page_idx++) {
struct page *page = lru_to_page(pages);
prefetchw(&page->flags);
list_del(&page->lru);
if (!add_to_page_cache_lru(page, mapping, page->index, gfp)) { //迴圈對映多個頁到檔案系統資料塊
bio = do_mpage_readpage(bio, page,
nr_pages - page_idx,
&last_block_in_bio, &map_bh,
&first_logical_block,
get_block, gfp);
}
put_page(page);
}
if (bio)
mpage_bio_submit(REQ_OP_READ, 0, bio); //提交資料讀請求給塊裝置
return 0;
}
檔案資料塊分散在磁碟上,要對資料進行讀寫操作就先要找到檔案資料塊的塊號,函式do_mpage_readpage的工作就是根據檔案資料位置偏移找到對應的資料塊塊號。map_bh用於讀取inode的對映塊。
static struct bio *
do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages,
sector_t *last_block_in_bio, struct buffer_head *map_bh,
unsigned long *first_logical_block, get_block_t get_block,
gfp_t gfp)
{
struct inode *inode = page->mapping->host;
const unsigned blkbits = inode->i_blkbits;
const unsigned blocks_per_page = PAGE_SIZE >> blkbits;
const unsigned blocksize = 1 << blkbits;
unsigned first_hole = blocks_per_page;
......
block_in_file = (sector_t)page->index << (PAGE_SHIFT - blkbits);
last_block = block_in_file + nr_pages * blocks_per_page;
last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits;
if (last_block > last_block_in_file)
last_block = last_block_in_file;
page_block = 0;
nblocks = map_bh->b_size >> blkbits;
if (buffer_mapped(map_bh) && block_in_file > *first_logical_block &&
block_in_file < (*first_logical_block + nblocks)) { //如果前一次迴圈已經讀取了對映塊,就在其中查詢對映關係
unsigned map_offset = block_in_file - *first_logical_block;
unsigned last = nblocks - map_offset;
for (relative_block = 0; ; relative_block++) {
if (relative_block == last) {
clear_buffer_mapped(map_bh);
break;
}
if (page_block == blocks_per_page)
break;
blocks[page_block] = map_bh->b_blocknr + map_offset +
relative_block;
page_block++;
block_in_file++;
}
bdev = map_bh->b_bdev;
}
map_bh->b_page = page;
while (page_block < blocks_per_page) { //呼叫函式get_block讀取對映塊,這個函式後面詳解
map_bh->b_state = 0;
map_bh->b_size = 0;
if (block_in_file < last_block) {
map_bh->b_size = (last_block-block_in_file) << blkbits;
if (get_block(inode, block_in_file, map_bh, 0))
goto confused;
*first_logical_block = block_in_file;
}
......
nblocks = map_bh->b_size >> blkbits;
for (relative_block = 0; ; relative_block++) {
if (relative_block == nblocks) {
clear_buffer_mapped(map_bh);
break;
} else if (page_block == blocks_per_page)
break;
blocks[page_block] = map_bh->b_blocknr+relative_block;
page_block++;
block_in_file++;
}
bdev = map_bh->b_bdev;
}
......
alloc_new:
if (bio == NULL) {
if (first_hole == blocks_per_page) {
if (!bdev_read_page(bdev, blocks[0] << (blkbits - 9),
page))
goto out;
}
bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
min_t(int, nr_pages, BIO_MAX_PAGES), gfp); //根據前面找到的資料塊編號初始化bio
if (bio == NULL)
goto confused;
}
length = first_hole << blkbits;
if (bio_add_page(bio, page, length, 0) < length) { //將記憶體頁新增到bio中
bio = mpage_bio_submit(REQ_OP_READ, 0, bio);
goto alloc_new;
}
......
out:
return bio;
......
塊對映
前面提到的函式指標get_block指向函式ext2_get_block,其實現邏輯如下:
ext2_block_to_path: 找到檔案偏移位置在對映塊中的位置 ext2_get_branch :檢查將要讀寫的所有資料塊是否都有對映(即在對映塊中是否都有值) 如果不是所有資料塊都有對映就繼續呼叫下面幾個函式分配資料塊: ext2_find_goal:返回查詢空閒塊的起始位置 ext2_blks_to_allocate:計算將要分配的塊數包含可能需要的間接塊 ext2_alloc_branch:分配前面計算的塊數,然後對映相關的資料塊(即將塊號寫入對映塊) 塊分配 塊分配是由函式ext2_new_blocks來實現的,該函式中包含一個叫做預分配的邏輯,在講解這個函式之前我們先認識下預分配相關的資料結構:
struct ext2_sb_info {
.......
spinlock_t s_rsv_window_lock;
struct rb_root s_rsv_window_root; //預分配視窗的紅黑樹
struct ext2_reserve_window_node s_rsv_window_head;//紅黑樹的根節點,空視窗
.......
};
struct ext2_inode_info {
......
struct ext2_block_alloc_info *i_block_alloc_info; //預分配資訊結構
......
};
struct ext2_block_alloc_info |
|
struct ext2_reserve_window_node rsv_window_node; |
預留視窗資訊 |
__u32 last_alloc_logical_block; |
上一次分配的邏輯塊,即相對檔案偏移的塊 |
ext2_fsblk_t last_alloc_physical_block; |
邏輯塊,即磁碟上的塊號 |
struct ext2_reserve_window_node |
|
struct rb_node rsv_node |
用於新增到ext2_sb_info的紅黑樹中 |
__u32 rsv_goal_size |
預分配的大小 |
struct ext2_reserve_window rsv_window |
struct ext2_reserve_window { ext2_fsblk_t _rsv_start; //預分配的起始位置 ext2_fsblk_t _rsv_end;//預分配的結束位置 }; |
inode預分配視窗的初始化:
void ext2_init_block_alloc_info(struct inode *inode)
{
struct ext2_inode_info *ei = EXT2_I(inode);
struct ext2_block_alloc_info *block_i;
struct super_block *sb = inode->i_sb;
block_i = kmalloc(sizeof(*block_i), GFP_NOFS);
if (block_i) {
struct ext2_reserve_window_node *rsv = &block_i->rsv_window_node;
rsv->rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
rsv->rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; //標識預分配視窗為空
if (!test_opt(sb, RESERVATION))
rsv->rsv_goal_size = 0;
else
rsv->rsv_goal_size = EXT2_DEFAULT_RESERVE_BLOCKS; //預設預分配視窗大小為8
rsv->rsv_alloc_hit = 0;
block_i->last_alloc_logical_block = 0;
block_i->last_alloc_physical_block = 0;
}
ei->i_block_alloc_info = block_i;
}
下面正式講解塊的分配:
ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal,
unsigned long *count, int *errp)
{
......
struct ext2_super_block *es;
struct ext2_sb_info *sbi;
struct ext2_reserve_window_node *my_rsv = NULL;
struct ext2_block_alloc_info *block_i;
unsigned short windowsz = 0;
unsigned long ngroups;
unsigned long num = *count;
sb = inode->i_sb;
sbi = EXT2_SB(sb);
block_i = EXT2_I(inode)->i_block_alloc_info;
if (block_i) {
windowsz = block_i->rsv_window_node.rsv_goal_size;
if (windowsz > 0)
my_rsv = &block_i->rsv_window_node;
}
group_no = (goal - le32_to_cpu(es->s_first_data_block)) /
EXT2_BLOCKS_PER_GROUP(sb); // 計算goal所在的塊組
goal_group = group_no;
retry_alloc:
gdp = ext2_get_group_desc(sb, group_no, &gdp_bh); //獲取組描述符
if (!gdp)
goto io_error;
free_blocks = le16_to_cpu(gdp->bg_free_blocks_count);
if (free_blocks > 0) {
grp_target_blk = ((goal - le32_to_cpu(es->s_first_data_block)) %
EXT2_BLOCKS_PER_GROUP(sb));
bitmap_bh = read_block_bitmap(sb, group_no); //讀取塊組資料塊點陣圖
if (!bitmap_bh)
goto io_error;
grp_alloc_blk = ext2_try_to_allocate_with_rsv(sb, group_no,
bitmap_bh, grp_target_blk,
my_rsv, &num); //分配資料塊,並實現資料塊預分配
if (grp_alloc_blk >= 0)
goto allocated;
}
ngroups = EXT2_SB(sb)->s_groups_count;
for (bgi = 0; bgi < ngroups; bgi++) { //如果在goal所在的塊組中沒有分配到就從第一個塊組開始嘗試分配
......
}
if (my_rsv) {
my_rsv = NULL;
windowsz = 0;
group_no = goal_group;
goto retry_alloc;
}
allocated:
ret_block = grp_alloc_blk + ext2_group_first_block_no(sb, group_no);
......
return ret_block; //返回分配到的塊組的塊號
......
下面函式是塊分配的核心函式,首先查詢一個可以容納預分配視窗大小的空閒空間,然後將資料塊點陣圖上對應的位置設定為1,表示已分配。
static ext2_grpblk_t
ext2_try_to_allocate_with_rsv(struct super_block *sb, unsigned int group,
struct buffer_head *bitmap_bh, ext2_grpblk_t grp_goal,
struct ext2_reserve_window_node * my_rsv,
unsigned long *count)
{
ext2_fsblk_t group_first_block, group_last_block;
ext2_grpblk_t ret = 0;
unsigned long num = *count;
if (my_rsv == NULL) { //直接分配資料塊不做預分配
return ext2_try_to_allocate(sb, group, bitmap_bh,
grp_goal, count, NULL);
}
group_first_block = ext2_group_first_block_no(sb, group);
group_last_block = group_first_block + (EXT2_BLOCKS_PER_GROUP(sb) - 1);
while (1) {
if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) ||
!goal_in_my_reservation(&my_rsv->rsv_window,
grp_goal, group, sb)) {//預分配視窗為空或者目標塊不在my_rsv中
if (my_rsv->rsv_goal_size < *count) // my_rsv->rsv_goal_size初始值為8
my_rsv->rsv_goal_size = *count;
ret = alloc_new_reservation(my_rsv, grp_goal, sb,
group, bitmap_bh); //查詢一個可以容納預分配視窗大小的空閒空間
if (ret < 0)
break; /* failed */
if (!goal_in_my_reservation(&my_rsv->rsv_window,
grp_goal, group, sb))
grp_goal = -1;
} else if (grp_goal >= 0) {
int curr = my_rsv->rsv_end -
(grp_goal + group_first_block) + 1;
if (curr < *count)
try_to_extend_reservation(my_rsv, sb,
*count - curr);
}
if ((my_rsv->rsv_start > group_last_block) ||
(my_rsv->rsv_end < group_first_block)) {
rsv_window_dump(&EXT2_SB(sb)->s_rsv_window_root, 1);
BUG();
}
ret = ext2_try_to_allocate(sb, group, bitmap_bh, grp_goal,
&num, &my_rsv->rsv_window);// 將預分配視窗中的資料塊在點陣圖上對應的位置設定為1
if (ret >= 0) {
my_rsv->rsv_alloc_hit += num; //統計預分配命中數
*count = num;
break; /* succeed */
}
num = *count; //返回分配到的塊數
}
return ret;
}
下面函式是預分配的核心:
static int alloc_new_reservation(struct ext2_reserve_window_node *my_rsv,
ext2_grpblk_t grp_goal, struct super_block *sb,
unsigned int group, struct buffer_head *bitmap_bh)
{
struct ext2_reserve_window_node *search_head;
ext2_fsblk_t group_first_block, group_end_block, start_block;
ext2_grpblk_t first_free_block;
struct rb_root *fs_rsv_root = &EXT2_SB(sb)->s_rsv_window_root;
unsigned long size;
int ret;
spinlock_t *rsv_lock = &EXT2_SB(sb)->s_rsv_window_lock;
group_first_block = ext2_group_first_block_no(sb, group);
group_end_block = group_first_block + (EXT2_BLOCKS_PER_GROUP(sb) - 1);
start_block = grp_goal + group_first_block; //搜尋空間區間的起始位置
size = my_rsv->rsv_goal_size;
search_head = search_reserve_window(fs_rsv_root, start_block); //查詢離start_block最近的預留視窗
retry:
ret = find_next_reservable_window(search_head, my_rsv, sb,
start_block, group_end_block); //以search_head為起點查詢一個可以容納my_rsv且不與其他預留視窗重疊的空閒區間
......
first_free_block = bitmap_search_next_usable_block(
my_rsv->rsv_start - group_first_block,
bitmap_bh, group_end_block - group_first_block + 1); //在位表中查詢塊組中rsv_start往後第一個空閒塊,因為不是所有塊分配都是通過預分配,所以有些塊可能已經分配了但是在預留視窗中找不到
......
start_block = first_free_block + group_first_block;
if (start_block >= my_rsv->rsv_start && start_block <= my_rsv->rsv_end)//空閒塊是否在my_rsv中
return 0; /* success */
search_head = my_rsv; //如果my_rsv區間中的塊都被分配出去了就以my_rsv為起點重新搜尋
goto retry;
}
相關推薦
EXT2檔案系統實現原理
目錄 二 塊快取 6 EXT2檔案系統結構概覽 1.1 EXT2檔案系統結構框圖 每一個檔案或者目錄在磁碟上都有一個inode用於管理檔案本身屬性資訊,還有資料塊用於存放檔案內容。其inode'和資料塊關係如下圖: 如果檔案比較小,其資料
簡訊系統實現原理(基於redis)
1:把需要傳送的資訊封裝成json 1.0:簡訊通道(驗證碼,短訊息,。。。) 1.0.0:把需要傳送的json放入redis對應的池子中(有個模板templet) 1.1:定時傳送 1.2:延遲傳送 2:簡訊系統開啟多執行緒,實時從redis中獲取key,發簡訊 2.0:獲
檔案系統實現
檔案系統實現 檔案系統結構 磁碟提供大量的外部空間來維持檔案系統。磁碟的下述兩個特點,使其成為儲存多個檔案的方便介質: 可以原地重寫,可以從磁碟上讀一塊,修改該快,並將它寫回到原來的位置。 可以直接訪問磁碟上的任意一塊資訊。因此,可以簡單地順序或隨機地
ext2檔案系統結構分析
轉載自:https://blog.csdn.net/yuzhihui_no1/article/details/50256713 ext2檔案系統 總體儲存佈局 我們知道,一個磁碟可以劃分成多個分割槽,每個分割槽必須先用格式化工具(例如某種mkfs命令)格式化成某種格式的檔案系統,然後才能
《18.根檔案系統的原理》
《18.根檔案系統的原理》 第一部分、章節目錄 2.18.1.根檔案系統概述 2.18.2.根檔案系統的形式 2.18.3.自己製作ext3格式的根檔案系統 2.18.4.nfs方式啟動自制簡易資料夾形式的rootfs 2.18.5.到底什麼是linuxrc 2.18.6.rootfs中
淺談ext2檔案系統
我們知道檔案系統是組織和管理磁碟上的檔案,並向用戶提供操作介面(open、read、write等),Unix中的每個物件幾乎都可以當做檔案來看待。核心在沒有結構的硬體上構造結構化的檔案系統,而檔案抽象在整個系統中廣泛使用。Linux支援多種檔案系統,如ext2,ext3,vfat等,ex
FastDFS分散式檔案系統 -- 工作原理
FastDFS介紹 FastDFS分散式檔案管理系統,是用 c 語言編寫的一款開源的分散式檔案系統。FastDFS 為網際網路量身定製, 充分考慮了冗餘備份、負載均衡、線性擴容等機制,並注重高可用、高效能等指標,使用 FastDFS 很容易搭建一套高效能的檔案伺服器叢集提供檔案上傳、
Linux磁碟管理——Ext2檔案系統
前言 通常而言,對於一塊新磁碟我們不是直接使用,而是先分割槽,分割槽完畢後格式化,格式化後OS才能使用這個檔案系統。分割槽可能會涉及到MBR和GPT問題。至於格式化和檔案系統又有什麼關係? 這裡的格式化指的是高階格式化,由於每種OS所設定的檔案屬性/許可權並不相同, 為了能夠操作這些檔案,就需要對parti
Linux磁碟管理——日誌檔案系統與資料一致性 Linux磁碟管理——Ext2檔案系統
參考:Linux磁碟管理——Ext2檔案系統 資料不一致 上圖是Ext2結構圖,其他FS結構類似。 一般來說,我們將 inode table 與 data block 稱為資料區;至於其他例如 superblock、 block bitmap 與 inode bitmap 等稱為 metadata
根檔案系統的原理
根檔案系統概述 1.為什麼需要根檔案系統 (1)init程序的應用程式在根檔案系統上 (2)根檔案系統提供了根目錄/ (3)核心啟動後的應用層配置(/etc)在根檔案系統上,可以這麼認為:發行版=核心+rootfs (4)shell命令程式在根檔案系統上,例如ls、
基於檔案系統實現可追加的資料集市
一 問題背景 絕大多數的應用系統中,一開始資料的儲存和計算基本都是由資料庫來完成的,同時服務於業務交易和報表查詢;不過在經過幾年資訊化建設和資料積累後,常常都會遇到資料庫壓力變大,從而導致效能瓶頸的問題。 究其原因,往往發現針對歷史資料查詢的報表在其中佔了很大比重。進
Linux檔案系統實現
作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段宣告。謝謝! Linux檔案管理從使用者的層面介紹了Linux管理檔案的方式。Linux有一個樹狀結構來組織檔案。樹的頂端為根目錄(/),節點為目錄,而末端的葉子為包含資料的檔案。當我們給出一個檔案的完
什麼是分散式檔案系統?分散式檔案系統的原理、出現的問題與解決方法
本地檔案系統如ext3,reiserfs等(這裡不討論基於記憶體的檔案系統),它們管理本地的磁碟儲存資源、提供檔案到儲存位置的對映,並抽象出一套檔案訪問介面供使用者使用。但隨著網際網路企業的高速發展,這些企業對資料儲存的要求越來越高,而且模式各異,如淘寶主站的大量商品圖片,其
作業系統概念(高等教育出版社,第七版)複習——第十一章:檔案系統實現
第十一章 檔案系統實現 基礎知識 檔案系統實現(超重點) 一個磁碟可分為多個分割槽,或者一個卷可橫跨多個磁碟上的數個分割槽。分割槽可以是“生的”,即沒有檔案系統,也可以是“熟的”,即含有檔案系統。 目
hadoop hdfs分散式檔案系統工作原理
非HA下的namenode 工作原理 nameNode的作用:1.管理元資料 2.維護目錄樹 3.響應客戶請求(主要就是記錄一些 真實資料存放在 被切割後 放在了 哪些機器上,等客戶端下載時 找到這些檔案塊 在合併返回給客戶端) 1.當客戶端沒請求一次時 nameN
檔案系統實現概念
檔案系統永遠在外存中,儲存大量資料。 檔案能夠原地改寫,即能夠讀出一塊,並修改,再寫回。 外存和記憶體的傳輸的最小單位為block。 磁碟是隨機儲存器。 檔案系統設計也是分層設計思想。從最底層開始講起。 (1)I/O控制層,包括裝置驅動程式(翻譯高層命令控制硬體控制器)+
Laravel使用自帶的檔案系統實現檔案上傳
這算是觀看慕課網是的視訊然後寫的一篇筆記 介紹:Laravel自己的檔案系統 Laravel的檔案系統是基於Frank de Jonge的Flysystem擴充套件包 提供了簡單的介面,可以操作本地端空間,Amazon S3 、Rackspace Cloud S
glusterfs分散式檔案系統詳細原理
1.Glusterfs簡介 GlusterFS是Scale-Out儲存解決方案Gluster的核心,它是一個開源的分散式檔案系統,具有強大的橫向擴充套件能力,通過擴充套件能夠支援數PB儲存容量和處理數千客戶端。GlusterFS藉助TCP/IP或Infi
EXT2檔案系統簡介
一、EXT2檔案系統檔案組織形式 EXT2檔案系統是Linux系統中廣泛使用的檔案系統,該檔案系統是一種索引式檔案系統,它將分割槽分為inode和block,它會給每個檔案分配一個inode,inod
Linux kernel FAT32檔案系統實現
1. FAT表操作 FAT檔案系統中,使用FAT表標記哪個cluster被佔用,哪個沒被佔用。在Linux核心程式碼中,與FAT表操作對應的是fat_entry,fatent_ops結構和fat_cache_id快取等。 1.1 fat_entry fat中的f