首页 > 编程知识 正文

linux系统的开发模型,linux系统下c语言编程工具

时间:2023-05-05 21:52:23 阅读:14722 作者:3313

在Linux和Windows上,c语言内存布局(内存型号)被称为虚拟地址空间和编译模式,虚拟地址空间在32位环境中为4GB,在64位环境中为256TB。 那么一个c语言程序的内存是如何分布在整个地址空间中的呢? 数据在哪里? 代码在哪里? 为什么要这样分布?

内核空间和用户空间对于32位环境,理论上程序可以有4GB的独立空间,我们在c语言中使用的变量、函数、字符串等对应于存储器中的区域。

然而,该4GB地址空间中的一部分不能被APP应用程序直接访问到操作系统内核。 该存储器地址的一部分称为内核空间(Kernel Space )。

Windows默认为内核分配高地址的2GB空间,而Linux默认为内核分配高地址的1GB空间。 也就是说,APP应用程序只能使用剩下的2GB或3GB地址空间,称为“用户空间”。

Linux下32位环境用户空间内存分布情况内核空间内存分布情况暂时不在意。 下图是Linux下32位环境的典型内存模型:

每个内存分区的说明:

存储器分区描述程序代码区域(code )存储函数主体的二进制代码。 一个c语言程序由多个函数构成,c语言程序的执行是函数之间的相互调用。 常数字段(constant )存储一般的常数、字符串常数等。 此内存只有读取权限,也没有写入权限,因此在程序运行时不能更改他们的值。 全局数据区域(global data )存储全局变量、静态变量等。 此内存具有读写权限,因此他们的值可以在程序运行时任意更改。 堆区域(heap )一般由程序员分配和释放。 如果程序员不释放,程序运行结束时操作系统会回收。 malloc ()、calloc ()、free () )等函数操作的就是这个内存。 也是我们要说明的重点。 注:这里所说的堆和数据结构中的堆不是概念,堆的分配方法类似于链表。 动态链接库用于在程序运行时加载和卸载动态链接库。 堆栈区域(Stack )存储函数的参数值、局部变量的值等,其行为类似于数据结构中的堆栈。 在这些内存分区中,对于“动态链接库”,程序代码区域用于存储指令,常量区域、全局数据区域、堆和堆栈都用于存储数据。 内存的研究侧重于数据分区的研究。

程序代码区域、常量区域、全局数据区域在程序加载到内存中后分配,并在程序运行时一直存在。 既不能销毁也不能增加。 (大小是固定的)在执行结束之前,操作系统只能回收,因此全局变量和字符串等可以从程序的任何地方访问。 因为他们的内存一直存在。

常量区域和全局数据区域有时统称为静态数据区域。 这意味着这个内存专门用于保存数据,并且在程序运行的过程中一直存在。

调用函数时,有关函数的信息(如参数、局部变量和返回地址)将被推入堆栈中,函数执行结束时这些信息将被丢弃。 因此,局部变量、参数只对当前函数有效,不能传递到函数外部。 因为他们的内存不见了。

常量空间、全局数据空间和堆栈上的内存由系统自动分配和释放,无法由程序员控制。 程序员唯一可以控制的内存空间是堆(heap )。 他是一个巨大的内存空间,常常占整个虚拟空间的大部分。 在这个空间里,程序可以申请一张内存,自由使用。 堆内存在程序自发释放之前,不会随着函数的退出而失效。 函数内部生成的数据只需放在堆中,即可在函数外部使用。

实例代码如下:

# include stdio.h char * str1=' hello,world '; //字符串为常数区域,str1为全局数据区域int n; //全局数据区char* func () { char *str='你好,世界); //字符串为常数区,str为堆栈区return str; (}int main ) ) { int a; //堆栈区域char *str2='01234 '; //字符串为常量区域,str2为堆栈区域char arr[20]='56789 '; //字符串和arr都位于堆栈区域中的char *pstr=func (; //堆栈区域int b; //堆栈区域printf (str1: % # x (NP str : % # x (nstr 23360 % # x (n ),str 1,pstr,str2); puts-------- '; printf (str1: % # x (n : % # x (n ),str 1,n ); puts-------- '; printf (a : % # x (nARR : % # x (nb : % # x (n ),a,arr,b ) ); puts-------- '; printf(n:%d(na:%d ) nb:%d(n ),n,a,b ); puts-------- '; printf(%s(n )、pstr ); 返回0; }执行结果:

str 1:0 x 400710 pstr :0 x 400720 str 2:0 x 400731---- str 1:00 x 601040 n 3360

0X60104C-------------- &a: 0X19D0728Carr: 0X19D07270 &b: 0X19D0726C--------------n: 0a: -858993460b: -858993460--------------你好,世界

对代码的说明:

全局变量的内存在编译时就已经分配好了,他的默认初始值是0(他所占用的每一个字节都是0值),局部变量的内存在函数调用时分配,他的默认初始值是不确定的,由编译器决定,一般是垃圾值,之后我们会详细说。函数func()中的局部字符串常量“你好,世界”也被存储到常量区,不会随着func()的运行结束而销毁,所以最后依然能输出。字符数组arr[20]在栈区分配内存,字符串“56789”就保存在这块内存中,而不是在常量区,要注意区分。 Linux下64位环境的用户内存分布情况

在64位环境下,虚拟地址空间大小为256TB,Linux将高128TB的空间分配给内核使用,而将低128TB的空间分配给用户程序使用,如图:

虚拟地址空间及编译模式中提到在64位环境下,虚拟地址虽然占用64位,但只有最低48位有效。这里需要补充一点是,任何虚拟地址的48位至63位必须与47位一致。

上图中,用户空间地址的47位是0,所以高16位也是0,换算成十六进制形式,最高的四个数都是0。内核空间地址的47位是1,所以高16位也是1,换算成十六进制,最高的四个数都是1.这样中间的一部分地址正好空出来,也就是图中的“未定义区域”,这部分内存无论如何也访问不到。

Windows下C语言程序的内存布局(内存模型)

在32位环境下,Windows默认会将高地址的2GB空间分配给内核(也可以配置为1GB),而将剩下的2GB空间分配给用户程序。

不同于Linux,Windows是闭源的,有版权保护,资料较少,不好深入研究每一个细节,至今仍然有一些内部原理不被大家知晓。关于Windows地址空间的内存分布,官网上只给出了简单的说明:

对于32位程序,内核占用较高的2GB,剩下的2GB分配给用户程序对于64位程序,内核占用最高的248TB,用户程序占用最低的8TB

下图是一个典型的Windows 32位程序的内存分布:

可以看到,Windows的地址空间被分配给了各种exe、dll文件、堆、栈。其中exe文件一般位于0x00400000起始的地址;一部分DLL位于0x10000000起始的地址,如运行库dll;还有一部分DLL位于接近0x80000000的位置,如系统dll,Ntdll.dll、Kernel32.dll。

栈的位置则在 0x00030000 和 exe 文件后面都有分布,可能有读者奇怪为什么Windows需要这么多栈呢?我们知道,每个线程的栈都是独立的,所以一个进程中有多少个线程,就有多少个对应的栈,对于Windows来说,每个线程默认的栈大小是1MB。

在分配完上面这些地址以后,Windows的地址空间已经是支离破碎了。当程序向系统申请堆空间时,只好从剩下的还没有被占用的地址上分配。

Windows64位程序的地址空间分布情况如下图所示:

由于官方资料不足,我们不再深入讲解 Windows 64 位程序的具体内存分布。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。