1. 程式人生 > >Linux kernel FAT32檔案系統實現

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 
版權宣告:本文為博主原創文章,轉載請附上博文連結!