如何在用户空间中查看当前用户表空间实际物理地址空

linux系统(64)
linux memory(15)
有这么一系列的问题,是否在困扰着你:用户程序编译连接形成的地址空间在什么范围内?内核编译后地址空间在什么范围内?要对外设进行访问,I/O的地址空间又是什么样的?
先回答第一个问题。Linux最常见的可执行文件格式为elf(Executable and Linkable Format)。在elf格式的可执行代码中,ld总是从0x8000000开始安排程序的“代码段”,对每个程序都是这样。至于程序执行时在物理内存中的实际地址,则由内核为其建立内存映射时临时分配,具体地址取决于当时所分配的物理内存页面。
我们可以用Linux的实用程序objdump对你的程序进行反汇编,从而知晓其地址范围。
例如:假定我们有一个简单的C程序Hello.c
&&# include&stdio.h&
&&greeting ( )
& & & & & &printf(“Hello,world!\n”);
& && && &greeting();
之所以把这样简单的程序写成两个函数,是为了说明指令的转移过程。我们用gcc和ld对其进行编译和连接,得到可执行代码hello。然后,用Linux的实用程序objdump对其进行反汇编:
$objdump –d hello
得到的主要片段为:
&greeting&:
& &8048568:& &&&pushl&&%ebp
& &8048569:& &&&movl&&%esp, %ebp
& &804856b:& &&&pushl&&$0x809404
& &8048570:& &&&call& & 8048474&&&_init+0x84&
& &8048575:& &&&addl& &$0x4, %esp
& &8048578:& &&&leave
& &8048579:& &&&ret
& &804857a:& &&&movl&&%esi, %esi
& &0804857c &main&:
& &804857c:& &&&pushl&&%ebp
& &804857d:& &&&movl&&%esp, %ebp
& &804857f:& &&&call& &8048568&&&greeting&
& &8048584:& &&&leave
& &8048585:& &&&ret
& &8048586:& &&&nop
& &8048587:& &&&nop
其中,像这样的地址,就是我们常说的虚地址(这个地址实实在在的存在,只不过因为物理地址的存在,显得它是“虚”的罢了)。
.虚拟内存、内核空间和用户空间
& Linux虚拟内存的大小为2^32(在32位的x86机器上),内核将这4G字节的空间分为两部分。最高的1G字节(从虚地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。而较低的3G字节(从虚地址0x到0xBFFFFFFF),供各个进程使用,称为“用户空间”。因为每个进程可以通过系统调用进入内核,因此,Linux内核空间由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟地址空间(也叫虚拟内存)。
& &每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的。最高的1GB内核空间则为所有进程以及内核所共享。另外,进程的“用户空间”也叫“地址空间”,在后面的叙述中,我们对这两个术语不再区分。
用户空间不是进程共享的,而是进程隔离的。每个进程最大都可以有3GB的用户空间。一个进程对其中一个地址的访问,与其它进程对于同一地址的访问绝不冲突。比如,一个进程从其用户空间的地址0x1234ABCD处可以读出整数8,而另外一个进程从其用户空间的地址0x1234ABCD处可以读出整数20,这取决于进程自身的逻辑。
任意一个时刻,在一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个4GB的虚拟地址空间,这个虚拟地址空间是面向此进程的。当进程发生切换的时候,虚拟地址空间也随着切换。由此可以看出,每个进程都有自己的虚拟地址空间,只有此进程运行的时候,其虚拟地址空间才被运行它的CPU所知。在其它时刻,其虚拟地址空间对于CPU来说,是不可知的。所以尽管每个进程都可以有4
GB的虚拟地址空间,但在CPU眼中,只有一个虚拟地址空间存在。虚拟地址空间的变化,随着进程切换而变化。
从上面我们知道,一个程序编译连接后形成的地址空间是一个虚拟地址空间,但是程序最终还是要运行在物理内存中。因此,应用程序所给出的任何虚地址最终必须被转化为物理地址,所以,虚拟地址空间必须被映射到物理内存空间中,这个映射关系需要通过硬件体系结构所规定的数据结构来建立。这就是我们所说的段描述符表和页表,Linux主要通过页表来进行映射。
于是,我们得出一个结论,如果给出的页表不同,那么CPU将某一虚拟地址空间中的地址转化成的物理地址就会不同。所以我们为每一个进程都建立其页表,将每个进程的虚拟地址空间根据自己的需要映射到物理地址空间上。既然某一时刻在某一CPU上只能有一个进程在运行,那么当进程发生切换的时候,将页表也更换为相应进程的页表,这就可以实现每个进程都有自己的虚拟地址空间而互不影响。所以,在任意时刻,对于一个CPU来说,只需要有当前进程的页表,就可以实现其虚拟地址到物理地址的转化。
.内核空间到物理内存的映射&&
& &内核空间对所有的进程都是共享的,其中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据,不管是内核程序还是用户程序,它们被编译和连接以后,所形成的指令和符号地址都是虚地址(参见2.5节中的例子),而不是物理内存中的物理地址。
虽然内核空间占据了每个虚拟空间中的最高1GB字节,但映射到物理内存却总是从最低地址(0x)开始的,如图4.2所示,之所以这么规定,是为了在内核空间与物理内存之间建立简单的线性映射关系。其中,3GB(0xC0000000)就是物理地址与虚拟地址之间的位移量,在Linux代码中就叫做PAGE_OFFSET。
&&& && && && && &
我们来看一下在include/asm/i386/page.h头文件中对内核空间中地址映射的说明及定义:
#define__PAGE_OFFSET& && && &&&(0xC0000000)
#definePAGE_OFFSET& && && && & ((unsignedlong)__PAGE_OFFSET)
#define__pa(x)& && && && &&&&&((unsigned long)(x)-PAGE_OFFSET)
#define__va(x)& && && && &&&&&((void *)((unsigned long)(x)+PAGE_OFFSET))
对于内核空间而言,给定一个虚地址x,其物理地址为“x- PAGE_OFFSET”,给定一个物理地址x,其虚地址为“x+ PAGE_OFFSET”。
这里再次说明,宏__pa()仅仅把一个内核空间的虚地址映射到物理地址,而决不适用于用户空间,用户空间的地址映射要复杂得多,它通过分页机制完成。
图1&&从PAGE_OFFSET开始的1GB地址空间&&
先说明图中符号的含义:
PAGE_OFFSET:0XC0000000,即3GB
high_memory:这个变量的字面含义是高端内存,到底什么是高端内存,
内核规定,RAM的前896为所谓的低端内存,而896~1GB共128MB为高端内存。如果你的内存是512M,那么high_memory是多少?是3GB+512,也就是说,物理地址x
在源代码中函数mem_init中,有这样一行:
high_memory = (void *) __va(max_low_pfn *PAGE_SIZE);
其中,max_low_pfn为物理内存的最大页数。
所以在图中,PAGE_OFFSET到high_memory 之间就是所谓的物理内存映射。只有这一段之间,物理地址与虚地址之间是简单的线性关系。
&&还要说明的是,要在这段内存分配内存,则调用kmalloc()函数。反过来说,通过kmalloc()分配的内存,其物理页是连续的。
VMALLOC_START:非连续区的的起始地址。
VMALLOC_END:非连续区的的末尾地址
在非连续区中,物理内存映射的末端与第一个VMalloc之间有一个8MB的安全区,目的是为了“捕获”对内存的越界访问。处于同样的理由,插入其他4KB的安全区来隔离非连续区。
非连续区的分配调用VMalloc()函数。
vmalloc()与 kmalloc()都是在内核代码中用来分配内存的函数,但二者有何区别?
& &从前面的介绍已经看出,这两个函数所分配的内存都处于内核空间,即从3GB~4GB;但位置不同,kmalloc()分配的内存处于 3GB~high_memory之间,这一段内核空间与物理内存的映射一一对应,而vmalloc()分配的内存在VMALLOC_START~4GB之 间,这一段非连续内存区映射到物理内存也可能是非连续的。
& &vmalloc()工作方式与kmalloc()类似, 其主要差别在于前者分配的物理地址无需连续,而后者确保页在物理上是连续的(虚地址自然也是连续的)。
尽 管仅仅在某些情况下才需要物理上连续的内存块,但是,很多内核代码都调用kmalloc(),而不是用vmalloc()获得内存。这主要是出于性能的考虑。vmalloc()函数为了把物理上不连续的页面转换为虚拟地址空间上连续的页,必须专门建立页表项。还有,通过vmalloc()获得的页必须一个 一个的进行映射(因为它们物理上不是连续的),这就会导致比直接内存映射大得多的缓冲区刷新。因为这些原因,vmalloc()仅在绝对必要时才会使用 ——典型的就是为了获得大块内存时,例如,当模块被动态插入到内核中时,就把模块装载到由vmalloc()分配的内存上。
vmalloc()函数用起来比较简单:
buf = vmalloc(16*PAGE_SIZE);&&/*获得16页*/
if (!buf)
& & /* 错误!不能分配内存*/
在使用完分配的内存之后,一定要释放它:
vfree(buf);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:264383次
积分:3363
积分:3363
排名:第8504名
原创:50篇
转载:126篇
评论:29条
(5)(3)(9)(8)(2)(1)(3)(2)(1)(3)(1)(13)(20)(1)(1)(2)(4)(7)(4)(3)(12)(11)(3)(9)(1)(2)(5)(7)(2)(2)(3)(2)(8)(17)(1)(1)后使用快捷导航没有帐号?
查看: 4308|回复: 11
简单问题:如何获取进程空间虚拟地址的实际的物理地址?
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
反之,如果把一个物理地址映射到进程空间虚拟地址?不是MMU那个虚拟地址
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
虽然标示&简单问题&
其实这个问题不好搞,似乎很繁琐,当初也研究过,失败收场
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
没事,讨论一下也可以啊,我目前知道的是,每个进程空间的前4M内容好像和这个有关系。
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
1. ARM Core 的 datasheet 在
网站上全部可以下载, lz 有兴趣可以将 mmu 相关的章节看一遍, 就可以藉由 cp15 去把所有的 virtual address 全部找到对映的 physical address.
2. wince 下有 api, 可以直接得到 physical address 与相对应的 virtual address, lz 可用之.
3. pa=&va, 则用 VirtualAlloc & VirtualCopy 即可.
Paul, Chao @ Techware
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
楼上,我有一个疑问,第一,ce6进程不能使用VirtualAlloc和VirtualCopy,如何转换?如果在驱动层去做这件事情,映射后的虚拟地址并非是进程空间的虚拟地址,如何使用?第二,你说的WinCE API具体是哪一个?
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
向台湾同胞学习!
在线时间7 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
1. CE6 下, ap 对 physical address 是 &耗子拖王八, 没下嘴的地方&
2. HalAllocateCommonBuffer
Paul, Chao @ Techware
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
引用 3 楼 paul_chao 的回复:
1. ARM Core 的 datasheet 在
网站上全部可以下载, lz 有兴趣可以将 mmu 相关的章节看一遍, 就可以藉由 cp15 去把所有的 virtual address 全部找到对映的 physical address.
2. wince 下有 api, 可以直接得到 physical address 与相对应的 virtual address, lz 可用之.
3. pa=&va, 则用 VirtualAlloc & VirtualCopy 即可.
Paul, Chao @ Techware
强,太强了
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
引用 3 楼 paul_chao 的回复:
1. ARM Core 的 datasheet 在
网站上全部可以下载, lz 有兴趣可以将 mmu 相关的章节看一遍, 就可以藉由 cp15 去把所有的 virtual address 全部找到对映的 physical address.
2. wince 下有 api, 可以直接得到 physical address 与相对应的 virtual address, lz 可用之.
3. pa=&va, 则用 VirtualAlloc & VirtualCopy 即可.
Paul, Chao @ Techware
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
paul_chao:
CE6.0 下AP一样可以使用物理地址,不过要在驱动层去实现接口,方法我还没验证,今天我验证后结贴。
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
从虚拟地址得到物理地址在CE5下其实不难,kernel中有一处GetPFN的定义就是干这个用的。
#define ArmHigh ((ARM_HIGH *)0xFFFD0000)
#define FirstPT (ArmHigh-&firstPT)
#define GetPFN(pgpi)&&((FirstPT[(ulong)(pgpi)&&20]&0xFFF00000) \
& && && && && && && && &| ((ulong)(pgpi)&0x000FF000))
所以你大概可以这样用:
DWORD WINAPI DoVirtualAddrToPhysicalAddr(DWORD dwVirtAddress)
& & & & return GetPFN(dwVirtAddress);
DWORD VirtualAddrToPhysicalAddr(DWORD dwVirtAddress)
& & & & PPROCESS pKernProc = (PPROCESS)UserKInfo[KINX_PROCARRAY];
& & & & CALLBACKINFO
& & & & cbi.hProc = pKernProc-&hP
& & & & cbi.pfn = (FARPROC)DoVirtualAddrToPhysicalA
& & & & cbi.pvArg0 = (PVOID)dwVirtA
& & & & return PerformCallBack(&cbi);
在线时间0 小时
TA的帖子TA的资源
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
一粒金砂(初级), 积分 0, 距离下一级还需 5 积分
引用 3 楼 paul_chao 的回复:
1. ARM Core 的 datasheet 在
网站上全部可以下载, lz 有兴趣可以将 mmu 相关的章节看一遍, 就可以藉由 cp15 去把所有的 virtual address 全部找到对映的 physical address.
2. wince 下有 api, 可以直接得到 physical address 与相对应的 virtual address, lz 可用之.
3. pa=&va, 则用 VirtualAlloc & VirtualCopy 即可.
Paul, Chao @ Techware
Powered by
逛了这许久,何不进去瞧瞧?在v6中能访问的虚拟地址空间为64KB,虚拟地址长度为16位,物理地址空间的地址长度在PDP-11/40上为18位共256KB。
MMU由2组寄存器组成,一组在用户态使用,另一组在核心态使用。通过PSW中的15-14位来切换当前模式。
一组由8对寄存器组成。 每一对(PAR、PDR)构成APR。一对寄存器控制一页(8k = 128*64),每页分为128块,每块长为64byte。块长是存储的映照单位,也是存储分配的基本单位。
通过APR(Active Page Register)将虚拟地址转换为物理地址。
基地址(以64byte为单位)
14-8(刚好7位,能表示0-127,共128块)
更新标志。表示页是否被更新
值为1页按高地址向低地址分配
2-1 页的控制:00未分配 01只读 11 读写
虚拟地址15-13:对应了的页PAR寄存器编号为0-7中的一个,
PAR中的0-11位决定了物理地址基地址的块地址 + 虚拟地址的12-6比特得到物理内存的块地址。虚拟地址0-5为块内偏移值。
u为user结构体,是用来管理进程打开的文件或目录等信息。内核要访问正在执行的进程的u值,为了解决这个问题,内核态下的那组APR专门拿了一对寄存器来转换u的物理地址值。
内核之所以可以通过全局变量u(进制)访问执行进程的user结构体,是因为内核对供内核模式使用的APR做了相应的设定。内核将内核模式使用的编号为6的APR设为了执行进程的数据段的物理地址(proc.p_addr)。
虚拟地址表示为8进制) = 001 100 000 000 000 000 取16位 = 00 0000 其中高3位110 = 6 对应的就是APR为6的那一对寄存器,由于低位全为0,所以u指向内核空间第6页的起始地址。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:211次
排名:千里之外

我要回帖

更多关于 查看用户的表空间 的文章

 

随机推荐