Linux0.12内核之内存管理(3)

本系列的第三篇文章主要来介绍与共享物理页面相关的两个函数。

//在发生缺页异常的时,首先看看能否与运行同一个文件的其他进程进行页面共享处理。该函数首先判断系统中是否有另外进程也在运行与当前进程一样的执行文件。若有,则在系统当前任务中找寻这样的任务。若找到了这样的任务就尝试与其共享指定地址处的页面。判断系统中是否有另一个进程也在执行同一个可执行文件的方法是利用进程任务数据结构中的executable字段。该字段执行进程正在执行程序的内存中的i节点,根据该i节点的引用次数i_count我们就可以判断。
static int share_page(struct m_inode *inode,unsigned long address)
{
	struct task_struct **p;
	if(inode->i_count<2 || !inode)
		return 0;

	for(p=&LAST_TASK;p>&FIRST_TASK;--p)
	{
		if(!*p)
			continue;
		if(current==*p)
			continue;
		if(address<LIBRARY_OFFSET)
			if(inode!=(*p)->executable)
				continue;
		else{
			if(inode!=(*p)->library)
				continue;
			}
		if(try_to_share(address,*p))
			return 1;
	}
	return 0;
}

//在任务p中检查位于线性地址"address"处的页面,看页面是否存在,是否干净。如果p进程address处的页面存在并且没有被修改过的话,就让当前进程与p进程共享之。同时还需要验证置顶的地址处是否已经申请了页面,若是则出错。
static int try_to_share(unsigned long address,struct task_struct *p)
{
	unsigned long from;
	unsigned long to;
	unsigned long from_page;
	unsigned long to_page;
	unsigned long phys_addr;

	//首先分别求指定进程p中和当前进程中逻辑地址address对应的页目录项。为了计算方便,先计算出address处“逻辑"页目录项号,即以进程空间0-64MB算出的页目录项号。该"逻辑"页目录项号加上进程p在cpu 4G线性空间中起始地址对应的页目录项即得p中的地址address处页面所对应的4G线性空间中的实际页目录项from_page

	from_page=to_page=((address>>20) & 0xffc);
	from_page+=((p->start_code) & 0xffc);
	to_page+=((current->start_code) & 0xffc);

	//在得到p和当前进程address对应的页目录项后,下面分别对进程p和当前进程进行处理。首先对p进程的表项进程操作。目标是取得p进程中address对应的物理内存页面地址,并且该物理页面存在并且干净(没被修改)。方法是首先取目录项内容,如果该目录项无效(P=0),表示目录项对应的二级页表不存在,于是返回。否则取该目录项对应的页表地址from,从而计算出逻辑地址address对应的页表项指针,并取出该页表项内容临时保存在phys_addr中。
	from=*(unsigned long*)from_page;
	if(!(from & 1))
		return 0;
	from &=0xfffff000;
	from_page=from+((address+10) &0xffc;
	phys_addr=*(unsigned long*)from_page;

	//接着看看页表映射的物理页面是否存在并干净。0x41对应页面中的D和P标志。如果页面不干净或者无效则返回。然后我们从该表象中取出物理地址保存在phys_addr中,最后我们检查这个物理页面地址的有效性。
	if(phys_addr & 0x41)!=0x01)
		return 0;
	phys_addr &=0xfffff000;
	if(phys_addr>=HIGH_MEMORY || phys_addr<LOW_MEM)
		return 0;

	//下面对当前进程的表项进程操作。目标是取得当前进程中address对应的页表项地址,并且该页表项还没有映射物理页面,即其P=0.首先取得当前进程页目录项内容->to。如果该目录项无效P=0,即目录项对应的二级页表不存在,则首先申请空闲物理页来存放页表,并更新目录象to_page内容,让其指向该内容页面。
	to=*(unsigned long*)to_page;
	if(!(to & 1))
	{
		if(to=get_free_page())
			*(unsigned long*)to_page=to|7;
		else
			com();
	}
	to&=0xfffff000;
	to_page=to+((address>>10) &0xffc);
	if(1 & *(unsigned long*)to_page)
		panic("try_to_share:to_page already exists");

	//共享处理:首先对p进程的页表项进行修改,设置其写保护标志,然后让当前进程复制p进程这个页表项。此时当前进程逻辑地址address处页面就被映射到p进程逻辑地址address处映射的物理页面上。
	*(unsigned long*)from_page&=~2;
	*(unsigned long*)to_page=*(unsigned long*)from_page;

	invalidate();
	phys_addr-=LOW_MEM;
	phys_addr>>12;
	mem_map[phys_addr]++;
	return 1;
}

使用一个图来总结share_page的工作:

Linux0.12内核之内存管理(3)

时间: 2024-10-24 08:10:44

Linux0.12内核之内存管理(3)的相关文章

Linux0.12内核之内存管理(2)

本文主要介绍Linux0.12内核memory.c中的函数 1.void free_page(unsigned long addr) //释放物理地址addr处的一页内存.用于free_page_tables()函数 void free_page(unsigned long addr) { //首先判定给定物理地址的合理性.如果物理地址addr小于内存低端1M,对此不///予处理.如果addr>=内存最高端,则显示出错并且内核停止工作 if(addr<LOW_MEM) return; if(a

