- 论坛徽章:
- 0
|
在水木看到的一些讨论,希望楼主可以找到一些线索。
i386环境
过程如下:
1。用一个内核模块注册一个char设备。
2。在一用户进程中,定义一全局的字符串。在main中将字符串的首地址(线性地址)通过字符设备传递给内核模块。之后一直循环打印字符串的值。
3。内核模块得到字符串的线性地址后。可以直接修改这个线性地址对应的字符,修改后,发现用户态进程打印的字符内容相应也发生了改变。
4。在内核模块得到线性地址后,接着取cr3寄存器的值,因为cr3的值应该是物理地址,所以将cr3的值+3G,可以读到pgd的基地址,以此类推,按照二级映射的方式,应该最终可以读到应用程序中字符串的线性地址对应到的物理地址。
5。可是最后得到的物理地址明显不对,因为将这个物理地址+3G后访问,其中内容不是字符串的内容?
MMU的映射应该也可以用程序来仿真的。不知到为什么最终结果不对?
请hacker们赐教。。。
>可是最后得到的物理地址明显不对,因为将这个物理地址+3G后访问,其中内容不是字符串的内容?
用户空间的内存地址不是线性映射的,所以简单的+3G,是不会得到该任务线性地址的。
看了上述的回帖,发现似乎没有cover我的真实想法。
可能是我表述的不太清楚或者我的基本概念理解有误,再解释一下:
1。在用户空间(例如用户态进程程序)和内核空间(例如内核模块程序)中,我们在程序中通过指针访问的(指针)地址应该是线性地址,只不过用户态数据的线性地址<3G,而内核态数据的线性地址>3G。
例如,在内核模块中,
char p[100]="hello";
printf("8lx",(unsigned long int)p);
p的值是大于3G的。
同理,在用户态的地址值应该是小于3G的。
2。我不知道在内核中如何直接访问物理地址?所以猜想在内核中可以直接通过(3G+物理地址)来访问物理地址对应的数据。
例如,在内核模块中,
char *p;
p=(char *)0xc0000000; //3G地址对应物理地址0 ?
*p='a';
在执行上述语句后。
我用 dd if=/dev/mem of=/xx bs=4k count=1 将物理内存的开始4k导入到/xx文件,用VI编辑/xx后发现首字节的确改为'a';
当然这里我又假想了/dev/mem就是和物理地址一一对应的?不知道是否如此?
3。从用户进程通过系统调用,将用户态进程的字符窜的首地址(此地址应该是用户态的线性地址)传递给内核模块时,内核模块此时应该是处于进程上下文中,直接用用指针访问该线性地址,可以直接修改用户态的字符串。这一点我已经在程序中得到验证。
这是否说明,如果内核处在某个进程的进程上下文中,可以直接访问该用户进程的线性地址对应的数据?
4。在内核模块的进程上下文中,我可以直接通过用户进程的线性地址来访问数据。这一点很简单。但是,我想在内核模块中,将此线性地址通过页面映射转换成真实的物理地址,然后通过/dev/mem来进行验证。
5。此时,在内核模块得到用户态进程中字符串的线性地址后,取cr3的值,应该得到的是用户态进程的pgd页的首地址,而且该地址应该是物理地址?但是在内核中知道了物理地址,怎样取物理地址的数据呢?(用汇编?)
6。由于i386实际是二级页面映射,pmd不起作用。我假设可以在内核中用(物理地址+3G)来访问物理地址,这样由cr3的值和线性地址的高10位/中10位/低12位偏移,就可以依次得到pgd页的首地址,pte页的首地址,真正物理地址所在页的首地址,最后得到真正的物理地址。
7。得到物理地址后,通过/dev/mem,我发现物理地址对应的数据,并不是用户态进程中的字符串。所以,说明以上地址映射过程不正确?
8。所以想请高手指教,以上过程中那些过程或者概念有误?
不知道这次我讲明白没有???
【 在 linuxman (linuxunil) 的大作中提到: 】
: 我想通过编码方式验证和学习linux的内存映射方式。但发现编完程序后,最终映射出的物理地址不对,一直困惑不解。
: 请大虾指教:
: i386环境
: ...................
没仔细看你的贴子,你最好用一两句话写清楚你的目的,
在没搞清原理以前钻进细节是不好的。
如果你是要在kernel里直接写用户内存。
你可以通读一遍direct I/O的实现,
通常先要get_user_pages确保页已经提交,否则页可能还没提交或者已经被换出。
然后可以通过得到的page指针的page_address来操作。
最近的kernel一般都会要求这些操作在进程的address space之内进行。
【 在 linuxman (linuxunil) 的大作中提到: 】
: 看了上述的回帖,发现似乎没有cover我的真实想法。
: 可能是我表述的不太清楚或者我的基本概念理解有误,再解释一下:
: 1。在用户空间(例如用户态进程程序)和内核空间(例如内核模块程序)中,我们在程序中通过指针访问的(指针)地址应该是线性地址,只不过用户态数据的线性地址<3G,而内核态数据的线性地址>3G。
今早无意中看到指针对齐的讨论,猛然发现我的内核模块printk的指针地址没有对齐,于是就猜想,在二级映射中没有正确的寻址。
果然,发现代码中在pgd和pte加上偏移时,偏移量忘了*4,因为一个pgd页有1024个entry,偏移量从0到1023,如果偏移量不*4的话,就不是指向pte的指针了。
这样一来,内存映射最重要也就是最基本的概念已经搞透彻了。
【 在 linuxman (linuxunil) 的大作中提到: 】
: 看了上述的回帖,发现似乎没有cover我的真实想法。
: 可能是我表述的不太清楚或者我的基本概念理解有误,再解释一下:
: 1。在用户空间(例如用户态进程程序)和内核空间(例如内核模块程序)中,我们在程序中通过指针访问的(指针)地址应该是线性地址,只不过用户态数据的线性地址<3G,而内核态数据的线性地址>3G。
: ................... |
|