文件读写的理论

文件读写的理论

大家好,又见面了,我是全栈君。

为了提高效率,略微复杂一些的操作系统对文件的读写都是带缓冲的,Linux当然也不例外。所谓缓冲,就是操作系统为近期刚读写的文件内容在内核保留一份副本,以便当再次须要已经缓冲存储在副本中的内容时就不必再暂时从设备上读入,而须要写的时候则能够先写到副本中,待系统较为空暇的时候再从副本写入设备。在多进程的系统中,因为同一个文件可能为多个进程所共享,缓冲的作用就更为显著。

然而,怎么样实现缓冲,在哪一个层次上实现缓冲,确实一个值得考虑的问题。

在文件层有三种基本的数据结构:file、dentry、inode。

先看file结构:前面讲过。一个file结构代表着目标文件的一个上下文,不但不同的进程能够在同一个文件上建立不同的上下文(每一个进程都有自己的file结构体),就是同一个进程也能够通过打开同一个文件多次而建立起多个上下文。假设在file结构中设置一个缓冲区队列,那么缓冲区中的内容尽管贴近这个特定上下文的使用者,却不便于为多个进程共享,甚至不便于同一个进程打开的不同上下文共享,这显然是不合适的。

那么dentry结构怎么样呢?这个数据结构并不属于某一个上下文,也不属于一个进程,能够为全部进程和上下文共享。但是dentry结构与目标文件并非一一相应的关系,通过文件链接,我们能够为已经存在的文件建立别名。一个dentry结构知识唯一的代表这文件系统中的一个节点,也就是一个路径名,但是多个节点能够同一时候代表同一个文件,所以还应该再抽象一次。

显然,在inode数据结构设置一个缓冲队列是最合适只是了,首先。inode结构与文件是一一相应的关系。即使一个文件有多个路径名。最后也归为同一个inode上。再说,一个文件里的内容是不能由其它文件共享的,在同一时间里,设备上的每个记录都仅仅能属于至多一个文件,将载有同一个文件内容的缓冲区都放在其所属文件的inode结构中是非常自然的事。因此在inode数据结构中设置了一个指针i_mapping,它就指向一个address_space数据结构。缓冲区队列就在这个数据结构中。

只是。挂在缓冲区队列中的并非记录块而是内存页面。也就说,文件的内容并非以记录块为单位。而是以页面为单位进行缓冲的。为什么这个搞?这是为了将文件内容的缓冲与文件的内存映射结合在一起。进程能够通过系统调用mmap()将一个文件映射到它的用户空间。建立了这种映射以后。就能够像訪问内存一样訪问这个文件。假设将文件的内容以页面为单位缓冲,放在附属于该文件的inode结构的缓冲队列中,那么仅仅要对应的设置进程的内存映射表。就能够非常自然地将这些缓冲页面映射到用户空间中。这样。在按常规方式文件操作訪问一个文件时,就能够通过read()和write()系统调用訪问目标文件的inode结构訪问这些缓冲页面;而通过内存映射机制訪问这个文件时。就能够经由页面映射直接訪问这些缓冲着的页面。当目标页面不在内存中时,常规的文件操作通过系统调用read()、write()的底层将其从设备读入。而通过内存映射机制訪问这个文件时,则由缺页异常的服务程序将目标页面从设备上读入。明确了这个背景,就明确上述的指针为什么叫i_mapping,它指向的数据结构为什么叫address_space就不会奇怪了。

但是,虽然以页面为单位的缓冲对于文件层确实是非常好的选择,对于设备层则不那么合适了。对设备层而言。最自然的当然是以记录块为单位的缓冲,由于设备的读写都是以记录块为单位的。

只是,从磁盘上读写基本的时间都花在准备工作上,一旦准备好了以后读一个记录块与接连读几个记录块相差并不大,并且每次仅仅读写一个记录块反而是不经济的。所以每次读写若干连续的记录块、以页面为单位缓冲并非问题。

还有一方面,假设以页面为单位缓冲。而一个页面相当于若干连续记录块,那么不管是对于缓冲页面还是对于记录块缓冲区,其控制信息显然应该游离于该页面之外,这些信息不应该映射到进程的用户空间。