linux0.12内核的内存组织和进程结构

进程结构 Linux0.12中的每个进程都有如下的结构: 在gdt中占有两项,一项是tss段描述符,一项是ldt段描述符. 在task数组中占有一项,指向一页物理内存,该物理内存低端是进程控制块task_struct(里面包括tss段和ldt段),其余部分是进程的内核态堆栈. 在页目录表和页表中设置有相关项. Linux0.12中,最多只有64个进程,task数组大小也定义成了64,每个进程与一个task数组中的项一一对应.虽然gdt中有256项,但是并不是都用到.第一个gdt项保留不用,内核用

Linux 0.12内核与现代内核在内存管理上的区别

0.12内核的内存管理比较简单粗暴,内核只用了一个页目录,只能映射4G的线性空间,所以每个进程的虚拟空间(逻辑空间)只能给到64M,最多64个进程:每个进程都有对应的任务号nr,当一个进程需要分配进程空间时,只需要nr乘以64M就可以得出该进程空间的线性起始地址.然后该进程的代码段.数据段描述符里面的基址字段会被设定为(nr x 64M),同时可以为进程分配页目录项和页目录表用以承载映射关系. 之后如果进程要访问自己空间内的某个地址时就会首先用基地址与程序内32位偏移地址(逻辑地址)合成出线性地

Linux0.12内核学习之(1)——用MASM编写Boot Sector引导扇区

最近在学习Linux0.12内核,正在读<Linux内核完全剖析>.一开始就被ax86写的引导扇区弄晕了.于是Google了很多资料.最终实验了一晚上终于搞定.下面来看看我们怎么用Windows下的MASM来写个Boot Sector.因为我MASM汇编用的比较熟,所以就用MASM来写,当然,汇编只有语法差异,你用什么来写都没关系. 首先,先来说说计算机怎么启动的.经过一系列BIOS加电.系统自检后,会将硬盘0面0道1扇区的512字节(Boot Sector)加载到内存地址07c0:0000处

Linux内核之内存管理(4)--缺页处理程序

本文主要解说缺页处理程序,凝视足够具体,不再解释. //以下函数将一页内存页面映射到指定线性地址处,它返回页面的物理地址 //把一物理内存页面映射到线性地址空间指定处或者说把线性地址空间指定地址address处的页面映射到主内存区页面page上.主要工作是在相关也文件夹项和页表项中设置指定页面的信息.在处理缺页异常函数do_no_page中会调用这个函数. 參数:address--线性地址:page--是分配的主内存区中某一页面指针 static unsigned long put_page(u

linux0.12内核bootsect.S

这个文件就是0.12内核的主引导扇区代码,他的作用就是加载操作系统内核. 计算机加电,自检完毕后,BOIS就将启动设备的第一扇区加载到内存0x7c00(31KB)处,并开始从这里执行,若启动设备是硬盘的话,加载的即为该硬盘0磁道0柱面1扇区的内容,共512字节,以0xAA55为结束标志,这就是硬盘的MBR(master boot recorder). linux0.12内核bootsect.S中的代码编译后,将会写入启动设备的MBR中,它的主要工作加载操作系统内核的初始化程序setup.S和操作

Linux-0.12内核sleep_on函数分析

sleep_on用于进程休眠,原型如下: void sleep_on(struct task_struct **p) 当进程访问某个互斥资源时,如果资源被另外进程占用,当前进程就需要休眠. 假设资源的结构如下: struct res { .... struct task_struct *wait; } 其实我们参考下文件系统的i节点就会发现,i节点也是一种资源,它的结构体中就有一个变量i_wait.那么我们就用i节点举例.如果进程访问某个i节点,发现i节点被锁住,当前进程就需要睡眠:sleep_

Linux 内核开发 - 内存管理

1.1什么是内存管理 内存管理是对计算机内存进行分配和使用的技术.内存管理主要存在于多任务的操作系统中,由于内存资源极其有限,需要在不同的任务之间共享内存,内存管理的存在就是要高效.快速的非配内存,并在适当的时候回收和释放内存,以保各个任务正常的执行.最常见的内存管理机制有:段式内存管理和页式内存管理. 1.2内存中的地址 早期的16位计算中,寄存器的位宽只有16位,为了能访问到1M Bit的内存空间,CPU就采用了分段的方式来管理内存,将1M的内存分为若干个逻辑段,每个逻辑段的起始地址必须是1

Linux内核之内存管理完全剖析

linux虚拟内存管理功能 ? 大地址空间:? 进程保护:? 内存映射:? 公平的物理内存分配:? 共享虚拟内存.实现结构剖析 (1)内存映射模块(mmap):负责把磁盘文件的逻辑地址映射到虚拟地址,以及把虚拟地址映射到物理地址 (2)交换模块(swap)负责控制内存内容的换入与换出,淘汰最近没访问的页,保留最近访问的页. (3)core(核心内存管理模块):负责内存管理功能. (4)结构特定模块:实现虚拟内存的物理基础 内核空间和用户空间 Linux简化了分段机制,使得虚拟地址跟线性地址一样.