『壹』 linux进程虚拟地址空间的分布,以及堆和栈的区别
一、具体分布如图所示:
二、关于堆和栈
(1)分配方式:
栈:由编译器自动分配释放,存放函数的参数回值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆: 一般由程序员分配释放,它的分配方式类似于链表。
(2)申请后系统的响应:
栈:只要所申请的空间小于栈的剩余空间,则系统为程序分配内存,否则栈溢出。
堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,遍历该链表,找出第一个大于所申请空间的节点,然后将其从链表中删除并分配,如果没用完,则系统会把多余的重新放回到链表中。
(3)申请大小的限制:
栈:栈答是高地址向低地址扩展的连续内存,栈的大小一般是2M;
堆:堆是低地址向高地址扩展的不连续内存,堆的大小与计算机有效的虚拟内存有关系。
(4)申请效率:
栈:由系统自动分配,速度较快;
堆:速度慢,容易产生内存碎片;
关于Linux命令的介绍,看看《linux就该这么学》,具体关于这一章地址3w(dot)linuxprobe/chapter-02(dot)html.
『贰』 64位的linux上单个进程可以使用的最大内存数量是多少
对于64位Linux允许多达128个TB单个进程的虚拟地址空间,并且能够解决大约64 TB的物理存储器,但版是受处理器和系权统的限制。原话:64-bit Linux allows up to 128 TB of virtual address space for indivial processes, and can address approximately 64 TB of physical memory, subject to processor and system limitations.只要你的机器有足够的内存统统可以给你放倒。连银河曙光都能放下你的机器肯定不在话下:)而理论值更高,一个64位的微处理器可寻址存储器16 EB(1TB=1024GB,1PB=1024TB,1EB=1024PB自己换算)。顺便说一下64位的windows企业服务器最大2TB内存。
『叁』 Linux 的虚拟内存管理有几个关键概念
Linux 的虚拟内存管理有几个关键概念:
1、每个进程都有独立的虚拟地址版空间,进程访问权的虚拟地址并不是真正的物理地址;
2、虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址空间)与物理地址进行映射,获得真正物理地址;
3、如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表;如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中。
『肆』 linux 虚拟地址,到底怎么理解
不是仅仅 Linux 是这么设计的,整个现代流行的操作系统都是这么设计的。
应用程序被读入内存后,为了保证系统的统一性,所有的程序都有同样的一套寻址规范。这个寻址就是虚拟地址。这个虚拟地址是系统提供转换的,不是程序的工作。
如果系统不提供这个功能,那么应用程序就需要自己去寻找没有被使用的内存,以及还要自己去处理内存容量的问题,而且如果程序调用外部的一些函数库,这些函数库也需要分配内存,这会导致应用程序的设计难度非常大,每个应用程序实际上就是一个操作系统了。多个程序共同运行导致内存使用混乱也很容易出现。
应用程序申请内存,使用的是操作系统的内存分配功能。这样操作系统可以根据实际情况给应用程序内存,程序不需要考虑因为内存位置不同而必须不同编写的难度。而且操作系统还可以提供虚拟内存等等各种方式来扩充内存,这样的内存对于应用程序来说是不需要考虑的,一切都有系统打理。
使用虚拟地址后,对于应用程序来说,他的内存使用不需要考虑其他的程序占用,也不需要考虑内存容量的问题,也不需要考虑内存块位置,函数库的调用也都扔给操作系统打理。这使得应用程序不需要考虑具体如何管理内存,只需要考虑作为应用程序的应用部分。
而且,因为内存是虚拟的,应用程序一些函数调用,操作系统可以把多个应用程序的调用都用同一套数据来处理,这样,既可以节约内存使用(就是启动100个应用程序,也只需要内存里有一套函数库而已),也可以做到外部函数库和应用程序没有直接关联,纯粹是由系统做虚拟地址过渡。
至于为什么 4G ,这是传统+一些兼容的考虑。
以前没有这个技术时,每个程序都可以完全使用整个系统,整个空间是连续的。到了这种虚拟地址的方式后,每个程序还是有自己“独立”的一整套内存地址。但每个程序内存使用量肯定不一样。那么多少内存空间才完全够用呢?当时因为正好使用了 32 位系统。那么就把整个 32 位环境支持的 4G 内存容量作为这个极限。
不过因为内存地址是虚拟的。实际应用程序要用内存,是需要先申请的,所以只有程序申请后,真实内存才会被占用。这个 4G 只是在算法上作为极限。
不过因为 4G 也是硬件极限。所以 4G 以外的地址都是不能使用的,这就导致另一个问题,一些硬件有存储器,有些硬件需要存储空间做交互(比如 PCI ,比如各种硬件,比如 AGP 显卡)。这些存储区域怎么处理?
所以,Windows Vista 的 32 位版在 4G 内存的机器上曾经报出只有 3.5G (有的机器甚至只有 3.25G 可以用)。就是这个问题的解决办法导致的:把硬件的内存用虚拟地址的方式,放到虚拟地址的最后面。这样应用程序调用硬件存储时,可以直接按照内存的方式读写。这样应用程序就很好的统一了存储界面:只有 4G 的内存范围,不存在其他方式的存储调用方式(硬盘需要用读写功能读取到内存后才能处理,而不是直接进行处理)。这样应用程序的开发就很简单,而且整个内存的使用每个程序都一样。不存在各种硬件的原因而不同导致的需要重新设计内存管理算法。操作系统也能根据实际应用程序的需要随时分配数据,也可以根据每个程序的运行情况,区别的提供物理内存或者虚拟的内存。
这么设计最大的一个好处是,硬件环境和应用程序是无关的,中间由操作系统做转换。而且应用程序互相之间也没有影响,就好象整个内存都由他自己一个程序使用一样。
PS:说了半天,我发现我自己也说不清楚其中的缘由……
『伍』 linux:谁能给我解释下虚拟地址和物理地址的联系
这个问题很大。。。。我尽自己所能给你解释一下吧,如果你不能完全看懂,以后可以回头再翻翻来看。关于虚拟内存的事情,大概是这样的:
首先你要明确什么是虚拟内存。虚拟内存实际上是操作系统对于内存管理的一种方式,比如说,对每个程序而言,它的内存编址都从0x00到0xff,但是实际上,这些内存对应的物理地址,应用程序本身是无法知道的,在这里就可以理解成操作系统对内存管理的一层抽象。
比如,可能进程init的虚拟地址0x00对应了物理地址的0x10,而kthreadd的虚拟地址0x00对应物理地址0x20,etc.
而且虚拟内存也是一种有效的进程间隔离的方式,极大的提升了进程的安全性和操作系统的稳定性,也就是我一个进程不管做什么,都是在它自己的地址空间里做的,不会影响到其他进程和OS。
当然这是理想情况,实际上还有进程间通信啦之类,这就不在这个问题的范围之内了。
而具体怎么把这些虚拟地址对应到物理地址上,这是操作系统做的事情,估计这个也就是你的问题。
----以上是背景1-----
然后我要明确一下:地址总线4位的意思是说内存用4个bit位来表达地址,所以能够index的地址位就是2^0-2^4,也就是0x0到0xf,就是16个bit的内存空间。
然后我们再来细化一下你的例子,就比方说在你的16bit的内存的机器上有1个OS,上面跑着2个程序。一般来说OS会保留地址的高位,比如11-15bit的位置,作为kernel space;然后0-10bit是user space。
在以上的前提下,虚拟内存的效果是:在每一个程序看来,这个程序都有0x0到0xf的地址可以用,并且它们的0xb-0xf都是shared kernel space,然后0x0-0xa都是自己的user space,这样仿佛就有了32个bit的地址一样。这就是你所谓的是用虚拟地址可以使总的地址操作物理地址。至于os是怎么做到这点的,继续往下看。
-----以上是背景2-----
操作系统对每一个进程有一个进程控制块,叫PCB,Process Control Block,里边存储了每一个进程的进程信息,比如说寄存器,file descriptor,还有我们最关心的内存映射信息。每一个进程有一个递增的id号,叫pid,也就是Process IDentifier.
-----以上是背景3-----
进程间切换,也就是说,比如说你一个系统只有1个CPU,但是有两个进程要跑,而且要让我们看起来好像是两个进程同时在跑一样。为什么我要提到这个呢,后面继续看。
-----以上是背景4-----
好,现在来讲如何把虚拟地址映射到物理地址。从程序的角度来看,从malloc开始讲起,比如,在某一时刻,一个进程调用了malloc,在堆(heap)上申请了2bits的空间。实际上这个行为的流程是,程序调用malloc,进入内核模式之后,调用mmap,如果成功,操作系统会从物理地址上取一块2bits的内存,交给应用程序编入虚拟地址空间。更详细一点说,每个进程对内存管理是一个红黑树的结构,也就是说,在每一个进程的PCB,里维护了一颗红黑树,然后动态的将所有的新分配的内存加到这个红黑树里边,以保证程序对每一块内存的访问时间是差不多的。然后不知道你们教材中有没有提到页表(page table),页表也是PCB中的一项,你们教材中应该会对页表有详细的讲解,将如何对内存的地址进行换算,之类的。然后你要明确,页表实际上是红黑树的cache,这样可以加速程序对于常用的内存的访问速度。
以上是操作系统对内存管理的一个大致概括,就是一块物理的内存如何映射成为一块虚拟的内存。
我在背景2中说,两个程序都看到自己有16个bit的虚拟地址,总共有32bit,但是实际上硬件只有16bits,也就是说,不管你在红黑树和页表中怎么映射,一定会有冲突发生,比如,可能物理地址的0x02对应了进程1中的0x04,又在进程2的PCB中映射到了pid2的虚拟地址位0x06上。操作系统如何解决这个矛盾呢,首先在进程pid 1运行的时候,这个0x02对应的是pid1中的0x04;然后这个时候进程切换发生了,pid 2开始运行。当pid2需要用到它的0x04时,os发现0x02这个地址在pid1中也有映射,于是它就把0x02这个地址上的内容存到硬盘上的一个叫swap的空间内,然后把这个地址交给pid2使用。这样就达到了扩大虚拟地址的效果。
但是这样做是有代价的,因为一旦这个page被swap出去,那么在pid1再来调用的时候会发生一系列的miss,从L1 cache miss到 L2 cache miss到L3 cache miss,然后页表miss,memory miss,会对程序的性能造成极大的影响。影响有多大呢,平均来说:
L1 cache hit: 0.5ns
L2 cache hit: 7ns
主内存引用:100ns
顺序从内存中读取1MB:250,000ns
硬盘寻道:10,000,000ns
从硬盘上顺序读取1MB:30,000,000ns
所以你就可以知道这种行为是以极大的性能为代价的。
----讲完啦-----
总的来说这个很大的话题,我刚才所写的东西的就是试图让你对虚拟内存这个东西有一个基本的概念,然后大致的了解内存是如何映射的。就我现在能想到的,对这个虚拟内存话题的讨论还包括多级页表,进程间隔离&通信以及memory fragment。
个人水平有限,如果以上有什么地方说错的或者遗漏的,还请各位多多补充和批评,谢谢。
『陆』 Linux下怎样在进程中获取虚拟地址对应的物理地址
Linux文件目录中的/proc记录着当前进程的信息,称其为虚拟文件系统。在/proc下有一个链接目录名为self,这意味着哪一个进程打开了它,self中存储的信息就是所链接进程的。self中有一个名为page_map的文件,专门用来记录所链接进程的物理页号信息。这样通过/proc/pid/page_map文件,允许一个用户态的进程查看到每个虚拟页映射到的物理页
/proc/pid/page_map中的每一项都包含了一个64位的值,这个值内容如下所示。每一项的映射方式不同于真正的虚拟地址映射,其文件中遵循独立的对应关系,即虚拟地址相对于0x0经过的页面数是对应项在文件中的偏移量
* /proc/pid/pagemap. This file lets a userspace process find out which
physical frame each virtual page is mapped to. It contains one 64-bit
value for each virtual page, containing the following data (from
fs/proc/task_mmu.c, above pagemap_read):
* Bits 0-54 page frame number (PFN) if present//present为1时,bit0-54表示物理页号
* Bits 0-4 swap type if swapped
* Bits 5-54 swap offset if swapped
* Bit 55 pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
* Bit 56 page exclusively mapped (since 4.2)
* Bits 57-60 zero
* Bit 61 page is file-page or shared-anon (since 3.5)
* Bit 62 page swapped
* Bit 63 page present//如果为1,表示当前物理页在内存中;为0,表示当前物理页不在内存中
在计算物理地址时,只需要找到虚拟地址的对应项,再通过对应项中的bit63判断此物理页是否在内存中,若在内存中则对应项中的物理页号加上偏移地址,就能得到物理地址
通过程序获取物理地址并验证写时拷贝技术
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
//计算虚拟地址对应的地址,传入虚拟地址vaddr,通过paddr传出物理地址
void mem_addr(unsigned long vaddr, unsigned long *paddr)
{
int pageSize = getpagesize();//调用此函数获取系统设定的页面大小
unsigned long v_pageIndex = vaddr / pageSize;//计算此虚拟地址相对于0x0的经过的页面数
unsigned long v_offset = v_pageIndex * sizeof(uint64_t);//计算在/proc/pid/page_map文件中的偏移量
unsigned long page_offset = vaddr % pageSize;//计算虚拟地址在页面中的偏移量
uint64_t item = 0;//存储对应项的值
int fd = open("/proc/self/pagemap", O_RDONLY);。。以只读方式打开/proc/pid/page_map
if(fd == -1)//判断是否打开失败
{
printf("open /proc/self/pagemap error
");
return;
}
if(lseek(fd, v_offset, SEEK_SET) == -1)//将游标移动到相应位置,即对应项的起始地址且判断是否移动失败
{
printf("sleek error
");
return;
}
if(read(fd, &item, sizeof(uint64_t)) != sizeof(uint64_t))//读取对应项的值,并存入item中,且判断读取数据位数是否正确
{
printf("read item error
");
return;
}
if((((uint64_t)1 << 63) & item) == 0)//判断present是否为0
{
printf("page present is 0
");
return ;
}
uint64_t phy_pageIndex = (((uint64_t)1 << 55) - 1) & item;//计算物理页号,即取item的bit0-54
*paddr = (phy_pageIndex * pageSize) + page_offset;//再加上页内偏移量就得到了物理地址
}
const int a = 100;//全局常量
int main()
{
int b = 100;//局部变量
static c = 100;//局部静态变量
const int d = 100;//局部常量
char *str = "Hello World!";
unsigned long phy = 0;//物理地址
char *p = (char*)malloc(100);//动态内存
int pid = fork();//创建子进程
if(pid == 0)
{
//p[0] = '1';//子进程中修改动态内存
mem_addr((unsigned long)&a, &phy);
printf("pid = %d, virtual addr = %x , physical addr = %x
", getpid(), &a, phy);
}
else
{
mem_addr((unsigned long)&a, &phy);
printf("pid = %d, virtual addr = %x , physical addr = %x
", getpid(), &a, phy);
}
sleep(100);
free(p);
waitpid();
return 0;
}
测试结果如下:
全局常量:符合写时拷贝技术
子进程修改动态内存
*其实想要知道虚拟地址对应的物理地址,通过这样的方式也可以得到物理地址而不用操作MMU。。。*
以上就是Linux下怎样在进程中获取虚拟地址对应的物理地址的全文介绍,希望对您学习和使用linux系统开发有所帮助.