C语言和内存

1.程序的运行

对cpu来讲,内存只是一个存放指令和数据的地方,具体的运算在cpu内完成。程序员

   1.寄存器(Register)

是CPU内部很是小、很是快速的存储部件,它的容量颇有限,对于32位的CPU,每一个寄存器通常能存储32位(4个字节)的数据,对于64位的CPU,每一个寄存器通常能存储64位(8个字节)的数据。为了完成各类复杂的功能,现代CPU都内置了几十个甚至上百个的寄存器,嵌入式系统功能单一,寄存器数量较少。编程

寄存器在程序的执行过程当中相当重要,不可或缺,它们能够用来完成数学运算、控制循环次数、控制程序的执行流程、标记CPU运行状态等。数组

  2.缓存

虽然内存的读取速度已经很快了,可是和CPU比起来,仍是有很大差距的,不是一个数量级的,若是每次都从内存中读取数据,会严重拖慢CPU的运行速度,CPU常常处于等待状态,无事可作。在CPU内部设置一个缓存,能够将使用频繁的数据暂时读取到缓存,须要同一地址上的数据时,就不用大老远地再去访问内存,直接从缓存中读取便可。缓存

2.虚拟内存(内存地址都是假的)

1.虚拟地址

把程序给出的地址看作是一种虚拟地址(Virtual Address),而后经过某些映射的方法,将这个虚拟地址转换成实际的物理地址。这样,只要咱们可以妥善地控制这个虚拟地址到物理地址的映射过程,就能够保证程序每次运行时均可以使用相同的地址。函数

除了在编程时可使用固定的内存地址,给程序员带来方便外,使用虚拟地址还可以使不一样程序的地址空间相互隔离,提升内存使用效率。布局

使用了虚拟地址后,程序A和程序B虽然均可以访问同一个地址,但它们对应的物理地址是不一样的,不管如何操做,都不会修改对方的内存。spa

2.提升内存使用效率

使用虚拟地址后,操做系统会更多地介入到内存管理工做中,这使得控制内存权限成为可能。例如,咱们但愿保存数据的内存没有执行权限,保存代码的内存没有修改权限,操做系统占用的内存普通程序没有读取权限等。操作系统

3.内存对齐,提升寻址效率

将一个数据尽可能放在一个步长以内,避免跨步长存储,这称为内存对齐。线程

在32位编译模式下,默认以4字节对齐;在64位编译模式下,默认以8字节对齐。blog

内存对齐由编译程序实现,编译时咱们能够设置对齐长度。

 

4.内存分页机制

现代计算机都使用分页(Paging)的方式对虚拟地址空间和物理地址空间进行分割和映射,以减少换入换出的粒度,提升程序运行效率。

分页(Paging)的思想是指把地址空间人为地分红大小相等(而且固定)的若干份,这样的一份称为一页,就像一本书由不少页面组成,每一个页面的大小相等。如此,就可以以页为单位对内存进行换入换出:

  • 当程序运行时,只须要将必要的数据从磁盘读取到内存,暂时用不到的数据先留在磁盘中,何时用到何时读取。
  • 当物理内存不足时,只须要将原来程序的部分数据写入磁盘,腾出足够的空间便可,不用把整个程序都写入磁盘。

页的大小由硬件决定,或硬件支持多种,由操做系统选择一种大小,只能选一种。

内存分页由操做系统决定管理。

 

5.Linux下c语言程序的内存布局

内核空间和用户空间

操做系统会默认将高地址的1G或2G空间分配给内核,剩下的内存空间是用户空间

Linux下32位环境的用户空间内存分布状况:

 

 

 

程序代码区:主要存放二进制代码。不可修改,有执行权限

常量区:存放通常的常量,有读取权限,没有修改权限。因此数据不能修改

全局数据区:存放全局变量,静态变量等,有读写权限

堆区:由程序员分配和释放,malloc(),calloc()等函数操做的内存

栈区:存放函数的参数,局部变量值

 

一个程序的代码区,常量区,全局数据区在程序加载到内存的时候就分配好了,大小固定。

 

函数被调用时,会将参数、局部变量、返回地址等与函数相关的信息压入栈中,函数执行结束后,这些信息都将被销毁。因此局部变量、参数只在当前函数中有效,不能传递到函数外部,由于它们的内存不在了。

 

常量区、全局数据区、栈上的内存由系统自动分配和释放,不能由程序员控制。程序员惟一能控制的内存区域就是堆(Heap):它是一块巨大的内存空间,经常占据整个虚拟空间的绝大部分,在这片空间中,程序能够申请一块内存,并自由地使用(放入任何数据)。堆内存在程序主动释放以前会一直存在,不随函数的结束而失效。在函数内部产生的数据只要放到堆中,就能够在函数外部使用。

 

6.用户模式和内核模式

内核空间存放的是操做系统内核代码和数据,是被全部程序共享的,在程序中修改内核空间中的数据不只会影响操做系统自己的稳定性,还会影响其余程序,这是很是危险的行为,因此操做系统禁止用户程序直接访问内核空间。

 

要想访问内核空间,必须借助操做系统提供的 API 函数,执行内核提供的代码,让内核本身来访问,这样才能保证内核空间的数据不会被随意修改,才能保证操做系统自己和其余程序的稳定性。

 

用户程序调用系统 API 函数称为系统调用(System Call);发生系统调用时会暂停用户程序,转而执行内核代码(内核也是程序),访问内核空间,这称为内核模式(Kernel Mode)。

 

用户空间保存的是应用程序的代码和数据,是程序私有的,其余程序通常没法访问。当执行应用程序本身的代码时,称为用户模式(User Mode)。

 

7.栈的概念以及栈溢出

栈的概念:

栈内存由系统自动分配和释放:发生函数调用时就为函数运行时用到的数据分配内存,函数调用结束后就将以前分配的内存所有销毁。因此局部变量、参数只在当前函数中有效,不能传递到函数外部。

 

在计算机中,栈能够理解为一个特殊的容器,用户能够将数据依次放入栈中,而后再将数据按照相反的顺序从栈中取出。也就是说,先放入的数据最后才能取出,而最后放入的数据必须先取出。这称为先进后出(First In Last Out)原则。

 

栈的大小以及栈溢出

对每一个程序来讲,栈能使用的内存是有限的,通常是1M~8M,在程序编译时已经决定,运行期间不能改变,若是程序使用的栈超出最大值,就会发生栈溢出错误。

一个程序可能包含多个线程,每一个线程都有本身的栈,因此严格来讲,最大值是针对线程来讲的。

栈的大小是编译器决定的,咱们能够经过对编译器设置来调整栈的大小。

 

栈溢出攻击原理

C语言不会对数组溢出作检测,数组溢出致使覆盖了函数返回地址的例子,咱们将这样的错误称为“栈溢出错误”。

 

8.c语言动态内存分配

在进程的地址空间中,代码区、常量区、全局数据区的内存在程序启动时就已经分配好了,它们大小固定,不能由程序员分配和释放,只能等到程序运行结束由操做系统回收。这称为静态内存分配

相关文章
相关标签/搜索