Linux内核设计与实现摘录(3)

内核把物理页作为内存管理的基本单位。尽管处理器的最小可寻址单位通常为字,但是,内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址的硬件)通常以页为单位进行处理。从虚拟内存的角度来看,页就是最小单位。

内核用struct page结构标识系统中的每个物理页。必须要理解的一点是page结构与物理页相关,而并非与虚拟页相关。

这种数据结构的目的在于描述物理内存本身,而不是描述包含在其中的数据。系统中的每个物理页都要分配一个这样的结构体。

2.区

由于硬件的限制,内核并不能对所有的页一视同仁。有些页位于内存中特定的物理地址上,所以不能将其用于一些特定的任务。

由于存在这种限制,内核把页划分为不同的区(zones)。

Linux使用了三种区:

ZONE_DMA:这个区包含的页能用来执行DMA操作。

ZONE_NORMAL:这个区包含的都是能正常映射的页。

ZONE_HIGHEM:这个区包含“高端内存”,其中的页并不能永久地映射到内核地址空间。

3.slab层

分配和释放数据结构是所有内核最普遍的操作之一。为了便于数据的频繁分配和回收,编程者常常会用到一个空闲链表。该空闲链表包含有可供使用的、已经分配好的数据结构块。当代码需要一个新的数据结构实例时,就可以从空闲链表中抓取一个,而不需要分配内存,再把数据放进去。以后,当不再需要这个数据结构的实例时,就把它放回空闲链表,而不是释放掉它。Linux提供了slab层(也就是所谓的slab分配器),其扮演了“通用数据结构缓存层”的角色。

4.内存分配

void *kmalloc(size_t size, int flags)

这个函数返回一个指向内存块的指针,其内存块至少要有size的大小。所分配的内存区在物理上是连续的。

分配的内存可能比你请求的还多,但是你无法知道到底多了多少。因为内核分配器本质上是基于页的,因此在可用内存内,某些分配可能向上取整。但内存绝不会少于所需的内存,如果内核不能找到所需的最少量,那么,分配就会失败,函数返回NULL。

5.内核栈

内核的栈大小固定,大多数32位体系的结构上,栈为8KB。

每个进程都有自己的内核栈。进程在内核执行期间的整个调用链必须放在自己的内核栈上。深度嵌套会导致溢出。

中断处理程序也使用被它们打断的进程的堆栈。

在栈上进行大量静态分配,比如分配大型数组和大型结构体,是很危险的。

内核栈溢出时悄无声息,不会有任何错误提示,因为内核没有在管理内核上做足工作,因此,当栈溢出时,多出的数据就会直接溢出来,覆盖掉紧邻堆栈末端的东西。

内核是完全信赖自己的,这点与用户空间不同,如果你有非法操作,内核会开开心心地把自己挂起来,停止运行。

【块I/O层】

块设备

块设备中最小的可寻址单元是扇区。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元--块设备无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次传输多个扇区。大多数块设备的扇区都是512字节。块是文件系统的一种抽象--只能基于块来访问文件系统。虽然物理磁盘寻址是按照扇区级进行的,但是内核执行的所有磁盘操作都是按照块进行的。块和扇区的大小都是2的整数倍,一般块是数个扇区大小,另外块大小不能超过一个页的长度。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwsxwj.html