此文章是围绕该文章 的思路进行总结的,原文记录的笔记详尽,在此基础上,我总结了本身的一下(想对浅显)。
若是对具体细节想要了解的,能够访问这篇文章,在文章最后付了一段代码,跟该成程序的内存映射图,若是有哪里写的不正确,欢迎补充和指正。linux
程序是什么
抽象的来讲当咱们打开电脑点击应用程序图标,一个程序就运行起来了,
可是在操做系统层面来看,实际上是产生了一个进程 ,这是一个程序的实体。ios
接下来以linux操做系统来介绍linux中进程的各个段和其表明的含义c++
linux虚拟地址空间
在linux操做系统中,每当生成一个进程时,它就具备了一个虚拟地址空间,一般在32位系统中,它的大小为4GB,按3:1的比例分配给用户空间和内核空间。程序员
虚拟地址空间的简单工做原理就是将虚拟地址经过页表映射到实际物理地址(因此在c语言中malloc分配内存时,即便是malloc一个int大小的空间,可是其实是分配了一个页的内存,通常是4096个字节)编程
用户内存空间的各个段分布
代码段:可执行的代码,字符串常量等
数据段: 1.数据段:已初始化的,且初值为非0的全局变量(static变量)
2.bss段:未初始化,或者初值为0的全局变量 (static变量)
堆:能够理解为程序员在程序中本身为变量分配的内存(需手动释放)
栈:保存了函数被调用时所须要的局部变量,函数参数,返回地址。
该进程在派生出不少线程时,线程栈也保存在这里。
栈
栈说到底也是一种数据结构,它遵循着先进后出的顺序,因此特别适合函数的执行(进栈),函数返回(出栈)。
堆栈的用途分三种:
1.为函数内部声明的局部变量(非静态局部变量)分配内存空间。
2.记录函数调用信息,用栈帧表示。除此以外还有函数的返回地址,一些进出寄存器的变量。
3.临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存。(不是很了解)数据结构
在此基础上,因此会常常引起一个编程上的问题,即:栈溢出, 这种状况在递归调用中出现的尤为多,
除此以外还有申请了内存,没有释放,也极可能形成内存泄露。避免这种状况,就须要本身注意内存的
释放,通常一个new对应一个的delete。多线程
在linux中能够用ulimit -s 来查看堆栈的大小,下面的是我linux系统中的堆栈大小架构
[m@m ~]$ ulimit -s
8192
堆
堆是程序运行时动态分配内存的内存段,它一般用malloc和new(底层也是malloc)来得到一段内存空间,用
free和delete来释放已经使用完毕的内存空间。app
常见问题:
因为是经过程序员本身手动建立,因此必定要注意内存释放,问题,在类,main函数中动态分配的内存,
通常都能释放,可是有时在其余函数中new出来的内存空间,、并无delete掉,致使内存泄露。
屡次运行函数,可能程序会崩溃。函数
#include
int* exp(int i){
int *ptr = new int(i);
return ptr;
}
int main(){
int a = 1;
int *p = exp(a);
// delete ptr; // 错误! 因为函数中的ptr已经被回收,因此不能delete ptr
delete p; // 释放定义在函数外部的指针是正确的
p = NULL;
return 0;
}
下面这句话是引用:
使用堆时常常出现两种问题:1) 释放或改写仍在使用的内存(“内存破坏”);2)未释放再也不使用的内存(“内存泄漏”)。当释放次数少于申请次数时,可能已形成内存泄漏。泄漏的内存每每比忘记释放的数据结构更大,由于所分配的内存一般会圆整为下个大于申请数量的2的幂次(如申请212B,会圆整为256B)。
代码段
代码段也称正文段或文本段,一般用于存放程序执行代码(即CPU执行的机器指令)。通常C语言执行语句都编译成机器代码保存在代码段。一般代码段是可共享的,所以频繁执行的程序只须要在内存中拥有一份拷贝便可。代码段一般属于只读,以防止其余程序意外地修改其指令(对该段的写操做将致使段错误)。某些架构也容许代码段为可写,即容许修改程序。
数据段
数据段一般用于存放程序中已初始化且初值不为0的全局变量(也包括静态全局变量)和静态局部变量
bss段
bss段一般存放如下两种状况:
1.未初始化的全局变量(静态全局变量)和局部静态变量
2.初始化为0的全局变量(静态全局变量)和静态局部变量
内存映射区
除了这些以外,夹在stack和heap之间还有一个内存映射段(mmap
如下三段为引用
此处,内核将硬盘文件的内容直接映射到内存, 任何应用程序均可经过Linux的mmap()系统调用或Windows的CreateFileMapping()/MapViewOfFile()请求这种映射。内存映射是一种方便高效的文件I/O方式, 于是被用于装载动态共享库。用户也可建立匿名内存映射,该映射没有对应的文件, 可用于存放程序数据。在 Linux中,若经过malloc()请求一大块内存,C运行库将建立一个匿名内存映射,而不使用堆内存。”大块” 意味着比阈值 MMAP_THRESHOLD还大,缺省为128KB,可经过mallopt()调整。
最后附上一段本身写的代码用来查看内存布局
Linux下在/proc/进程号/maps 查看内存映射
源代码:
#include
void get(){
int e;
int f = 4;
static int g;
static int h = 5;
std::cout << "in get(), the address a is " << &e << std::endl;
std::cout << "in get(), the address b is " << &f << std::endl;
std::cout << "in get(), the address c is " << &g << std::endl;
std::cout << "in get(), the address d is " << &h << std::endl;
while(1); // 让程序死在这用来查看内存映射表
}
int main(){
int a = 1;
int b;
static int c = 2;
static int d;
int *p = new int(3);
std::cout << "in main, the address a is " << &a << std::endl;
std::cout << "in main, the address b is " << &b << std::endl;
std::cout << "in main, the address c is " << &c << std::endl;
std::cout << "in main, the address d is " << &d << std::endl;
std::cout << "in main, the address p is " << p << std::endl;
get(); // 程序在这进入死循环
delete p;
p = NULL;
return 0;
}
运行结果:
in main, the address a is 0x7ffd270384a8
in main, the address b is 0x7ffd270384a4
in main, the address c is 0x602074
in main, the address d is 0x60219c
in main, the address p is 0x1c06c20
in get(), the address a is 0x7ffd2703844c
in get(), the address b is 0x7ffd27038448
in get(), the address c is 0x602198
in get(), the address d is 0x602070
在内存中,各个变量所处在各个段的虚拟地址:
[m@m ~/practice]$ cat /proc/16138/maps
00400000-00401000 r-xp 00000000 08:06 2364212 /home/m/practice/12.out
00601000-00602000 r--p 00001000 08:06 2364212 /home/m/practice/12.out
00602000-00603000 rw-p 00002000 08:06 2364212 /home/m/practice/12.out
01bf5000-01c27000 rw-p 00000000 00:00 0 [heap]
7fa3c231b000-7fa3c24b0000 r-xp 00000000 08:07 2361747 /lib/x86_64-linux-gnu/libc-2.24.so
7fa3c24b0000-7fa3c26b0000 ---p 00195000 08:07 2361747 /lib/x86_64-linux-gnu/libc-2.24.so
7fa3c26b0000-7fa3c26b4000 r--p 00195000 08:07 2361747 /lib/x86_64-linux-gnu/libc-2.24.so
7fa3c26b4000-7fa3c26b6000 rw-p 00199000 08:07 2361747 /lib/x86_64-linux-gnu/libc-2.24.so
7fa3c26b6000-7fa3c26ba000 rw-p 00000000 00:00 0
7fa3c26ba000-7fa3c26d0000 r-xp 00000000 08:07 2359364 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa3c26d0000-7fa3c28cf000 ---p 00016000 08:07 2359364 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa3c28cf000-7fa3c28d0000 r--p 00015000 08:07 2359364 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa3c28d0000-7fa3c28d1000 rw-p 00016000 08:07 2359364 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fa3c28d1000-7fa3c29d4000 r-xp 00000000 08:07 2361751 /lib/x86_64-linux-gnu/libm-2.24.so
7fa3c29d4000-7fa3c2bd3000 ---p 00103000 08:07 2361751 /lib/x86_64-linux-gnu/libm-2.24.so
7fa3c2bd3000-7fa3c2bd4000 r--p 00102000 08:07 2361751 /lib/x86_64-linux-gnu/libm-2.24.so
7fa3c2bd4000-7fa3c2bd5000 rw-p 00103000 08:07 2361751 /lib/x86_64-linux-gnu/libm-2.24.so
7fa3c2bd5000-7fa3c2d47000 r-xp 00000000 08:07 3676435 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
7fa3c2d47000-7fa3c2f47000 ---p 00172000 08:07 3676435 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
7fa3c2f47000-7fa3c2f51000 r--p 00172000 08:07 3676435 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
7fa3c2f51000-7fa3c2f53000 rw-p 0017c000 08:07 3676435 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.24
7fa3c2f53000-7fa3c2f56000 rw-p 00000000 00:00 0
7fa3c2f56000-7fa3c2f79000 r-xp 00000000 08:07 2359411 /lib/x86_64-linux-gnu/ld-2.24.so
7fa3c3151000-7fa3c3155000 rw-p 00000000 00:00 0
7fa3c3176000-7fa3c3179000 rw-p 00000000 00:00 0
7fa3c3179000-7fa3c317a000 r--p 00023000 08:07 2359411 /lib/x86_64-linux-gnu/ld-2.24.so
7fa3c317a000-7fa3c317b000 rw-p 00024000 08:07 2359411 /lib/x86_64-linux-gnu/ld-2.24.so
7fa3c317b000-7fa3c317c000 rw-p 00000000 00:00 0
7ffd27018000-7ffd2703b000 rw-p 00000000 00:00 0 [stack]
7ffd2704a000-7ffd2704c000 r--p 00000000 00:00 0 [vvar]
7ffd2704c000-7ffd2704e000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]