页写保护

1、进程A和进程B共享页面,代码如下:

if (!(pid=fork())) {
	压栈操作;//子进程B
}
if (pid>0){
    压栈操作;//父进程A
}

2、我们假设现在系统有一个用户进程A,他自己对应的程序代码已经载入内存中,此时该进程内存中所占用的页面引用计数都为”1“,接下来他开始执行,通过调用fork函数创建一个新进程(进程B)。在新进程创建的过程中,系统将进程A的页表项全部复制给进程B,并设置进程B的页目录项。此时这两个进程就共享页面,被共享页面的引用计数累加为2,并将此共享页面全部设置为”只读“属性,即无论是进程A还是进程B,都只能对这些共享的页面进程读操作,而不能进行写操作。执行代码如下:

int copy_page_tables(unsigned long from,unsigned long to,long size)
{
	......
	for( ; size-->0 ; from_dir++,to_dir++) {
		......
		for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
			this_page = *from_page_table;
			if (!(1 & this_page))
				continue;
			this_page &= ~2;//进程A对页面的操作属性被设置为只读
			*to_page_table = this_page;//进程B对页面的操作属性被设置为只读
			if (this_page > LOW_MEM) {
				*from_page_table = this_page;
				this_page -= LOW_MEM;
				this_page >>= 12;
				mem_map[this_page]++;//引用计数记录在mem_map中,累加为2
			}
		}
	}
	invalidate();
	return 0;
}

3、我们假设接下来轮到进程A执行,进程A接下来的动作是一个压栈动作,现在看看会发生什么。

现在进程A的程序对应的所有页面都是只读状态的。压栈是个写操作,所以会产生”页写保护“异常,”页写保护“中断对应的服务程序时up_wp_page()函数。

函数执行时,先要在主内存中申请一个空闲页面(以后我们称之为新页面),以便备份刚才压栈的位置所在页面(以后我们称之为原页面)的全部数据,然后将原页面的引用计数减1。

代码如下:

void un_wp_page(unsigned long * table_entry)//页表项,现在指向原页面
{
	unsigned long old_page,new_page;

	old_page = 0xfffff000 & *table_entry;
	......
	if (!(new_page=get_free_page()))//申请到新页面
		oom();
	if (old_page >= LOW_MEM)
		mem_map[MAP_NR(old_page)]--;//页面引用计数递减1
	*table_entry = new_page | 7; //7的二进制形式为111,标志着新页面可读可写了,将进程A的页表项指向新申请的页面
	invalidate();
	copy_page(old_page,new_page);//复制原页面的内容到进程A新申请的页面
}	

4、将进程A的页表项指向新申请的页面,代码是*table_entry = new_page | 7;

5、复制原页面的内容到进程A新申请的页面,代码是copy_page(old_page,new_page);如下图:

6、进程A在用户空间开始执行压栈,仿佛什么都没有发生,压栈的数据却已经压入了新申请的页面。

7、进程A执行一段时间后,就该轮到他的子进程--进程B执行了,进程B仍然使用着原页面。进行压栈操作,也会出现”页写保护“异常,执行up_wp_page()函数。由于原页面的引用计数已经被削减为1了,所以现在就要将原页面的属性设置为”可读可写“,如下:

void un_wp_page(unsigned long * table_entry)
{
	unsigned long old_page,new_page;

	old_page = 0xfffff000 & *table_entry;
	if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {//发现原页面引用计数为1,不用共享了
		*table_entry |= 2;//2的二进制形式为010,R/W位被设置为1,可读可写
		invalidate();
		return;
	}
	if (!(new_page=get_free_page()))
		oom();
	if (old_page >= LOW_MEM)
		mem_map[MAP_NR(old_page)]--;
	*table_entry = new_page | 7;
	invalidate();
}

进程B在用户空间开始执行压栈,仿佛什么都没有发生,压栈的数据却已经压入了原页面。

最后形成如下图:

8、进程A和进程B在压栈数据的处理方面可以操作不同的页面了。这些页面都是可读可写的,而且引用计数都为1,以后彼此都不会干扰对方。
    现在进程B并没有自己的程序,如果将来它有了自己的程序,就会和原页面解除关系,原页面的引用计数将会继续减1,于是变成0,系统将认定他为”空闲页面“,可以重新用于分配。

时间: 2024-09-16 13:13:42

页写保护的相关文章

2_多核复杂性

