博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于内核页表和进程页表的一个问题
阅读量:6199 次
发布时间:2019-06-21

本文共 2221 字,大约阅读时间需要 7 分钟。

昨天回复了一封电子邮件,有朋友问个问题很有代表性,内核初始化时会将896M前的物理页面作一一映射,那么用户进程分配到896M前的页面建立用户映射时是否要清除内核的一一映射。

关于这个问题,我的前面的文章已经有了解释,但是不甚详细,现在通过一个例子详细解释一下。实际上并不需要清除内核的一一映射,内核的一一映射只有内核自己使用,而且带来了很多的方便,内核巧妙的通过一一映射快速的执行内核路径,其实内核的一一映射也只有内核自己知道,用户进程根本涉及不到,内核只要管理好自己的内存没有什么是不可以的。页面的页表映射是硬件MMU的机制,而OS内核中拥有的是内存管理机制,二者并不冲突,它们都是独立的机制,并且层次不同,完全可以独立存在,比如你可以在没有MMU的嵌入式设备上实现复杂的内存管理,同时你也可以在同一个硬件MMU下实现不同的内存管理,比如windows和linux的就不同,我的观点大致总结如下(缺漏的部分前面的文章中有):在linux中内核一般不会介入用户的策略,它只是提供机制,向上就到系统调用接口为止,它没有upcall接口;只要用户不会访问内核,内核不会随意访问用户内存就不会有冲突,用户是难缠的,而且行为是不确定的,内核的行为是确定的,用户显然不能访问内核内存,但是内核却可以访问用户内存,然而内核十分清楚自己拥有哪些内存,比如初始化的时候将896以下页面作了一一映射,可是内核不一定用得了那么多,当用户进程需要内存时,完全可以从还在伙伴系统的512M处拽一个页面分配之并且映射到用户空间同时保留着内核的一一映射,这时这个512M处的页面已经从伙伴系统脱离了,内核如果这时也需要内存是不会被分配到该页面的,这样就不会有冲突,如果该页面本来就由内核所使用,那么它就不会在伙伴系统也不可能分配到用户进程,这么来说也不会有冲突,linux的内存管理和硬件的 MMU是两码事,如果说有联系那就是映射,影射仅仅是一个纽带和一个适配器而已。

下面我就通过一个例子来说明,当然要写内核模块了,我的机器是512M内存,少于896M,也就是全部作了一一映射。我们首先执行以下命令得到insmod程序的一些信息,因为模块加载是在insmod进程的上下文中:

[root@zhaoya ~]#objdump -d /sbin/insmod

...

08048ab0 <.fini>:

8048ab0: 55 push %ebp

8048ab1: 89 e5 mov %esp,%ebp

8048ab3: 53 push %ebx

...//0xcebe0000

我们看到进程地址空间0x8048ab3处是53,我们如果直接访问0x8048ab3,那么得到的就是53,这是显然的,但是我们还可以得到0x8048ab3所在页面的内核一一映射地址,然后访问那个地址看是不是还是53,如果是,那么就说明用户页面可能存在两份映射,接下来写一个模块:

#include

#include

#include

#include

#include

#include

static __init int test_init(void)

{

char * user_addr = (char *)0x8048ab3; //常规访问这个地址

printk("%x/n",*str);

struct page *page;

int n = get_user_pages(current, current->mm, user_addr, 1, 0, 1,&page,NULL);

printk("count:%d/n",n);

if(n>0)

{

unsigned long addr = page_address(page);

printk("address:%X/n",addr);

unsigned char * p = (unsigned char *)(address + user_addr&0xfff) ;//最后加入偏移

printk("%x/n",*p); //按照内核一一映射访问页面的内核地址

}

return 0;

}

static __exit void test_exit(void)

{

return ;

}

module_init(test_init);

module_exit(test_exit);

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("Zhaoya");

MODULE_DESCRIPTION("kernel map");

MODULE_VERSION("Ver 0.1");

我们实在没有必要用cr3得到页目录,然后用二级或者三级乃至多级页面映射的方式去得到页面,那是非常复杂的,而且和体系结构相关,如果非要那样做的话首先你必须熟悉你的机器,其次还要熟悉C语言,幸运的是,内核提供了get_user_pages这个函数,我们可以轻而易举的根据虚拟地址得到页面。结果当然显而易见了,两次打印都是53,如果觉得不保险可以多试几个虚拟地址。

实际上一个页面保持多个映射并不是稀罕事,原因还是前面说的,内存管理和MMU管理是两码事。比如共享内存页面就可能保持多个映射。

 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1273945

转载地址:http://zlnca.baihongyu.com/

你可能感兴趣的文章
hdu 3306 Another kind of Fibonacci
查看>>
【SICP练习】116 练习3.42
查看>>
【sql查询与优化】3.操作多个表
查看>>
connecting docker containers on multiple hosts with open vswitch GRE
查看>>
linux网络实现分析(1)——数据包的接收(从网卡到协议栈)
查看>>
自定义 Lint 规则简介
查看>>
从volatile解读ConcurrentHashMap(jdk1.6.0)无锁读
查看>>
学习嵌入式的心得
查看>>
开发一个vue插件并且发布到npm(二)
查看>>
使用koa2+wechaty打造个人微信小秘书
查看>>
自用日志中ThreadLocal的使用
查看>>
前端日刊君来也
查看>>
知识点归档,博客记录
查看>>
docker
查看>>
一文秒懂厂商推送
查看>>
Vue子传父,父组件事件处理函数中arguments的使用
查看>>
DispatchSemaphore & DispatchGroup
查看>>
使用Netty三分钟手写一个RPC
查看>>
前端导出数据表格(jpg、csv)
查看>>
少编码多思考:代码越多 问题越多
查看>>