这个问题不难解决。

在设备层中要保持一些buffer_head结构。让它们的b_data指针分别指向缓冲区页面中的对应位置就能够了。

以一个缓冲页面为例,在文件层它通过一个page数据结构挂入所属inode结构的缓冲页面队列。并且同一时候又能够通过各个进程的页面映射表映射到这些进程的内存空间。而在设备层又通过若干buffer_head结构挂入其所在设备的缓冲区队列。

文件读写的理论

文件读写的理论

在这样一个结构框架中,一旦所欲訪问的内容已经在缓冲页面队列中,读文件的效率就非常高了。仅仅要找到文件的inode结构就找到了缓冲页面的队列,从队列中找到对应的页面就能够读出了。


上面的设备缓冲区队列我们还没介绍是什么东西,以下来介绍

当一个块调入内存时,它要存储在一个缓冲区中。每一个缓冲区与一个块相应。它相当于是磁盘块在内存中的表示,磁盘块包括一个或者多个扇区,可是不能超过一个页面。所以一个物理页能够容纳一个或者多个内存中的块。因为内核在处理数据时须要知道一些相关信息(比方块属于哪一个块设备。块相应于哪个缓冲区等),所以每一个缓冲区都有一个相应的描写叙述符。该描写叙述符用buffer_head结构体表示。成为缓冲区头,在文件<linux/buffer_head.h>中定义,它包括了内核操作缓冲区的所有信息。

文件读写的理论

事实上这个buffer_head存在于linux2.4版本号中。在linux中使用bio结构体取代。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116504.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • a标签下划线

    a标签下划线页面中有一处box中的a标签都被加上了下划线,查找元素却没有找到css中的underline。原因是<a>标签默认是有下划线的,而一般看到的<a>标签链接中的下划线都被覆盖掉了,所以误以为<a>标签的默认状态是没有下划线的,出现下划线是有css另外渲染的。其实下划线才是<a>标签的默认状态在head中加上下面一段覆盖掉<…

    2022年5月2日
    156
  • 编程画爱心代码_程序员爱心代码

    编程画爱心代码_程序员爱心代码520情人节送到那个你所疼爱的人

    2022年10月13日
    7
  • 阶乘算法简单实现

    阶乘算法简单实现longintfac1(intn){ staticlongintf=1; f*=n; returnf;}longintfac2(intn){ longintf=1; if(n==0|n==1) {  returnf; } else {  f=n*fac2(n-1); }}voidmain(){ inti; for(i=1;i {  printf(“fac1[%d!]=%ld/n”,i,fac1(i)); } printf(“fac2[8!]=%ld/n”,fac2(8));

    2022年7月24日
    13
  • 简述物联网,云计算,大数据和人工智能的相互关系_物联网应用领域

    简述物联网,云计算,大数据和人工智能的相互关系_物联网应用领域我们在《互联网进化论》一书中提出“互联网的未来功能和结构将于人类大脑高度相似,也将具备互联网虚拟感觉,虚拟运动,虚拟中枢,虚拟记忆神经系统”,并绘制了一幅互联网虚拟大脑结构图。根据这一观点,我们尝试分析目前互联网最流行的四个概念————-大数据,云计算,物联网和移动互联网与传统互联网之间的关系。从这幅图中我们可以看出:物联网对应了互联网的感觉和运动神…

    2022年9月27日
    5
  • java中的Set集合

    java中的Set集合概述Set集合类似于一个罐子,程序可以依次把多个对象“丢进”Set集合,而Set集合通常不能记住元素的添加顺序。实际上Set就是Collection只是行为略有不同(Set不允许包含重复元素)。Set集合不允许包含相同的元素,如果试图把两个相同元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。HashSet类HashSet是Set接口的典型实现,…

    2022年4月29日
    52
  • 费曼学习法

    费曼学习法费曼学习法我的理解:费曼学习法就是把学好的东西用简洁易懂的语言,传授给别人举例:你学完微积分,然后自己去培训班,自己做老师,传授给学生们,并且学生们都能听懂费曼学习法的四个步骤:1.确定目标

    2022年7月29日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号