文本探讨了Linux kernel中对fat32文件系统的实现,关于fat文件的格式可以查看微软的fat白皮书。

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 + 。

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);

linux fat get entry,Linux kernel FAT32文件系统分析相关推荐

  1. 光盘隐藏文件夹 linux,linux常用命令大全2--挂载/dpkg/文件系统分析/apt/光盘/关机...

    挂载一个文件系统 mount /dev/hda2 /mnt/hda2 挂载一个叫做hda2的盘 - 确定目录 '/ mnt/hda2' 已经存在 umount /dev/hda2 卸载一个叫做hda2 ...

  2. fat32文件系统分析

    fat32文件系统结构: fat32文件系统比fat16文件系统少了根目录FDT,其实是将根目录归结到数据区中了. 注意数据区第一个扇区所在蔟为2号蔟. 首先在磁盘管理中创建一个fat32磁盘: 大小 ...

  3. linux中生成考核用的FAT32文件系统结构样例(一)

    实验FAT32-1说明:FAT32-1.img是一个包含FAT32文件系统的磁盘镜像,请使用winhex手工方式读出这个文件系统内的指定文件,并回答其md5 HASH值. 要求: 1.利用WINHEX ...

  4. 分析Linux 0.11中的kernel部分的makefile文件

    # # 在UltraEdit下注释 #  # # # if you want the ram-disk device, define this to be the # size in blocks. ...

  5. linux系统desktop entry 文件详解

    https://www.linuxidc.com/Linux/2011-08/40397.htm 1.Desktop Entry 文件标准简介 在 Windows 平台上,用户可以通过点击位于桌面或菜 ...

  6. linux格式化为fat,linux下把u盘格式化成 FAT32的例子

    u盘格式化我们在windows系统中非常的快速就可完成了但在linux中感觉并不那么容易了,我们来看一篇关于linux下把u盘格式化成 FAT32的教程吧. df 可以看到你的电脑中的硬盘 + U盘. ...

  7. Linux sysfs文件系统分析

    "sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel ...

  8. Linux 之旅 5:磁盘与文件系统管理

    Linux 之旅 5:磁盘与文件系统管理 Linux文件系统 文件系统特性 我们知道,对于一个新的存储设备,无论是移动硬盘还是U盘,在第一次连接电脑后一般都会提示要格式化后使用,那格式化是做什么用的呢 ...

  9. Linux文件系统(文件系统类型、设备文件、常用命令、U盘与光盘挂载)

    Linux文件系统(文件系统类型.设备文件.常用命令.U盘与光盘挂载)   本篇文章是Linux文件系统整块集合,包含了Linux文件系统介绍.设备文件介绍.常用文件系统命令(查看.修复与配置).挂载 ...

最新文章

  1. 利用CxImage实现编解码Gif图像代码举例
  2. pygame做的著名游戏_用python写游戏之2D跑酷游戏(一)
  3. 《LoadRunner 没有告诉你的》之三——理发店模型
  4. java 数值区间_java 各数据类型数值范围
  5. ASP.NET 2.0的编译模型
  6. oracle 存储过程
  7. ns-allinone错误锦囊
  8. 企业使用开源软件的风险
  9. python爬取网页上的特定链接_python 用bs4解析网页后,如何循环打开爬取出来的网址链接?...
  10. Linux 命令(48)—— stat 命令
  11. Prometheus监控学习笔记之PromQL简单示例
  12. matlab 韩明距离_科学网—Matlab中 pdist 函数详解(各种距离的生成) - 朱新宇的博文...
  13. 华为交换机配置时区_把华为交换机设置成时钟源服务器(NTP)
  14. 页面加载过程中触发的事件
  15. 谷歌联网断网都可以玩的恐龙小游戏(内容有不死加速挂)
  16. 计算机导论的答案,计算机导论答案
  17. 神探狄仁杰是一个Debug高手
  18. vue项目,解决ie缓存问题
  19. 快速上手Linux核心命令(九):文件备份与压缩
  20. 扑克洗牌(乱数排列)

热门文章

  1. 操作系统 -- 处理器管理
  2. adb remount失败的一种解决办法
  3. 一缕黑暗中的火光-----------协作图--------------优雅的建模语言
  4. 一缕黑暗中的火光-----------构件图--------------优雅的建模语言
  5. Centos 7 安装 docker
  6. python前端开发招聘_【天津前端开发招聘_最新天津前端开发招聘信息】-前程无忧...
  7. 互联网企业的项目管理者:免费的才是最贵的
  8. win7 关闭防火墙
  9. FlexRay总线原理及应用
  10. 一文看尽2020上半年阿里、腾讯、百度入选AI顶会论文