BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #57109同步于 2011/10/4
CPP机器人发帖

[合集] 程序在内存中的分布...

shenlei
2011/10/4镜像同步0 回复
☆─────────────────────────────────────☆ ColdZenLeft (Left) 于 (Sat Sep 17 00:48:11 2011) 提到: 有同学要去中兴面试,在那里复习历届考题,看到一道题问“程序在内存中的分布”... 感觉好霸气啊,这东西感觉要说清楚都可以开讲座了,也不知道人家到底要什么答案? ☆─────────────────────────────────────☆ fentoyal (长风长歌) 于 (Sat Sep 17 00:52:01 2011) 提到: 就那几个段吧,大概讲讲就是了 ☆─────────────────────────────────────☆ wodetiandi (啥也不写了~) 于 (Sat Sep 17 02:43:28 2011) 提到: 乱序 ☆─────────────────────────────────────☆ qiuyesuifeng (秋叶随风|Int_so|QiuYe) 于 (Sat Sep 17 07:48:00 2011) 提到: 说一下个人理解,从低地址到高地址, 预留内存地址(操作系统维护的内存地址,不可访问,比如int *a=(int *)100, *a=l,访问这个地址会出现段错误,不太清楚这段内存地址的作用?) 程序代码区(只读,存代码和一些其他的东西,这里其他的东西我不太清楚) data段(存初始化的全局变量和static变量,是不是还有别的?因为看有人说过常量区(const类型和enum类型之类的?)和静态区?这里不确定是包含其中还是有其他的地方存放) bss段(存未初始化的全局变量和static变量) 堆(由低地址向高地址增长,一般new和malloc分配,说下个人理解,在linux中,一般是根据请求内存的大小,会调用brk或者mmap,malloc函数进行了封装,但是这里我不确定malloc和new的关系,是不是new中调用了malloc?也有一种说法是把malloc和new分开,malloc的部分称为自由存储区,这是c语言的说法,c++里面malloc和new请求的内存统一称为堆) 共享库文件(调用的库文件,位于堆和栈之间) 栈(由高地址向低地址增长,个人喜欢理解成倒漏斗,和堆的增长方式相对,对不同的OS来说,栈的初始大小有规定,可以修改,目前默认一般为2M这个数量级?) 在上面存的都是操作系统和内核调用的一些内存地址,不清楚了 这里说的只是虚拟内存地址,虚拟内存和物理内存之间还有专门的虚拟内存技术,以上大概就是我的理解,顺便和大家讨论一下,轻拍~~ ☆─────────────────────────────────────☆ bahuasheng (花生‰) 于 (Sat Sep 17 08:40:41 2011) 提到: ym LS ☆─────────────────────────────────────☆ kaka2w (长期征人游泳跑步) 于 (Sat Sep 17 08:44:43 2011) 提到: 这个要理清了思路,几句话就讲清楚了 【 在 ColdZenLeft (Left) 的大作中提到: 】 : 标 题: 程序在内存中的分布... : 发信站: 北邮人论坛 (Sat Sep 17 00:48:11 2011), 站内 : : 有同学要去中兴面试,在那里复习历届考题,看到一道题问“程序在内存中的分布”... : : 感觉好霸气啊,这东西感觉要说清楚都可以开讲座了,也不知道人家到底要什么答案? : -- : : ※ 来源:·北邮人论坛 http://bbs.byr.cn·[FROM: 59.64.9.*] ☆─────────────────────────────────────☆ security (一切顺利) 于 (Sat Sep 17 08:52:40 2011) 提到: 地址从高到低,依次是操作系统,stack,heap,data,code。data存储全局变量,stack存储局部变量,heap是动态分配的内存。 ☆─────────────────────────────────────☆ wodetiandi (啥也不写了~) 于 (Sat Sep 17 09:19:17 2011) 提到: 看完回复,好像大家说的都是虚拟内存啊,lz不是问得内存么 ☆─────────────────────────────────────☆ muyinvna (天秤座女孩) 于 (Sat Sep 17 09:23:13 2011) 提到: 【 在 qiuyesuifeng 的大作中提到: 】 : 说一下个人理解,从低地址到高地址, : 预留内存地址(操作系统维护的内存地址,不可访问,比如int *a=(int *)100, *a=l,访问这个地址会出现段错误,不太清楚这段内存地址的作用?) : 程序代码区(只读,存代码和一些其他的东西,这里其他的东西我不太清楚) : ................... 我觉得你说的挺有道理的~ ☆─────────────────────────────────────☆ yongang (yongang) 于 (Sat Sep 17 09:23:46 2011) 提到: 画个图更清晰点吧,我们通常说的地址空间是虚拟的,虚拟地址与物理地址的映射是由MMU负责,程序并不可见 ☆─────────────────────────────────────☆ security (一切顺利) 于 (Sat Sep 17 09:28:15 2011) 提到: 物理内存就复杂的多了,page table的存在就是为了使物理内存可以不连续分布,不连续,自然就没有规律了。还有on-demand paging使一部分虚拟内存对应的内容在secondary storage里,并不在内存中,需要时才掉入内存。 这些都不用用户去操心,内核会为你完成的。用户所需要的就是关心关心逻辑地址。 【 在 wodetiandi 的大作中提到: 】 : 看完回复,好像大家说的都是虚拟内存啊,lz不是问得内存么 : -- ☆─────────────────────────────────────☆ antique (糊涂) 于 (Sat Sep 17 09:45:31 2011) 提到: 学习下。 ☆─────────────────────────────────────☆ thonly ({【(||No.25||)】}) 于 (Sat Sep 17 09:50:33 2011) 提到: 看看! ☆─────────────────────────────────────☆ pplly (风中精灵) 于 (Sat Sep 17 09:50:47 2011) 提到: 分虚拟地址 线性地址 物理地址吧 虚拟地址到线性地址的转换一般由软件实现,即OS实现,为的是使对程序员来说有更大的编址空间,这个对程序员透明,但是对操作系统不透明 这个就是所谓的程序在内存中的分布的概念了,程序一般分为text段,data段,bss段,当然还可以有其他段,这要看操作系统运行程序时怎么设计程序的分布情况 然后就是线性地址到物理地址的转换,这个就是由硬件实现,如果页式管理的话就会查询页表进行线性地址到物理地址的转换的过程,或者使用TLB来加快查询时间,这个对程序员和操作系统都是透明的 ☆─────────────────────────────────────☆ myself333 (巴渝人家|毅执追球) 于 (Sat Sep 17 09:53:07 2011) 提到: 不懂的帮顶 【 在 ColdZenLeft (Left) 的大作中提到: 】 : 有同学要去中兴面试,在那里复习历届考题,看到一道题问“程序在内存中的分布” : ... : 感觉好霸气啊,这东西感觉要说清楚都可以开讲座了,也不知道人家到底要什么答案? ☆─────────────────────────────────────☆ kaokao2011 (江南西道|呼呼噜噜|小混混|╮(╯_╰)╭) 于 (Sat Sep 17 10:02:36 2011) 提到: 3L不错啊 ☆─────────────────────────────────────☆ stfairy (大真似伪、大爱无疆的小楼主) 于 (Sat Sep 17 10:16:09 2011) 提到: 转一个 C程序一般分为 1.程序段:程序段为程序代码在内存中的映射.一个程序可以在内存中多有个副本. 2.初始化过的数据:在程序运行值初已经对变量进行初始化的 3.未初始化过的数据:在程序运行初未对变量进行初始化的数据 4.堆(stack):存储局部,临时变量,在程序块开始时自动分配内存,结束时自动释放内存.存储函数的返回指针. 5.栈(heap):存储动态内存分配,需要程序员手工分配,手工释放. 附程序分布图: [upload=1][/upload] #include <stdio.h> int g1=0, g2=0, g3=0; int max(int i) { int m1=0,m2,m3=0,*p_max; static n1_max=0,n2_max,n3_max=0; p_max = (int*)malloc(10); printf("打印max程序地址\n"); printf("in max: 0x%08x\n\n",max); printf("打印max传入参数地址\n"); printf("in max: 0x%08x\n\n",&i); printf("打印max函数中静态变量地址\n"); printf("0x%08x\n",&n1_max); //打印各本地变量的内存地址 printf("0x%08x\n",&n2_max); printf("0x%08x\n\n",&n3_max); printf("打印max函数中局部变量地址\n"); printf("0x%08x\n",&m1); //打印各本地变量的内存地址 printf("0x%08x\n",&m2); printf("0x%08x\n\n",&m3); printf("打印max函数中malloc分配地址\n"); printf("0x%08x\n\n",p_max); //打印各本地变量的内存地址 if(i) return 1; else return 0; } int main(int argc, char **argv) { static int s1=0, s2, s3=0; int v1=0, v2, v3=0; int *p; p = (int*)malloc(10); printf("打印各全局变量(已初始化)的内存地址\n"); printf("0x%08x\n",&g1); //打印各全局变量的内存地址 printf("0x%08x\n",&g2); printf("0x%08x\n\n",&g3); printf("======================\n"); printf("打印程序初始程序main地址\n"); printf("main: 0x%08x\n\n", main); printf("打印主参地址\n"); printf("argv: 0x%08x\n\n",argv); printf("打印各静态变量的内存地址\n"); printf("0x%08x\n",&s1); //打印各静态变量的内存地址 printf("0x%08x\n",&s2); printf("0x%08x\n\n",&s3); printf("打印各局部变量的内存地址\n"); printf("0x%08x\n",&v1); //打印各本地变量的内存地址 printf("0x%08x\n",&v2); printf("0x%08x\n\n",&v3); printf("打印malloc分配的堆地址\n"); printf("malloc: 0x%08x\n\n",p); printf("======================\n"); max(v1); printf("======================\n"); printf("打印子函数起始地址\n"); printf("max: 0x%08x\n\n",max); return 0; } 这个程序可以大致查看整个程序在内存中的分配情况: 可以看出,传入的参数,局部变量,都是在栈顶分布,随着子函数的增多而向下增长. 函数的调用地址(函数运行代码),全局变量,静态变量都是在分配内存的低部存在,而malloc分配的堆则存在于这些内存之上,并向上生长. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在操作系统中,一个进程就是处于执行期的程序(当然包括系统资源),实际上正在执行的程序代码的活标本。那么进程的逻辑地址空间是如何划分的呢? 图1做了简单的说明(Linux系统下的) [upload=2][/upload] 左边的是UNIX/LINUX系统的执行文件,右边是对应进程逻辑地址空间的划分情况。 首先是堆栈区(stack),堆栈是由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。栈的申请是由系统自动分配,如在函数内部申请一个局部变量 int h,同时判别所申请空间是否小于栈的剩余空间,如若小于的话,在堆栈中为其开辟空间,为程序提供内存,否则将报异常提示栈溢出。 其次是堆(heap),堆一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。堆的申请是由程序员自己来操作的,在C中使用malloc函数,而C++中使用new运算符,但是堆的申请过程比较复杂:当系统收到程序的申请时,会遍历记录空闲内存地址的链表,以求寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,此处应该注意的是有些情况下,新申请的内存块的首地址记录本次分配的内存块大小,这样在delete尤其是delete[]时就能正确的释放内存空间。 接着是全局数据区(静态区) (static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 另外文字常量区,常量字符串就是放在这里,程序结束后有系统释放。 最后是程序代码区,放着函数体的二进制代码。 【 在 ColdZenLeft 的大作中提到: 】 : 有同学要去中兴面试,在那里复习历届考题,看到一道题问“程序在内存中的分布”... : 感觉好霸气啊,这东西感觉要说清楚都可以开讲座了,也不知道人家到底要什么答案? : -- : ................... ☆─────────────────────────────────────☆ longlong (梦の点滴) 于 (Sat Sep 17 10:28:50 2011) 提到: LS很霸气。。 ☆─────────────────────────────────────☆ Windmoon1 (与人为善|与己为善) 于 (Sat Sep 17 11:05:28 2011) 提到: 至少要把堆和栈的道理说出来吧~~~~ 我瞎猜的, 苦逼的程序员~ ☆─────────────────────────────────────☆ shenlei (我爱果子|[路]|天山南北|潇湘隐士) 于 (Sat Sep 17 11:08:54 2011) 提到: 又十大...版面要火啊... ps 程序员何苦要为难程序员啊... ☆─────────────────────────────────────☆ ColdZenLeft (Left) 于 (Sat Sep 17 11:48:27 2011) 提到: 其实我觉得这个已经是一个讲座了,学习中... ☆─────────────────────────────────────☆ michael2008 (Life is Magical) 于 (Sat Sep 17 12:01:46 2011) 提到: 貌似和操作系统也有关系,甚至内核的版本变更也在变化,Linux下面虚拟内存,按页分配,实际内存、硬件地址等应该是映射入虚拟内存,在此基础上,内核组织内存,用户程序分到的内存在逻辑上是连续的,但物理上没有联系,这个不能是无规律的,相反是很有规律的,强调逻辑内存而非物理内存。在内核级别分析完之后,才到用户层面,用户的代码当然分段,这个是在虚拟内存的之下进行的了。 个人觉得要把内核级和用户级按层次分析下才能把整个问题说清楚。不过题目要求应该不用说虚拟内存了,直接到用户层面如何组织内存就好,16楼足够清晰了。 ☆─────────────────────────────────────☆ fenixlee520 (流浪剑客) 于 (Sat Sep 17 12:13:50 2011) 提到: 你所看到的都是虚拟地址 ☆─────────────────────────────────────☆ mymz (好久不见) 于 (Sat Sep 17 12:59:25 2011) 提到: bd ☆─────────────────────────────────────☆ suye (烨子) 于 (Sat Sep 17 13:10:25 2011) 提到: 谢谢16L,好好梳理了一下。 ☆─────────────────────────────────────☆ xinguohenan (小水) 于 (Sat Sep 17 13:27:05 2011) 提到: 进来学习 ☆─────────────────────────────────────☆ a206206 (最爱大白腿|肉丝控) 于 (Sat Sep 17 15:22:15 2011) 提到: 0xcfffffff以上是os的。。剩下的分别是站,堆,数据,常量,大概说一下就行,想搞得很清晰就要研究很久 ☆─────────────────────────────────────☆ buptdby (Born to Win) 于 (Sat Sep 17 16:19:18 2011) 提到: 学习学习 ☆─────────────────────────────────────☆ jokerlee (Es gilt viele mauern abzubauen) 于 (Sat Sep 17 16:23:29 2011) 提到: 这个问题分成两个层次,虚拟地址和物理地址 虚拟地址首先被操作系统分区 windows下有空指针区、用户模式区、64k禁入区、内核模式区,对于普通应用程序的内存空间是在用户模式区中(0x00010000~0x7ffeffff),应用程序通过C库和系统api使用的这段虚拟地址 一般来说pe程序的内存布局从低到高是代码、数据、堆、栈,loader将可执行文件及其依赖的动态库按照不同的段load到进程的虚拟内存空间中,text段、data、bss,堆栈空间在程序运行过程中动态的分配和释放。 物理内存方面 物理内存的存储空间是内存+磁盘上的页交换文件,虚拟内存到物理内存的转换通过进程pcb中的页映射表->快表TLB->页表,内存满了换出,page fault了换入,blablabla... 大体如此 ☆─────────────────────────────────────☆ ClearRiver (清河|【人生如河,清者自清,浊者自浊】) 于 (Sat Sep 17 19:12:05 2011) 提到: 建议看《深入理解计算机系统》 ☆─────────────────────────────────────☆ ly517691895 (几何老师) 于 (Sat Sep 17 22:58:32 2011) 提到: 楼上好多大神,说说我的理解吧,我觉得要理解程序在内存中运行的原理,关键应该是对虚拟内存的认识。简单的说,操作系统会映射到程序虚存的最低地址,后面就是程序的各种段了,应该是想考你一些变量所在位置吧?如全局变量在.data段之类的,.bss段装的什么,stack装的是局部变量,函数调用是stack用来传递参数,heap用于动态内存分配。这是虚存的分布,至于虚存到物理内存的映射是硬件完成的,中间还有线性地址的,映射虽是硬件完成,但是需要由操作系统来设置页表和段选择子,段描述符,这个对程序是透明的,编程写程序时,我们是没有分配地址的,在链接阶段,链接器会设定相应的各种地址。C语言的程序段结构看以参考前面那位同学的,估计比较重要的是虚存的概念,面试的时候不能说错了。看看《linker & loader》,真能把这个理解透彻,说清楚了,应该是计算机大神了 ☆─────────────────────────────────────☆ Carrie0593 (包子~) 于 (Sat Sep 17 23:32:24 2011) 提到: 进来学习~这楼好多大牛哇 ☆─────────────────────────────────────☆ handspeaker (handspeaker) 于 (Sun Sep 18 00:19:05 2011) 提到: 顶楼主,楼主大牛 ☆─────────────────────────────────────☆ fentoyal (长风长歌) 于 (Sun Sep 18 12:24:40 2011) 提到: 大家越说面越广了啊。。这题不会问这么深而广吧, 估计再说就要先分64bit 32bit了。。。
订阅后,新回复会通过你的通知中心匿名送达。
0 条回复
暂无回复 · 你可以订阅本帖等待新回复。