大内高手—内存模型
了解linux的内存模型,或许不能让你大幅度提高编程能力,但是作为一个基本知识点应该熟悉。坐火车外出旅行时,即时你对沿途的地方一无所知,仍然可以到达目标地。但是你对整个路途都很比较清楚的话,每到一个站都知道自己在哪里,知道当地的风土人情,对比一下所见所想,旅程可能更有趣一些。
类似的,了解linux的内存模型,你知道每块内存,每个变量,在系统中处于什么样的位置。这同样会让你心情愉快,知道这些,有时还会让你的生活轻更松些。看看变量的地址,你可以大致断定这是否是一个有效的地址。一个变量被破坏了,你可以大致推断谁是犯罪嫌疑人。
Linux的内存模型,一般为:
地址
|
作用
|
说明
|
>=0xc000 0000
|
内核虚拟存储器
|
用户代码不可见区域
|
<0xc000 0000
|
Stack(用户栈)
|
ESP指向栈顶
|
|
↓
↑
|
空闲内存
|
>=0x4000 0000
|
文件映射区
|
|
<0x4000 0000
|
↑
|
空闲内存
|
|
Heap(运行时堆)
|
通过brk/sbrk系统调用扩大堆,向上增长。
|
|
.data、.bss(读写段)
|
从可执行文件中加载
|
>=0x0804 8000
|
.init、.text、.rodata(只读段)
|
从可执行文件中加载
|
<0x0804 8000
|
保留区域
|
|
很多书上都有类似的描述,本图取自于《深入理解计算机系统》p603,略做修改。本图比较清析,很容易理解,但仍然有两点不足。下面补充说明一下:
1. 第一点是关于运行时堆的。
为说明这个问题,我们先运行一个测试程序,并观察其结果:
#include <stdio.h>
int main(int argc, char* argv[])
{
int first = 0;
int* p0 = malloc(1024);
int* p1 = malloc(1024 * 1024);
int* p2 = malloc(512 * 1024 * 1024 );
int* p3 = malloc(1024 * 1024 * 1024 );
printf("main=%p print=%p\n", main, printf);
printf("first=%p\n", &first);
printf("p0=%p p1=%p p2=%p p3=%p\n", p0, p1, p2, p3);
getchar();
return 0;
}
|
运行后,输出结果为:
main=0x8048404 print=0x8048324
first=0xbfcd1264
p0=0x9253008 p1=0xb7ec0008 p2=0x97ebf008 p3=0x57ebe008
l main和print两个函数是代码段(.text)的,其地址符合表一的描述。
l first是第一个临时变量,由于在first之前还有一些环境变量,它的值并非0xbfffffff,而是0xbfcd1264,这是正常的。
l p0是在堆中分配的,其地址小于0x4000 0000,这也是正常的。
l 但p1和p2也是在堆中分配的,而其地址竟大于0x4000 0000,与表一描述不符。
原因在于:运行时堆的位置与内存管理算法相关,也就是与malloc的实现相关。关于内存管理算法的问题,我们在后继文章中有详细描述,这里只作简要说明。在glibc实现的内存管理算法中,Malloc小块内存是在小于0x4000 0000的内存中分配的,通过brk/sbrk不断向上扩展,而分配大块内存,malloc直接通过系统调用mmap实现,分配得到的地址在文件映射区,所以其地址大于0x4000 0000。
从maps文件中可以清楚的看到一点:
00514000-00515000 r-xp 00514000 00:00 0
00624000-0063e000 r-xp 00000000 03:01 718192 /lib/ld-2.3.5.so
0063e000-0063f000 r-xp 00019000 03:01 718192 /lib/ld-2.3.5.so
0063f000-00640000 rwxp 0001a000 03:01 718192 /lib/ld-2.3.5.so
00642000-00766000 r-xp 00000000 03:01 718193 /lib/libc-2.3.5.so
00766000-00768000 r-xp 00124000 03:01 718193 /lib/libc-2.3.5.so
00768000-0076a000 rwxp 00126000 03:01 718193 /lib/libc-2.3.5.so
0076a000-0076c000 rwxp 0076a000 00:00 0
08048000-08049000 r-xp 00000000 03:01 1307138 /root/test/mem/t.exe
08049000-0804a000 rw-p 00000000 03:01 1307138 /root/test/mem/t.exe
09f5d000-09f7e000 rw-p 09f5d000 00:00 0 [heap]
57e2f000-b7f35000 rw-p 57e2f000 00:00 0
b7f44000-b7f45000 rw-p b7f44000 00:00 0
bfb2f000-bfb45000 rw-p bfb2f000 00:00 0 [stack]
|
2. 第二是关于多线程的。
现在的应用程序,多线程的居多。表一所描述的模型无法适用于多线程环境。按表一所述,程序最多拥有上G的栈空间,事实上,在多线程情况下,能用的栈空间是非常有限的。为了说明这个问题,我们再看另外一个测试:
#include <stdio.h>
#include <pthread.h>
void* thread_proc(void* param)
{
int first = 0;
int* p0 = malloc(1024);
int* p1 = malloc(1024 * 1024);
printf("(0x%x): first=%p\n", pthread_self(), &first);
printf("(0x%x): p0=%p p1=%p \n", pthread_self(), p0, p1);
return 0;
}
#define N 5
int main(int argc, char* argv[])
{
int first = 0;
int i= 0;
void* ret = NULL;
pthread_t tid[N] = {0};
printf("first=%p\n", &first);
for(i = 0; i < N; i++)
{
pthread_create(tid+i, NULL, thread_proc, NULL);
}
for(i = 0; i < N; i++)
{
pthread_join(tid[i], &ret);
}
return 0;
}
|
运行后,输出结果为:
first=0xbfd3d35c
(0xb7f2cbb0): first=0xb7f2c454
(0xb7f2cbb0): p0=0x84d52d8 p1=0xb4c27008
(0xb752bbb0): first=0xb752b454
(0xb752bbb0): p0=0x84d56e0 p1=0xb4b26008
(0xb6b2abb0): first=0xb6b2a454
(0xb6b2abb0): p0=0x84d5ae8 p1=0xb4a25008
(0xb6129bb0): first=0xb6129454
(0xb6129bb0): p0=0x84d5ef0 p1=0xb4924008
(0xb5728bb0): first=0xb5728454
(0xb5728bb0): p0=0x84d62f8 p1=0xb7e2c008
我们看一下:
主线程与第一个线程的堆之间的距离:0xbfd3d35c - 0xb7f2c454=0x7e10f08=126M
第一个线程与第二个线程的堆之间的距离:0xb7f2c454 - 0xb752b454=0xa01000=10M
其它几个线程间的距离均为10M。
也就是说,主线程的堆空间最大为126M,而普通线程的栈空间仅为10M,超这个范围就会造成栈溢出。
栈溢出的后果是比较严重的,或者出现Segmentation fault错误,或者出现莫名其妙的错误。
分享到:
相关推荐
【大内高手(基于Linux的内存模型)】 Linux内存模型是操作系统中至关重要的一部分,它定义了内存如何被分配、管理和访问。了解这个模型对于优化程序性能、排查内存问题以及理解系统资源的使用至关重要。以下是对...
### Linux C内存管理详解——《大内高手》 #### 一、引言 在Linux环境下进行C语言编程时,深刻理解内存管理对于提高程序性能、增强软件稳定性以及提升开发效率至关重要。本文旨在全面解析Linux C内存管理的核心...
《李先静——大内高手系列》是一套专注于C++编程技术的教程,由知名IT专家李先静编著。这个系列深入浅出地探讨了C++编程中的各种陷阱和难点,旨在帮助初学者和有经验的程序员避免常见的错误,提升编程技能。以下是该...
【大内高手——内存模型】 在Linux环境下编程,特别是对于C程序员来说,深入理解内存管理至关重要。内存知识的掌握能够显著提升程序性能、稳定性和开发效率。本文将围绕内存模型、内存分配、内存管理策略以及常见...
知其然也知其所以然,是我们《大内高手》系列一贯做法,本文亦是如此。这里我不打算讲解如何使用boundschecker、purify、valgrind或者gdb,使用这些工具非常简单,讲解它们只是多此一举。相反,我们要研究一下这些...
很抱歉,但根据您提供的信息,"李先静_大内高手系列"似乎是某个人物或课程系列的名称,而具体的文件名称列表只包含这一条重复的信息,这并不足以生成详细的IT知识。若要提供相关的IT知识,我需要更具体的内容,例如...
【大内高手调试手段及原理】这篇文章探讨了高级调试技巧,旨在深入了解调试工具的运作机制,而不仅仅是如何使用它们。作者没有详细介绍boundschecker、purify、valgrind或gdb等常见调试工具的使用方法,因为这些工具...
【大内安卓学习资料demo全套】是一份全面的安卓开发学习资源集合,旨在为初学者提供一个月完整的学习路径。这份资料可能涵盖了从基础概念到实际应用的各种知识点,以帮助学习者扎实地掌握安卓开发技能。"达内"作为...
【大内 Java 笔记】是一份非常经典的 Java 学习资源,主要涵盖了从环境配置到实际编程的全过程。这份笔记适用于已经有一定 Java 学习基础的读者,可以帮助他们更深入地理解和掌握 Java 开发环境的搭建以及基本的编程...
java面试题暗示法法师嘎嘎个挨个发生巨额研究院统计引渡条约是是是
在MVC(模型-视图-控制器)架构中,Servlet通常作为控制器,处理用户请求并调用模型进行业务处理,然后用JSP生成响应。 【SSH框架】 SSH是Struts、Spring和Hibernate三个开源框架的缩写,它们是Java EE开发中的常用...
威廉·大内的Z理论(1981).doc
由蒋金楠(大内老A)编写的《ASP.NET Web API 2 框架揭秘-带源码版》深入浅出地介绍了这个强大的工具,帮助开发者掌握其核心概念和技术。 首先,Web API 2 是ASP.NET框架的一部分,它为创建高效、可扩展且与平台...
在深入探讨JSP(JavaServer Pages)的九大内建对象之前,我们首先简要回顾一下JSP技术。JSP是一种服务器端脚本语言,用于创建动态网页,它将HTML、CSS、JavaScript与Java代码结合在一起,使开发者能够构建功能丰富的...
综上所述,这份“大内培训”的资源涵盖了Web开发的核心内容,对于初学者或希望深入理解Web技术的人来说,是一份非常全面的学习资料。通过系统地学习和实践,可以建立起扎实的Web开发基础,并具备独立开发动态网站的...
亲,有此宝典在手,速成大内高手!!! Linux 指令大全.doc Linux菜鸟专用资料.pdf linux从入门到精通.pdf 初入Linux世界.pdf 攻克Linux系统教程28天没有难学的Linux.CHM 如何搭建一个安全的Linux服务器教程 Linux...