前置知识 为了防止中断嵌套会自动 cli 在中断能处理的时候会自动 cli -- 清除中断标记位 if. 这样如果在if = 0 的时候 ,0环死循环 那么就会造成系统假死崩溃. 实际代码验证 我们把前一次的代码设置成死循环 void _declspec(naked) IdtEntry(){// 这里是裸函数,所以不会有函数头 push ebp,mov ebp,esp,,和 ret x / sub esp,x 来平衡堆栈:// 这样的好处是 我们能控制全部的代码.__asm{ //mov eax

15_TLB中的G属性

> TLB 是为了增加访问内存的效率 即 如果 是 29 9 12 分页 请求数据 可能需要访问 4次内存:为了解决这个问题:出现了 TLB (虚拟地址到物理地址的转换关系),如果目标地址在TLB缓存中,那么直接从TLB 取出 物理地址: > 这个实验做起来很麻烦,因为: TLB 是CPU 内部的,没法通过汇编指令访问TLB: 调试器,也没有办法知道 TLB 中有哪些项 只有通过实验现象 结果,来证明其存在. 内存访问的步骤: > 注意 TLB 产生异常 3: 不会回到 2:而是产生 0

【转】关于SSDT HOOK取消内存写保护的问题

原帖 关于SSDT HOOK取消内存写保护的问题 有些人说不去掉也不会蓝屏,照样能HOOK成功确实,我当时也是这样过...不过拿给别人机器一测试就蓝了 网上找到了MJ给出的答案:当使用大页面映射内核文件时,代码段和数据段在一块儿,所以页必须是可写的,这种情况下直接改是没有问题的 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management下面增加两个DWORD值'LargePageMin

sql server 导入平面文件源数据,错误 0xc02020a1错误 0xc020902a 错误 0xc02020c5,返回状态值 4 和状态文本“文本被截断,或者一个或多个字符在目标代码页...

使用sql server 导入平面文件源数据时,报错:错误 0xc02020a1: 错误 0xc020902a: 错误 0xc02020c5:错误 0xc0047022: 返回状态值 4 和状态文本"文本被截断,或者一个或多个字符在目标代码页中没有匹配项. 错误 0xc02020a1: 数据流任务 1: 数据转换失败.列"列 6"的数据转换返回状态值 4 和状态文本"文本被截断,或者一个或多个字符在目标代码页中没有匹配项.". (SQL Server 导入

ASP.NET 母版页和内容页中的事件触发顺序

母版页和内容页触发事件的先后顺序,触发事件的规则一般是初始化事件是从最里边的控件到最外边的控件,其他事件则是从最外边控件到最里边控件. 下面是一个测试: 第一次触发的是母版页的Init事件. 第二次触发的是内容页的Init事件. 第三次触发的是内容页的Load事件 第四次触发的是母版页的Load事件 总结: 母版页与内容页触发事件的顺序: 1.母版页Init事件. 2.内容页Init事件. 3.内容页Load事件 4.母版页Load事件. 5.内容页PreRender事件. 6.母版页PreRe

XtraGrid滚轮翻页

滚轮翻页与传动的翻页更为方便,经过本人一番探讨与琢磨终于在XtraGrid的GridView中实现了鼠标滚轮翻页. 我新建了一个组件继承原本的GridControl,在组件中添加了一个ImageList,专门存放一些资源图片.用于实现动态图的效果. 添加一个自定义委托的参数与枚举,委托参数用于传递分页的信息.     public class PagingEventArgs : EventArgs     {        public int PageSize { get; set; }   

JavaWeb:网上书店的案例 之 翻页操作

流程: 首先浏览器把请求发给 JSP(index.jsp),index.jsp 实际上调用的是 BookServlet 的 getBooks() 方法.然后到了 Servlet ,这个Servlet 需要获取请求参数:maxPrice, minPrice, pageNo,把请求参数封装为 CriteriaBook 对象.如果没有传这三个参数,它们是有默认值的.然后调用 BookService 的getPage() 方法,要把 CriteriaBook 传进来,BookService 再来调 Bo

Linux(CentOS)安装Oracle_11g_r2数据库(四)支持sqlplus上下翻页

下载地址:http://down.51cto.com/data/2277765 Oracle数据库使用时,因为不能上下翻页之前的命令,非常不方便.所以安装"rlwrap"来解决. 安装命令,注意安装是要在root用户下. # tar -xf rlwrap-0.42.tar.gz  # cd rlwrap-0.42 # ./configure  # make && make install 编译安装完成 在Oracle用户的变量文件.bash_profile文件里追加下面

Freemarker商品详情页静态化服务调用处理

--------------------------------------------------------------------------------------------- [版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/53151462 作者:朱培      ID:sdksdk0 ----------------------------------------------------