(一)进程空间分布图:
一个linux进程的虚拟地址空间分布如下图所示,分为用户空间和内核空间,对于一个32位操作系统来说,4GB的空间分成两部分,低地址的0~3G给用户空间,高地址的3G~4G给内核空间
(二) 用户空间
2.1 只读数据段(.rodata,又叫做常量数据段)
存放只读数据(字符串常量和const修饰的全局变量);const修饰的全局变量是存放在常量段的,但是使用const修饰的局部变量不存放在常量段,存放在栈段;为了提高空间的利用率,有些系统中.rodata段是多个进程共享的;程序加载运行时,.rodata和.text通常合并到一个段(Text Segment)中,操作系统将这个段只读保护起来,防止意外的改写
2.2 代码段(.text)
程序代码在内存中的映射,存放函数体的二进制代码;
2.3 已初始化过的数据段(.data)
数据段保存在源代码中已经初始化的静态变量的内容。数据段不是匿名的,它映射了一部分的程序二进制镜像,也就是源代码中指定了初始值的静态变量。
2.4 未初始化过的数据(.bss)
BSS保存的是未被初始化的静态变量内容,他们没有直接在程序的源码中被赋值。BSS内存区域是匿名的,它不映射到任何文件。.data和.bss在程序加载时合并到一个段(Data Segment)中,这个段是可读可写的。
2.5 堆(heap)
堆用于运行时内存分配;但不同于栈的是,堆用于存储那些生存期与函数调用无关的数据。大部分语言都提供了堆管理功能。在C语言中,堆分配的接口是malloc()函数。如果堆中有足够的空间来满足内存请求,它就不需要内核参与,否则,堆会被扩大,通过brk()系统调用来分配请求所需的内存块。
2.6 内存映射段
常被用来加载共享库(动态库);内存映射:将虚拟内存空间与磁盘上的文件关联起来,这个过程叫内存映射
2.7 栈(stack)
栈用于存储函数参数和局部变量。在程序中调用一个方法或函数会将一个新的栈帧(stack frame)压入到栈中,这个栈帧会在函数返回时被清理掉。栈中数据遵守先进先出的原则,有一个指针指向栈的顶端用来追踪栈中的内容,可以再较短的时间内完成数据压栈和退栈(popping)。进程中的每一个线程都有属于自己的栈。
【当通过不断向栈中压入数据,超出其容量就会耗尽栈所对应的内存区域,这将触发一个页故障(page fault),而被Linux的expand_stack()处理,它会调用acct_stack_growth()来检查是否还有合适的地方用于栈的增长。如果栈的大小低于限制的大小RLIMIT_STACK(通常为8MB),那么一般情况下栈会被加长,程序继续执行,感觉不到发生了什么事情。这是一种将栈扩展到所需大小的常规机制。然而,如果达到了最大栈空间的大小,就会栈溢出(stack overflow),程序收到一个段错误的提示。 动态栈增长是唯一一种访问未映射内存区域而被允许的情形,其他任何对未映射内存区域的访问都会触发页错误,从而导致段错误。一些被映射的区域是只读的,因此企图写这些区域也会导致段错误】
** 栈和堆的区别**:
(三)内核空间
3.1 内核代码段
存放内核的代码和数据,所有进程的内核代码段都映射到同样的物理内存,并在内存中持续存在。
2.2 与进程有关的数据结构段
存放进程各自的pcb等,并映射到不同的物理内存;
参考文章:
https://www.cnblogs.com/Joezzz/p/9803344.html
https://blog.youkuaiyun.com/cl_linux/article/details/80328608