当前位置: 首页 > 后端技术 > Java

腾讯三面:写文件的过程中,如果进程崩溃了,文件数据会丢失吗?

时间:2023-04-01 22:43:24 Java

1。PageCache1.1什么是PageCache?为了理解PageCache,我们不妨看一下Linux的文件I/O系统,如下图所示:Linux文件I/O系统上图中红色部分为PageCache。可见PageCache的本质是一块由Linux内核管理的内存区域。当我们通过mmap和bufferedI/O将文件读入内存空间时,实际上是读入了PageCache。1.2如何查看系统的PageCache?通过读取/proc/meminfo文件,可以实时获取系统内存状态:$cat/proc/meminfo...Buffers:1224kBCached:111472kBSwapCached:36364kBActive:6224232kBInactive:979432kBActive(anon):6173036kBInactive(anon):927932kBActive(file):51196kBInactive(file):51500kB...Shmem:10000kB...SReclaimable:43532kB...根据上面的数据,可以简单的得到这个公式(等式两边都是112696KB):Buffers+Cached+SwapCached=Active(file)+Inactive(file)+Shmem+SwapCached等式两边都是PageCache,即:PageCache=Buffers+Cached+SwapCachedby阅读第1.4和1.5节,您可以理解为什么SwapCached和Buffers也是PageCache的一部分。1.3页和PageCache页是内存管理分配的基本单位,PageCache是??由多个页组成的。操作系统中page的大小通常为4KB(32bits/64bits),而PageCache的大小为4KB的整数倍。另一方面,并??非所有页面都组织为页面缓存。Linux系统上用户可访问的内存分为两种类型[2],即:File-backedpages:File-backedpages是PageCache中的页面,对应磁盘上的几个数据块;对于这些页面,最大的问题是脏页返回到磁盘;匿名页:匿名页不对应磁盘上的任何磁盘数据块,它们是进程运行的内存空间(如方法栈、局部变量表等);为什么linux不叫PageCacheBlockcache,这样不是更好吗?这是因为从磁盘加载到内存的数据不仅放在PageCache中,还放在buffercache中。例如,通过DirectI/O技术的磁盘文件不会进入PageCache。当然,这个问题也是Linux的历史设计造成的。毕竟这只是一个名字,随着Linux系统的演进,含义也逐渐发生了变化。我们来比较一下Swap机制下File-backedpages和Anonymouspages的性能。内存是一种宝贵的资源。当内存不够用时,MemoryMangamentUnit需要提供调度算法来回收相关的内存空间。回收内存空间的方式通常是swap,即swap到一个持久化的存储设备上。File-backedpages(PageCache)的内存回收成本很低。PageCache通常对应一个文件上的几个顺序块,因此可以通过顺序I/O刷新到磁盘。另一方面,如果没有对PageCache进行写操作(即所谓的nodirtypages),PageCache甚至不会归还磁盘,因为数据的内容可以通过读取磁盘文件来获取再次。PageCache的主要难点在于将脏页返回磁盘,这将在第二节中详细说明。匿名页面的内存回收成本很高。这是因为匿名页面通常随机写入持久交换设备。另一方面,为了保证数据不丢失,无论是否有写操作,Anonymouspages都必须在swap时持久化到磁盘。1.4Swap和pagefaultinterruptswap机制是指当物理内存不够用时,内存管理单元(MemoryMangamentUnit,MMU)需要提供一种调度算法来回收相关的内存空间,然后将清理出来的内存空间给与给当前的内存申请人。Swap机制存在的本质原因是Linux系统提供了虚拟内存管理机制,每个进程都考虑自己独享的内存空间,所以所有进程的内存空间之和远大于物理内存。所有进程的内存空间总和超过物理内存的部分需要交换到磁盘。操作系统以页面为单位管理内存。当进程发现它需要访问的数据不在内存中时,操作系统可能会将数据以页为单位加载到内存中。上述过程称为缺页中断。当操作系统发生页面错误时,会通过系统调用将页面重新读入内存。但是,主存储器的空间是有限的。当主内存中没有可用空间时,操作系统会选择合适的物理内存页并将其逐出回磁盘,为新的内存页腾出空间。选择要驱逐的页面的过程被操作系统称为页面替换(PageReplacement),替换操作会触发交换机制。如果物理内存足够大,可能不需要Swap机制,但是Swap在这种情况下还是有一定优势的:对于有可能出现内存泄漏的应用程序(进程)来说,Swap交换分区比较重要,可以保证内存泄漏不会导致物理内存不足,最终导致系统崩溃。但是,内存泄漏会造成频繁的swap,极大地影响操作系统的性能。Linux通过一个swappiness参数来控制Swap机制[2]:这个参数的值可以是0-100,控制系统swap的优先级:高值:更高频率的swap,在进程执行时主动将其转出物理内存不活跃。低值:较低的交换频率,可以保证交互不会因为内存空间频繁交换到磁盘而增加响应延迟。最后,为什么Buffer也是PageCache的一部分?这是因为当匿名页面(Inactive(anon)和Active(anon))先被交换(swapout)到磁盘,然后再加载回(swapin)到内存中时,读入后的原始SwapFilememory还在,所以SwapCached也可以认为是一个File-backedpage,属于PageCache。这个过程如图2所示。图2。匿名页面交换后,也是PageCache1.5PageCache和buffercache执行free命令,注意会有buffers和cached两列,还有一行名为“-/+buffers/cache”。~free-mtotalusedfreesharedbufferscachedMem:12895696440325150536839900-/+buffers/cache:5117277784Swap:16002016001其中cached列表示当前页面缓存(PageCache)占用情况,buffers列表示当前块缓存(buffercache)的占用情况。一句话解释:PageCache用于缓存文件的页数据,buffercache用于缓存块设备(如磁盘)的块数据。Page是一个逻辑概念,因此PageCache与文件系统处于同一级别;block是一个物理概念,所以buffercache和blockdevicedriver处于同一层级。其中,cached列表示当前页缓存(PageCache)占用情况,buffers列表示当前块缓存(buffercache)占用情况。一句话解释:PageCache用于缓存文件的页数据,buffercache用于缓存块设备(如磁盘)的块数据。Page是一个逻辑概念,因此PageCache与文件系统处于同一级别;block是一个物理概念,所以buffercache和blockdevicedriver处于同一层级。PageCache和buffercache的共同目的是加速数据I/O:写入数据时,先写入缓存,将写入的页面标记为脏,然后flush到外部存储,也就是write-backin缓存写机制(另一种是write-through,linux默认不使用);读取数据时,先读取缓存,如果未命中,则从外部存储中读取,并将读取到的数据添加到缓存中。操作系统总是主动使用所有空闲内存作为PageCache和buffercache,并且在内存不够用的时候也会使用LRU等算法淘汰缓存页面。在Linux2.4内核之前,PageCache和buffercache是??完全分开的。但是,大部分的块设备都是磁盘,磁盘上的大部分数据都是通过文件系统来组织的。这种设计导致很多数据被缓存了两次,浪费了内存。所以在2.4版本的内核之后,这两种缓存近似融合在一起了:如果一个文件的一页被加载到PageCache中,那么同时buffercache只需要维护block指向的指针页。只有那些没有文件表示的块,或者绕过文件系统直接操作(比如dd命令)的块,才会真正被放到buffercache中。因此,我们现在提到PageCache,基本上是同时指代PageCache和buffercache。这篇文章之后我们就不再区分它们了,直接统称为PageCache。下图大致展示了一个在32位Linux系统中可能的PageCache结构,其中blocksize为1KB,pagesize为4KB。PageCache中的每个文件都是一棵基数树(radixtree,本质上是多叉搜索树),树的每个节点都是一个页面。根据文件中的偏移量,可以快速定位到页面,如下图所示。radixtree的原理可以参考英文wiki,这里不再赘述。1.6PageCache和预读操作系统为基于PageCache的读缓存机制提供了预读机制(PAGE_READAHEAD)。一个例子是:用户线程只请求读取文件A在磁盘上的偏移量在0-3KB范围内的数据。磁盘的基本读写单位是块(4KB),所以操作系统至少会读取0-4KB的内容,可以放在一个页面中。但由于局部性原则[3],操作系统会选择将磁盘块偏移量[4KB,8KB)、[8KB,12KB)和[12KB,16KB)加载到内存中[3],因此申请了3个附加页在记忆中;下图表示操作系统的预读机制:图。操作系统的预读机制;上图中,应用程序使用read系统调用读取了4KB的数据。实际上,内核是利用预读机制来完成16KB数据的读取。2.PageCache和文件持久化的一致性和可靠性现代Linux的PageCache,顾名思义,就是对磁盘上的页(pages)进行内存缓存,可以同时进行读/写操作。任何引入缓存的系统都会导致一致性问题:内存中的数据与磁盘上的数据不一致。比如常见的后端架构中Redis缓存和MySQL数据库存在一致性问题。Linux提供了多种机制来保证数据的一致性,但是无论是单机上内存和磁盘的一致性,还是分布式组件中节点1、节点2、节点3的数据一致性,理解的关键在于交易-off:吞吐量和数据一致性保证是一对矛盾。首先,我们需要了解文件的数据。文件=数据+元数据。元数据用于描述文件的各种属性,也必须存储在磁盘上。所以,我们说保证文件一致性其实包括两个方面:数据一致性+元数据一致性。文件的元数据包括文件大小、创建时间、访问时间、所有者和组等信息。我们考虑以下一致性问题:如果发生写操作,对应的数据在PageCache中,那么写操作会直接作用于PageCache。这个时候如果数据还没有刷到磁盘,那么内存中的数据就先于Disk,此时对应的page称为Dirtypage。目前Linux下文件一致性的实现有两种方式:WriteThrough:为用户层提供特定的接口,应用程序可以主动调用该接口来保证文件的一致性;回写(writeback):系统中存在周期性任务(表现为内核线程),周期性同步文件系统中的脏数据块,这是Linux默认的一致性方案;以上两个方法最终都依赖于系统调用,主要分为以下三个系统调用:方法含义fsync(intfd)fsync(fd):将fd所代表的文件的所有脏数据和脏元数据刷入磁盘。fdatasync(intfd)fdatasync(fd):将fd所代表的文件的脏数据刷新到磁盘,同时将必要的元数据刷新到磁盘。这里说的必要概念是指:对接下来访问文件信息起到关键作用,比如文件大小和文件修改时间等都不是必要信息sync()sync():刷新所有脏文件数据和元数据在系统到磁盘。以上三个系统调用可以分别由用户进程和内核进程发起执行。下面我们来研究一下内核线程的相关特性。为回写任务创建的内核线程数由系统中的持久化存储设备决定,并为每个存储设备创建一个单独的刷新线程;对于多线程的架构,Linux内核采用了Lighthttp的方式,即在系统线程中有一个管理,多个flush线程(每个持久化存储设备一个)。管理线程监视设备上的脏页。如果设备在一段时间内没有产生脏页,设备上的刷新线程就会被销毁;刷新线程处理脏页写回。刷新线程的任务比较单调,它只负责将设备中的脏页回写到持久化存储设备中。刷新线程刷新设备上的脏页。总体设计如下:每个设备保存一个脏文件链表,其中保存了设备上存放的脏文件的inode节点。所谓回写文件脏页,是指回写inode链表上某些文件的脏页;系统中有多种回写机会,第一种是应用程序主动调用回写接口(fsync、fdatasync和sync等),第二种是管理线程周期性的唤醒回写线程设备回写。三是一些应用程序/内核任务在发现内存不足时需要回收一些缓存的页面,提前进行脏页回写。设计一个统一的框架来管理这些回写任务是很有必要的。直写和回写在持久化的可靠性上有所不同:直写以牺牲系统I/O吞吐量为代价,保证一旦写入,数据已经写入上层应用,不会丢失;writebackin在系统宕机的情况下,没有办法保证数据已经放在磁盘上,所以存在数据丢失的问题。但是,当程序挂掉时,比如被killed-9,PageCache中的数据操作系统仍然会保证放在磁盘上;3.PageCache的优缺点3.1PageCache的优点1.加快数据访问速度如果数据可以存放在内存Cache中,那么下次访问就不需要经过磁盘I/O,直接命中内存缓存即可直接地。因为内存访问比磁盘访问快得多,所以加快数据访问是PageCache的一大优势。2.减少I/O次数,提高系统盘I/O吞吐量得益于PageCache的缓存和预读能力,而程序往往符合局部性原则,因此多个页面通过加载到Page中一个I/OCache可以减少磁盘I/O次数,从而提高系统磁盘I/O吞吐量。3.2PageCache的缺点PageCache也有它的缺点。最直接的缺点就是需要占用额外的物理内存空间。当物理内存比较紧张时,可能会导致频繁的swap操作,最终导致系统磁盘I/O负载的增加。PageCache的另一个缺陷是它没有为应用层提供良好的管理API,几乎是透明的管理。即使应用层想要优化PageCache的使用策略,也很难进行。因此,一些应用程序选择在用户空间实现自己的页面管理,而不是使用页面缓存。例如,MySQLInnoDB存储引擎使用16KB页面进行管理。PageCache的最后一个缺陷是在某些应用场景下,比DirectI/O多了一次磁盘读I/O和磁盘写I/O。这一点可以参考[4]。来源:https://spongecaptain.cool/Si...近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!