SD卡读写流程

本文主要介绍从用户层读sd卡和写sd卡中间执行的过程。有对内核普遍性的介绍,和sd卡驱动个性的描述,强调把内核与驱动分开来看。同时提出内核需要驱动提供的参数,数据。

一 SD卡使用流程框图

说简单点:就是完成SD卡与内存之间的数据交互。但是涉及到了设备管理与文件管理。用户操作就是用户进程的read/write系统调用,应该说是 fread/fwrite,表示读某个文件,再不是读sd卡这个设备文件,也就是说你不需要在sd驱动中实现read/write函数,因为用不到啊。系 统调用后进入fat文件系统中的read/write,如果你问我为什么会进入fat中,因为sd卡上的文件组织使用的是fat32文件系统,并且在 mount的时候明确了是fat。这是与字符设备差异的地方。

 

VFS层>具体的文件系统层(fat32体现文件系统的差异)>cache层(这是内存的一部分,用于缓存磁盘文件,读文件的时候现在内存找,命中就不用再读了)>通用块设备层(制造bio,bio是一次请求的容器,包含了内存中为将要读取文件预留的空间(bio_vec),还有文件在磁盘上的扇区)>IO调度层(以前的磁盘文件随意访问影响速度,这是由硬件决定的,所以有了对请求的调度,如果2个磁盘文件在磁盘中的位置相近,就把请求调整到一起,现在的sd卡等就不需要了)>块设备驱动层(从请求队列中一个接着一个的取request,并且执行,使用dma搬运数据)

二 SD卡使用流程图

三 文件在内存中的缓存

从物理地址上讲,文件缓存是一直在内存中的(超级块信息缓存在内核数据区,节点位图,设备块位图保存在缓冲区(缓冲区是内核空间与用户空间中间的一部分内存),文件缓存在page cache中),不会因为进程切换而切换,就是说所有进程共享这部分内容。普通文件是共享的,可以操作,可执行文件是要拷贝到进程空间执行的。这样做的好处是不用每次都去外存中读取文件,提高了整体的性能。都是页操作的,不错页管理与页中的内容无关,缓冲区还是你按照块操作的,这个块是文件块的意思,一般是1K,大小与操作系统有关,块IO也就是这个意思。在用户空间,使用缺页机制,经常会按页操作,不会与块有关。就是说块机制用于与外存文件交互,页机制用于其他内存管理,当然了缓冲区也有页的概念,这就与线性地址寻址有关了。

四 准备SD卡,准备具体分析流程

    准备一个SD卡,在其内放置一个文件test.txt,内容是:this is just a test!!!文件大小22字节,占空间4KB。这是在SD卡中占的空间,称之为一簇,但是大家知道,在内存中是不会占这么大的,可能比22字节大一点,保证字节对齐。从SD卡中取数据必须以扇区为单位,就是512字节,那么系统在执行的时候是读了一个扇区,还是8个扇区呢?因为后面根本没有数据。这个问题后面会解答。

   这个是test文件的信息,使用winhex工具时可以查看的,有创建时间,文件大小,所在簇号等信息。这些信息很重要,比如说当你在SD卡目录,cd打开一个目录,看到很多文件名,那么就代表这些文件在内存中或者cache中吗,显然不是,但是关于这些文件的信息比如在内存中,当你打开某一个文件时,会用到这些信息,使用文件大小确定读得扇区数,通过扇区号,确定文件在SD卡中的位置,这样才能找到文件。那么这些文件信息一开始就在内存中吗,是的,在mount的时候,就读进来了。或许只是部分,但是通过目录是可以找到的。在图片中最后的16 00 00 00 ,是文件大小22字节,前面一点是03 00,是表示文件在第三簇,然后推算到扇区号是7528+4+4=7536.怎么找到的就不说了,我的另外一篇博客中有写。

这副图就是在在7536号扇区中显示的,是文件的内容。这次的测试就是把这些内容读到内存中去。详细说明其过程。

五 上代码

从readpage函数开始分析,因为第一次肯定不在cache中,需要从sd卡中读。也是产生bio结构体的最直接的函数。表示一次磁盘连续IO操作的结构体为bio,bio在通用块层制造,构造好之后传递给IO调度层,进而传递给设备驱动来处理,磁盘操作最小的单位是扇区,所以bio要以起始扇区号和长度来描述一次IO操作。

当访问的sd卡中两个文件连续的 时候,两个bio会合成一个request,一般一个bio就是一次request,有时候一个request也包含多个bio。表示请求一次文件操作,不过,当文刚说到了内存中不连续的块,如果一次get_block分配到的几个块是连续的,就表示为一个段,所以bio_vec用来表示几个连续的文件块就是段了,当然了,如果文件大于4K,就会有多个页,就不是图中仅有的8个数据块了(页是4K,块大小与操作系统有关,感觉还是1K的多)。我还有个问题:如果两个块属于不同的物理页,但是物理地址上是连续的,可以组成一个段吗?貌似是不可以的。

const struct file_operations fat_file_operations = {
   
    .llseek        = generic_file_llseek,
    .read        = do_sync_read,
    .write        = do_sync_write,
    .aio_read    = generic_file_aio_read,
    .aio_write    = generic_file_aio_write,
    .mmap        = generic_file_mmap,
    .release    = fat_file_release,
    .ioctl        = fat_generic_ioctl,
    .fsync        = file_fsync,
    .splice_read    = generic_file_splice_read,
};
sys_read操作先调用do_sync_read,然后在do_sync_read中调用generic_file_aio_read。
generic_file_aio_read中先到缓存中去找,找到就直接拷贝文件到用户空间,找不到就要到磁盘上去运输,会调用read_page函数
/* Start the actual read. The read will unlock the page. */
 
 
error = mapping->a_ops->readpage(filp, page);
接着会.readpage = fat_readpage,就是直接调用fat_readpage函数。
文件系统在操作设备的时候,有一个预读取的动作。一般我们需要的数据是通过预读取读进内存的,这个时候调用的就是fat_readpages操作函数。
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
 
 
};
static int fat_readpages(struct file *file, struct address_space *mapping,
                         struct list_head *pages, unsigned nr_pages)
//注意他的最后一个参数fat_get_block是一个
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值