Linux内核详解(五)

来源:互联网 发布:ubuntu镜像文件安装 编辑:程序博客网 时间:2024/06/09 23:03
Linux块I/O层

系统能够随机访问固定大小数据块的硬件设备称作块设备,比如硬盘/闪存;按照字符流的方式被有序访问就是字符设备,比如键盘。两者的区别在于是否可以随机访问数据。


块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍(512B),虽然物理磁盘寻址是以扇区为基本单位的,但内核执行的所有磁盘操作都是以块为基本单位的。块是文件系统的一种抽象——只能基于块来访问文件系统。内核要求块大小是扇区大小的2的整数倍,并且要小于页的大小,所以块大小一般是512B/1KB/4KB。


当一个块被调入内存时(读入/写出),它要存储在一个缓冲区中,每个缓冲区都与一个块相对应,因为块大小不能超过一个页,所以一个页可以容纳一个或多个内存中的块;另外一些相关的控制信息(比如块属于哪个块设备、对应于哪个缓冲区)存放在缓冲区头中,每个缓冲区都有一个缓冲区头,用buffer_head结构体表示,缓冲区头的目的就是描述磁盘块和物理内存缓冲区之间的映射关系。


目前Linux内核中块I/O操作的基本容器由bio结构体来表示,该结构体代表了正在活动的以段segment链表形式组织的块I/O操作,一个segment是一小块连续的内存缓冲区(这样也就不需要单个缓存区一定要连续),所以通过使用段来描述缓冲区,一个缓冲区也就可以分散在内存的多个位置上。这样的向量I/O也就是发散汇聚I/O。

struct bio结构体中的bi_io_vec域指向一个bio_vec结构体链表,该结构体链表包含一个特定I/O操作所需要使用到的所有segment,即表示了一个完整的缓冲区。每个bio_vec结构体都是一个形式为<page,offset,len>的向量,描述一个特定的段segment(<片段所在的物理页,块在物理页中的偏移量,块长度)。另外bi_vcnt域描述向量的数量,bi_idx域表示当前索引。

总之,每一个块I/O请求都通过一个bio结构体表示,每个请求包含一个或多个块,这些块存储在bio_vec结构体链表中。


 I/O调度程序将磁盘I/O资源分配给系统中所有挂起的块I/O请求(进程调度程序是将cpu资源分配给运行进程)。为了优化寻址操作,内核既不会简单地按照请求接收次序,也不会立即将其提交给磁盘,相反它会在提交前先将请求队列中挂起的请求执行合并与排序的预操作。排序是指将整个请求队列按扇区增长方向排列,这样通过保持磁头单向移动缩短了所有请求的磁盘寻址时间(类似于电梯调度算法)。


进程地址空间

内核除了管理本身的内存外,还必须管理用户空间进程的内存,也就是进程地址空间,系统中的所有进程以虚拟方式共享内存。


内核使用内存描述符mm_struct结构体表示进程的地址空间,在进程的进程描述符task_struct结构体中的mm域指向当前进程的内存描述符。当调用fork函数时会执行copy_mm函数复制父进程的内存描述符给子进程,而子进程中的mm_struct结构体实际上是通过allocate_mm宏从mm_cachep slab缓存中分配得到的;通常每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间(否则就是线程——此时不需要调用allocate_mm,而只是在调用copy_mm时将mm域指向其父进程的内存描述符即可)。


当一个进程被调度时,该进程的mm域指向的地址空间被装载到内存中(另外内核线程没有地址空间,其mm域为NULL,它实际上是直接使用前一个进程的内存描述符,因为内核线程不会访问用户空间的内存,仅仅只是在访问内核内存时使用像页表这样的数据,所以OK)。
内存区域由vm_area_struct结构体描述(VMAs虚拟内存区域),该结构体描述了指定地址空间内连续区间上的一个独立内存范围,其中的vm_mm域指向和VMA相关的mm_struct结构体;注意每个VMA对其相关的mm_struct来说都是唯一的。


可以通过内存描述符中的mmap或mm_rb域访问内存区域,两者的区别在于前者使用单独的链表连接所有的内存区域对象,所有的区域按地址增长的方向排序,而mm_rb域使用红黑树连接所有的内存区域对象。链表适合进行遍历全部节点,而红黑树适合在地址空间中定位特定内存区域。


虽然应用进程操作的对象时映射到物理内存之上的虚拟内存,但是cpu直接操作的却是物理内存。当应用程序访问一个虚拟地址时,首先将虚拟地址转换成物理地址,这样cpu才能解析地址访问请求。地址转换是通过查询页表来完成的,即先将虚拟地址分段,使每个虚拟地址都作为一个索引指向页表,而页表项则指向下一级别的页表或最终物理页面。

Linux使用三级页表完成地址转换,这样可以节约地址转换所需要的内存:



0 0
原创粉丝点击