Linux kernel FAT32檔案系統實現
1. FAT表操作
FAT檔案系統中,使用FAT表標記哪個cluster被佔用,哪個沒被佔用。在Linux核心程式碼中,與FAT表操作對應的是fat_entry,fatent_ops結構和fat_cache_id快取等。
1.1 fat_entry
fat中的fat entry用於描述fat檔案系統的FAT分配表。
struct fat_entry {
int entry; // 代表當前的簇索引
union { // 簇索引表
u8 *ent12_p[2];
__le16 *ent16_p;
__le32 *ent32_p;
} u;
int nr_bhs; // buffer_head數目,可能是1也可能是2,FAT32是1
struct buffer_head *bhs[2]; // FAT表的扇區的buffer_head
struct inode *fat_inode; // 超級塊的inode
};
FAT檔案系統中對FAT12/16/32,分別實現了一個fatent_operations。fatent_operations的初始化在fat_ent_access_init函式,大意是根據超級塊裡的fat_bits判斷當前FAT型別,然後將對應的fatent_operations賦值到超級塊的fatent_ops中。
對於FAT32,超級塊的fatent_ops會指向fat32_ops;fatent_shift為2,代表一個簇的編號佔用4個位元組:
static struct fatent_operations fat32_ops = {
.ent_blocknr = fat_ent_blocknr,
.ent_set_ptr = fat32_ent_set_ptr,
.ent_bread = fat_ent_bread,
.ent_get = fat32_ent_get,
.ent_put = fat32_ent_put,
.ent_next = fat32_ent_next,
};
fat_ent_blocknr
根據entry編號,得到block號和在block裡的索引。entry為簇的索引,比如entry 5,對於FAT32它在FAT表的位置為5*4 + <FAT start>。
static void fat_ent_blocknr(struct super_block *sb, int entry, int *offset, sector_t *blocknr)
int bytes = (entry << sbi->fatent_shift); // FAT32中sbi->fatent_shift為2
*offset = bytes & (sb->s_blocksize - 1);
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
ent_set_ptr
設定fat_entry的u.ent32_p值。
static void fat32_ent_set_ptr(struct fat_entry *fatent, int offset)
fatent->u.ent32_p = (__le32 *)(fatent->bhs[0]->b_data + offset);
ent_bread
讀FAT分配表,讀到後儲存到fat_entry的bhs,並將其offset開始放到u.ent32_p。
static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent, int offset, sector_t blocknr)
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
fatent->bhs[0] = sb_bread(sb, blocknr); // 注意這裡bhs是個陣列,FAT32只用裡面第一個,所以下面的nr_bhs為1。
fatent->nr_bhs = 1;
ops->ent_set_ptr(fatent, offset);
ent_get
得到當前簇號
static int fat32_ent_get(struct fat_entry *fatent)
int next = le32_to_cpu(*fatent->u.ent32_p) & 0x0fffffff;
if (next >= BAD_FAT32)
next = FAT_ENT_EOF;
return next;
ent_put
將FAT表中某個簇的位置寫成new代表的值,即更新簇鏈,一般在釋放簇的時候會用到。
static void fat32_ent_put(struct fat_entry *fatent, int new)
if (new == FAT_ENT_EOF)
new = EOF_FAT32;
new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
*fatent->u.ent32_p = cpu_to_le32(new);
mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
ent_next
得到下一個簇,注意這裡是整個FAT表的下一個簇,不是某一個檔案的下一個簇。此函式在分配空閒簇、統計空閒簇是用於遍歷整個FAT表。
static int fat32_ent_next(struct fat_entry *fatent)
const struct buffer_head *bh = fatent->bhs[0];
fatent->entry++;
if (fatent->u.ent32_p < (__le32 *)(bh->b_data + (bh->b_size - 4))) {
fatent->u.ent32_p++;
return 1;
}
fatent->u.ent32_p = NULL;
1.2 fat_entry的對外介面
fat_get_cluster
fat_get_cluster根據cluster值得到該cluster在檔案中以及硬碟中的簇號。這裡的引數cluster是在檔案中的簇號,fclus是返回的在檔案中的簇號,dclus是在磁碟中的簇號。
int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
struct super_block *sb = inode->i_sb;
const int limit = sb->s_maxbytes >> MSDOS_SB(sb)->cluster_bits;
struct fat_entry fatent;
struct fat_cache_id cid;
*fclus = 0;
*dclus = MSDOS_I(inode)->i_start;
// 在cache中查詢與cluster最近的fclus和dclus
fat_cache_lookup(inode, cluster, &cid, fclus, dclus);
。。。。。。
fatent_init(&fatent); // 初始化一個fat_entry
while (*fclus < cluster) {
nr = fat_ent_read(inode, &fatent, *dclus);
。。。。。。
if (nr == FAT_ENT_EOF) {
fat_cache_add(inode, &cid);
goto out;
}
(*fclus)++;
*dclus = nr;
if (!cache_contiguous(&cid, *dclus))
cache_init(&cid, *fclus, *dclus);
}
// 加入快取
fat_cache_add(inode, &cid);
上面函式中,提到了fat_entry的快取,其實現程式碼在fs/fat/chche.c,作用為根據檔案中的簇號找到磁碟中的簇號。
另外,上文中呼叫了fat_ent_read函式,此函式執行一系列fatent_operations中的操作,例如讀fat表中某一個block,查詢該block中的cluster表索引,從而得到檔案簇對應的磁碟簇。注意這裡的讀block並不一定是真正去磁碟上讀,大多數情況下該block的內容會在block層的disk cache中得到。
fat_count_free_clusters
fat_count_free_clusters的作用是得到當前空閒的簇的總和,即磁碟上有多少剩餘空間。此函式實現很簡單,就是掃描FAT表,統計哪些簇是空閒的。掃描後的結果會儲存在超級塊的free_clusters中。
fat_alloc_clusters
為某個檔案(inode)分配新簇,這些新簇會連結到當前檔案簇鏈的末尾。
fat_ent_write
更新FAT表,這裡的更新包括兩部分,一部分為主FAT,另一部分為mirror FAT。
fat_free_clusters
釋放FAT表某個簇標記,更新包括兩部分,一部分為主FAT,另一部分為mirror FAT。
2. file_operations
FAT檔案系統使用的file_operations結構為fat_file_operations。
const struct file_operations fat_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read, // 通用讀函式,通過generic_file_aio_read實現
.write = do_sync_write, // 通用寫函式,通過generic_file_aio_write實現
.aio_read = generic_file_aio_read, // 通用實現,通過aops實現
.aio_write = generic_file_aio_write, // 通用實現,通過aops實現
.mmap = generic_file_mmap,
.release = fat_file_release,
.unlocked_ioctl = fat_generic_ioctl,
.fsync = fat_file_fsync,
.splice_read = generic_file_splice_read,
};
先看一下FAT的讀寫函式的實現。
讀操作
do_sync_read是kernel提供的通用同步讀函式,最終會呼叫到file_operations的非同步讀寫函式實現。
ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
。。。。。。
filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
wait_on_retry_sync_kiocb(&kiocb);
這裡aio_read為generic_file_aio_read。
ssize_t generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos)
if (filp->f_flags & O_DIRECT)
filemap_write_and_wait_range
mapping->a_ops->direct_IO
。。。。。。
do_generic_file_read
。。。。。。
do_generic_file_read中,會先在disk cache中查詢有沒有對應的disk cache,如果找到則直接返回,否則呼叫aops->read_page呼叫真正的讀操作,這裡aops會指向fat_aops。
寫操作
寫操作與讀操作類似:同步呼叫函式由非同步呼叫函式實現,非同步呼叫函式最終又是呼叫到aops-> write_begin和write_end。
seek
seek函式的實現使用的是generic_file_llseek,此函式中設定了file->f_pos。
mmap
mmap的實現使用的是generic_file_mmap
int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
struct address_space *mapping = file->f_mapping;
if (!mapping->a_ops->readpage)
return -ENOEXEC;
file_accessed(file);
vma->vm_ops = &generic_file_vm_ops;
vma->vm_flags |= VM_CAN_NONLINEAR;
return 0;
const struct vm_operations_struct generic_file_vm_ops = {
.fault = filemap_fault,
};
generic_file_vm_ops中實現了fault函式,fault會在處理缺頁異常時被呼叫。
int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
// 查詢對應頁是否在cache中
page = find_get_page(mapping, offset);
if (likely(page))
do_async_mmap_readahead(vma, ra, file, page, offset);
else
do_sync_mmap_readahead(vma, ra, file, offset);
page = find_lock_page(mapping, offset);
vmf->page = page; // 返回此page
3. address_space_operations
file_operations結構中提到了aops結構,此結構在FAT檔案系統中為fat_aops。
static const struct address_space_operations fat_aops = {
.readpage = fat_readpage,
.readpages = fat_readpages,
.writepage = fat_writepage,
.writepages = fat_writepages,
.sync_page = block_sync_page,
.write_begin = fat_write_begin,
.write_end = fat_write_end,
.direct_IO = fat_direct_IO,
.bmap = _fat_bmap
};
readpage
static int fat_readpage(struct file *file, struct page *page)
return mpage_readpage(page, fat_get_block);
int mpage_readpage(struct page *page, get_block_t get_block)
struct bio *bio = NULL;
sector_t last_block_in_bio = 0;
struct buffer_head map_bh;
unsigned long first_logical_block = 0;
map_bh.b_state = 0;
map_bh.b_size = 0;
bio = do_mpage_readpage(bio, page, 1, &last_block_in_bio,
&map_bh, &first_logical_block, get_block);
if (bio)
mpage_bio_submit(READ, bio);
---------------------
作者:walkingman321
來源:CSDN
原文:https://blog.csdn.net/walkingman321/article/details/7998780
版權宣告:本文為博主原創文章,轉載請附上博文連結!