2014年1月

Linux read函数浅析-高速缓存

最近一直在看Linux下read()函数的实现,发现确实是复杂,几次想写篇博客来把read()函数讲清楚,可是写着写着就断了思绪,有些地方不是很懂,有些概念太过于模糊。今天还是决定写下,只能大概讲讲read()函数。

高速缓存


首先引入的就是高速缓存,什么是高速缓存?众所周知,磁盘的I/O和内存的I/O两者访问是相差几个数量级的,A进程访问了X文件,Linux为了加速A进程或者其他进程再次访问X文件的时候不再千里迢迢到去磁盘上寻找,于是就设计了高速缓存,把A进程访问过的X文件缓存在内存里面。这么说高速缓存就是内存了?对,高速缓存就是属于物理内存的一部分!Linux设计思想就是尽可能地发挥物理内存的使用。

Linux读写机制


好,我们看看高速缓存怎么发挥作用,A进程要去磁盘上访问磁盘上的X文件,首先会在高速缓存区里面找,找到了就直接返回对应的内容,找不到呢?没办法,得再去磁盘上找,通过设备驱动程序把相应的文件内容读取到高速缓存里,然后A进程从高速缓存里得到数据,返回给用户空间。那么某个进程再去请求X文件的时候就会直接从高速缓存区里返回了,效率就会大大提高。
那么对于写文件呢?A进程要写磁盘文件,那么先把数据写到高速缓存区里面,并不是写了小部分数据就会立马同步到磁盘上的。内核会有一组回写线程,一段时间去把高速缓存区上“脏”的逻辑块写到磁盘上,这里“脏”的意思和虚拟内存页差不多,最简单定义就是内存中某个块和磁盘上对应块不一样就回写(不是页)。

高速缓存实现


对于块设备而言,比如磁盘,最小的访问单位都是扇区,一般一个扇区是512字节,Linux抽象了一层块的概念,平时说的I/O块和逻辑块都是这个意思,一个块必然是扇区的整数倍,一般都是1K。在内核0.11里面,高速缓存区都是这些块组成的,统一用buffer_head进行描述,所有的块都组成双向链表。每个设备号+逻辑块号还通过指定的HASH算法构成自己的链表,访问X文件会在inode节点里提供设备号,接下来需要的是逻辑块号的获取是通过提供的文件偏移地址算出来的,就可以在对应的设备号+逻辑块号HASH队列里找到相应缓存块,找不到就向底层的驱动程序发出读磁盘请求。块的概念都是面向设备的,也就是驱动程序和设备打交道的最基本单位都是块。而对于应用程序和高速缓存区打交道都是基于页的概念,页是在块纸上又一层抽象,一个页一般是4K,也就是4个块。下面这张图可以详细说明这些概念之间的关系。

buffer

总结


进程要和块设备打交道都是需要经过高速缓存的(也有特殊情况)进程发起读请求,首先会有缓存区页面管理程序接收,它会根据逻辑块号和设备号去高速缓存区找请求的buffer,找到就直接返回;如果找不到,缓存区页面管理程序会向底层的驱动程序发出读指令,等其放回后缓存到高速缓存里,然后返回给应用进程。至于高速缓存区的维护则是由内核维护,淘汰算法也是LRU。

Linux虚拟内存概述

为什么需要虚拟内存?


程序是一系列代码段,数据段的集合,而程序要运行必须是加载到内存里的,但是物理内存就那么大,如何能保证很多个程序都装载进去呢?这里就引进了虚拟内存的概念,虚拟内存基本思想就是,给每个程序都分配一个4G的虚拟的内存,但这部分内存占用的不是物理内存,而是磁盘空间,这部分叫做虚拟存储器,就是安装Linux系统时候的SWAP空间。而对应的物理内存就是物理存储器。
有没有觉得整个过程像开了“空头支票”一样?程序要跑起来,操作系统许诺它给你4G的空间,但却是不能用的,那真要执行的时候怎么办呢?操作系统会把磁盘上的程序代码数据“移”到内存里,把不需要的还会“移”出去到磁盘上,这样看上去就好像可以跑很多进程了。

- 阅读剩余部分 -