看了linux内核内存管理,参考网上的意见整理了一下,认为两个比较好的博文:
http://blog.csdn.net/chobit_s/article/details/6029527
http://blog.csdn.net/do2jiang/article/details/5495675
1.页框管理
Linux采用4KB页框大小作为标准的内存分配单元。内核必须记录每个页框的状态,这种状态信息保存在一个类型为page的页描述符中,所有的页描述存放在mem_map中。virt_to_page(addr)产生线性地址对应的页描述符地址。pfn_to_page(pfn)产生对应页框号的页描述符地址。
在页框描述符中,几个关键的字段我认为:flags、_count、_mapcount。
由于CPU对内存的非一致性访问,系统的物理内存被划分为几个节点(每个节点的描述符为pg_data_t),每个节点的物理内存又可以分为3个管理区:ZONE_DMA(低于16M的页框地址),ZONE_NORMAL(16MB-896MB的页框地址)和ZONE_HIGHMEM(高于896MB的页框地址)。
每个管理区又有自己的描述符,描述了该管理区空闲的页框,保留页数目等。每个页描述符都有到内存节点和到节点管理区的连接(被放在flag的高位字段)。
内核调用一个内存分配函数时,必须指明请求页框所在的管理区,内核通常指明它愿意使用哪个管理区。
2.保留的页框池
如果有足够的空闲内存可用、请求就会被立刻满足。否则,必须回收一些内存,并且将发出请求的内核控制路径阻塞,直到有内存被释放。但是有些控制路径不能被阻塞,例如一些内核路径产生一些原子内存分配请求。尽管无法保证一个原子内存分配请求不失败,但是内核会减少这中概率。为了做到如此,内核采取的方案为原子内存分配请求保留一个页框池,只有在内存不足时才使用。页框池有ZONE_DMA和ZONE_NORMAL两个区贡献出一些页框。
常用的请求页框和释放页框函数:
alloc_pages(gfp_mask, order): 获得连续的页框,返回页描述符地址,是其他类型内存分配的基础。
__get_free_pages(gfp_mask, order): 获得连续的页框,返回页框对应的线性地址。线性地址与物理地址是内核直接映射方式。不能用于大于896M的高端内存。
__free_pages(page,order);
__free_pages(addr,order);
3.高端内存页框的内核映射
高端内存是指物理地址大于 896M 的内存。对于这样的内存,无法在“内核直接映射空间”进行映射。因为“内核直接映射空间”最多只能从 3G 到 4G,只能直接映射 1G 物理内存,对于大于 1G 的物理内存,无能为力。实际上,“内核直接映射空间”也达不到 1G, 还得留点线性空间给“内核动态映射空间” 呢。因此,Linux 规定“内核直接映射空间” 最多映射 896M 物理内存。
对于高端内存,可以通过 alloc_page() 或者其它函数获得对应的 page,但是要想访问实际物理内存,还得把 page 转为线性地址才行(为什么?想想 MMU 是如何访问物理内存的),也就是说,我们需要为高端内存对应的 page 找一个线性空间,这个过程称为高端内存映射。高端内存映射有三种方式:
(1)永久内核映射
永久内核映射允许内核建立到高端页框内核地址空间的长期映射。当空闲页表项不存在时,也就是高端内存中没有页表项用用作页框的“窗口”时,永久内核映射可能阻塞当前进程。因此永久内核映射不能用用于中断处理程序和可延迟函数。
永久内核映射使用主内核页表中的一个专门页表,其地址存放在pkmap_page_table中。页表的表项有LAST_PKMAP产生。该页表映射的线性地址为从PKMAP_BASE开始,即内核专门为此留出一块线性空间,从 PKMAP_BASE 到 FIXADDR_START ,用于映射高端内存。在 2.4 内核上,这个地址范围是 4G-8M 到 4G-4M 之间。这个空间起叫“内核永久映射空间”或者“永久内核映射空间”
如果是通过 alloc_page() 获得了高端内存对应的 page,如何给它找个线性空间?(就是上面的PKMAP_BASE 到 FIXADDR_START)。
这个空间和其它空间使用同样的页目录表,对于内核来说,就是 swapper_pg_dir,对普通进程来说,通过 CR3 寄存器指向。
通常情况下,这个空间是 4M 大小,因此仅仅需要一个页表即可,内核通过来 pkmap_page_table 寻找这个页表。
通过 kmap(), 可以把一个 page 映射到这个空间来。
由于这个空间是 4M 大小,最多能同时映射 1024 个 page。因此,对于不使用的的 page,及应该时从这个空间释放掉(也就是解除映射关系),通过 kunmap() ,可以把一个 page 对应的线性地址从这个空间释放出来。
(2)临时内核映射
建立临时映射决不会要求阻塞档期进程,不过,它的缺点就是只有很少的临时内核映射可以同时建立起来。使用临时内核映射必须保证没有其他的内核控制路径使用同样的映射。&n
由于好长时间不用hibernate,故抽时间将这部分知识温习一下。在此记录一下自己的学习进度。好了,直接上代码
首先,先准备hibernate开发要用到的jar包,本次学习中使用的hibernate版本是3.4.0,上图:
准备好要用到的jar包,下面就开始真正的开发,我的开发环境是Eclipse helios 版本。
下一步,让我新建一个java project ,命名为spring_hibernate_day01,上图:
其中,model包下是实体类,还有hibernate映射文件,默认约定映射文件和实体类放在一起。
hibernate.cfg.xml 是hibernate的核心配置文件
test包下用来进行相应的测试。
好,直接上代码
model 类 Teacher:
import java.util.Date; public class Teacher { private int id; private String name; private String address; private Date year; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Date getYear() { return year; } public void setYear(Date year) { this.year = year; } }
映射文件teacher.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.**.hibernate.model"> <class name="Teacher"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="address"/> <property name="year"/> </class> </hibernate-mapping>
hiberante的核心配置文件,hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost/test</property> <property name="connection.username">root</property> <property name="connection.password">123456</property> <!-- JDBC connection pool (use the built-in) --> <!--<property name="connection.pool_size">1</property>--> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <!-- Enable Hibernate's automatic session context management --> <!--<property name="current_session_context_class">thread</property>--> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">update</property> <mapping resource="cn/***/hibernate/model/teacher.hbm.xml"/> </session-factory> </hibernate-configuration>
好,到此所有的代码都已经完成,下面让我们进行一下测试,看数据是否能保存到数据库里,上代码
TeacherTest:
public class TeacherTest { /** * @param args */ public static void main(String[] args) { Teacher t = new Teacher(); t.setName("name"); t.setAddress("beijing"); t.setYear(new Date()); Configuration cfg = new Configuration(); SessionFactory sf = cfg.configure().buildSessionFactory(); Session session = sf.openSession(); session.beginTransaction(); session.save(t); session.getTransaction().commit(); session.close(); sf.close(); } }
让我们查一下数据库,看数据是否保存到数据库中,上图:
到此,我们就完成了hibernate第一次的学习记录,有需要讨论的童鞋,请给我留言。。。
1.伙伴系统算法的提出
内核应该为分配一组连续的页框而建立一种健壮、高效的分配策略。为此,必须解决著名的内存,也就是所谓的外锁片问题(external fragmentation)。频繁的请求和释放不同大小的一组连续页框,必然导致在已分配的块内分散了许多小块的空闲页框。由此带来的问题时,即使有足够的空闲页框可以满足请求,但要分配一个大块的连续页框无法满足。
从本质上来说,避免外碎片的方法有两种:
(1)利用分页单元把一组非连续的空闲页框映射到连续的线性地址空间;
(2)开发一中适当的技术来记录现存的空闲连续页框快的情况,以尽量满足对小块的请求而分割大的空闲块。
Linux内核中引入了伙伴系统算法(buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍。例如,大小为16个页框的块,其起始地址是16*2^12(2^12=4096)的整数倍。
假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找。如果1024块存在,则将其中的256页框作为请求返回,剩余的768分成256块和512块分别插到相应的链表中。如果仍然没有,则返回错误。
页框块在释放时,会主动大小为相同的一个空闲伙伴块合成为2倍大小的单独块较大的页框块。两个块称为伙伴需要满足一下条件:
(1)两个块具有相同的大小
(2)它们的物理地址是连连续的。
(3)第一块的第一个页框的物理地址是2*b*2^12的倍数。
2.数据结构
包含一个11元素、元素类型为free_area的一个数组,每个元素对应一块大小。
free_area每个元素中有一个free_list,表示双向循环链表的头,这个双向循环链表集中了大小为2^k页的空闲块对应的页描述符。该链表包含每个空闲页框块(大小为2^k)的起始页框的页描述符。指向链表中相邻元素的指针存放在页描述符的lru字段中。
free_area每个元素还包含一个nr_free字段,它指定了大小为2^k的页框块个数。当然,如果没有大小为2^k的空闲页框块,则nr_free等于0且free_list为空。
一个空闲块的第一个页的描述符的private字段存放了块的order,也就是k。正式由于这个字段,当页框被释放时,内核可以确定这个块的伙伴是否也空闲。如果是的话就可以把两个块合成一个2^(i+1)的块。
3.实现