linux/mm/memory.c/do_no_page

// 处理缺页异常的函数体。address是事发地点线性地址。
void do_no_page(unsigned long error_code,unsigned long address)
{
int nr[4];
unsigned long tmp;
unsigned long page;
int block,i;


address &= 0xfffff000;
// address是线性页面地址,current->atart_code是进程在线性地址空间的起始地址,
// 两个相减所以tmp是事发地点的逻辑地址(即进程逻辑空间偏移量)
tmp = address - current->start_code;
// 缺页中断有多种情况,这里是第一种。
// current->executable == 0 表明当前进程没有可执行文件。tmp>=current_end_data
// 表示缺页的逻辑地址大于进程的代码段和数据段之和。这两种情况都对应着第一种缺页
// 异常来历:即当前缺页是由于进程压栈(为堆或栈中数据寻找新的页面)造成的,因此
// 直接调用get_empty_page为进程申请一页新物理内存即可。
if (!current->executable || tmp >= current->end_data) {
get_empty_page(address);
return;
}
// 若走到这里,则说明缺页异常不是由于进程压栈造成,那肯定就是执行exevce来得
// 进程的executable时导致缺页异常的。
// 于是乎先尝试share_page。即先看当前进程的executable是否被其他进程同样引用,若
// 能在其他进程中找到并且与其共享这页物理内存是最好了(因为这样省了读,而且避免了
// 同样地内存被多次加载到内存占地方又浪费时间)
if (share_page(tmp))
return;
// 要是没能share成功,退而求其次吧。只能为该进程注册一页新的物理内存,并且读取
// 相应内容到这页物理内存中了。
if (!(page = get_free_page()))
oom();
/* remember that 1 block is used for header */
// 以下为读取address地址处4096字节内容到内存中刚申请的page物理页面中。
// 首先计算address地址处内存在可执行文件中的块序号。计算方法为首先由address
// 得到缺页逻辑地址tmp(这个在之前已经算过了),然后由tmp/BLOCK_SIZE即得到
// tmp逻辑地址在可执行文件中的块序号,+1是考虑到文件头占用的开头一个block。
// 由这里可以得知,每个可执行文件在生成后,其第一个block都是用来存放文件头的
// 这个文件头放置文件的属性信息等。然后从第二个block开始才依次存放真正的可执行
// 文件image内容。
block = 1 + tmp/BLOCK_SIZE;
// 因为一个page有4k而一个block为1k,要求读取1page数据而读取函数每次读取1block
// 数据,因此要读取刚才计算的block开始的4个连续block的数据出来。
// 用bmap函数得到要读取的4个block在文件存储设备中的块号并记录下来。
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(current->executable,block);
// 调用bread_page函数读取这4个连续block的数据,放入page中。
bread_page(page,current->executable->i_dev,nr);
// 以下代码用来完成对多于读出来的无用字节清零。这主要是针对可执行文件中
// tmp后面的内容不足一页的情况下,我们读出的一页内容就有很多字节是无用的
// 这种情况下就要把这些内容给清零。
// 首先计算必须清零的字节数。tmp+4096将tmp指向出事地点页面的尾端,然后减去
// current->end_data便得到了必须清零的字节数。注意这里如果tmp后面的内容大于
// 一页时这个相减的结果i就是负值,当且仅当tmp后面剩余内容小于1页时i才为正值。
// 这个事实就会下面的while(i--)循环清除打下了伏笔。
i = tmp + 4096 - current->end_data;
// 将tmp指向page物理页面的尾端,这里是要清除位置的最后面。
tmp = page + 4096;
// 当上面计算的i为负值时清除代码是直接跳过的,ok,很有技巧···
// 若i为正值,则用i来计数开始从page页面的尾端开始逐个字节清除。
while (i-- > 0) {
tmp--;
*(char *)tmp = 0;
}
// 最后调用put_page来将当前进程address线性地址空间页面与page处物理页面
// 挂接起来,挂接成功直接返回。(返回后会重新执行刚才导致缺页异常的那句
// 代码,从而完美实现load on demand)
if (put_page(page,address))
return;
// 执行到这表示put_page失败,这表示此次load on demand失败了。善后时要
// free刚才本函数申请的一页物理内存。并且提示out of memory。
free_page(page);
oom();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱有鹏老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值