C语言与内存需要知道的事情:进程、地址空间、用户模式、内核模式、栈、数据类型、存储类型、静态变量、寄存器变量

  • 进程

    一个可执行程序就是一个进程Process),进程的最显著特点就是拥有独立的地址空间

    程序:静态概念,存储在磁盘上的一个文件,是指令和数据的集合;

    进程:动态概念,程序加载到内存运行后的一系列的活动;

    一个进程对应一个地址空间,而一个程序可能会创建多个进程。

  • 内核模式与用户模式

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

    用户空间保存的是应用程序的代码和数据,是程序私有的,其他程序一般无法访问,当执行应用程序自己的代码时,称为用户模式User Mode)。

    用户模式就是执行应用程度代码,访问用户空间;内核模式就是执行内核代码,访问内核空间(当然也有权限访问用户空间)。

  • 两种模式的区分源头

    内核最主要的任务是管理硬件,包括显示器、键盘、鼠标、内存、硬盘等,并且内核也提供了接口(也就是函数),供上层程序使用。当程序要进行输入输出、分配内存、响应鼠标等与硬件有关的操作时,必须要使用内核提供的接口。但是用户程序是非常不安全的,内核对用户程序也是充分不信任的,当程序调用内核接口时,内核要做各种校验,以防止出错。

    Intel 80386 开始,出于安全性和稳定性的考虑,CPU 可以运行在 ring0 ~ ring3 四个不同的权限级别,也对数据提供相应的四个保护级别。不过 LinuxWindows 只利用了其中的两个运行级别:

    • 一个是内核模式,对应 ring0 级,操作系统的核心部分和设备驱动都运行在该模式下。
    • 另一个是用户模式,对应 ring3 级,操作系统的用户接口部分(例如 Windows API)以及所有的用户程序都运行在该级别。
  • 内核和用户程序共享地址空间

    第一节已经说了,一个进程最显著的特点是拥有独立的地址空间,内核和用户共享地址空间,也就是说两者在同一个进程里。

    当内核和用户两者切换时,是模式切换,仅仅需要寄存器进栈出栈,不会导致缓存失效。

  • ==栈(Stack)==是程序的虚拟地址空间中地址较高的一个区域。

    栈(Stack)可以存放函数参数、局部变量、局部数组等作用范围在函数内部的数据,用途就是完成函数的调用。

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

    可以把栈理解成一个容器,先进后出(First In Last Out)的存储并利用数据。

    入栈(压栈、Push):放入数据;

    出栈(弹出、Pop):取出数据;

    物理上讲,栈是一段连续的内存,需要同时记录栈底和栈顶,才能定位当前的栈。

    ebpesp 都是CPU中的寄存器:ebpExtend Base Pointer 的缩写,通常用来指向栈底;espExtend Stack Pointer 的缩写,通常用来指向栈顶。

    在这里插入图片描述

  • 栈溢出

    栈能用的内存有限,一般是 1M~8M,这在编译时就已经决定了,程序运行期间不能再改变。如果程序使用的栈内存超出最大值,就会发生==栈溢出(Stack Overflow)==错误。

    一个程序可以包含多个线程,每个线程都有自己的,严格来说,栈的最大值是针对线程来说的,而不是针对程序

    栈内存的大小和编译器有关,编译器会为栈内存指定一个最大值,即可以通过软件设定参数指定。

    配置属性–>链接器–>系统–>堆栈保留大小

    栈经常被称为堆栈,而堆依然称为堆。

  • C语言变量的存储类别

    变量有两个知名属性:

    • 数据类型:指明变量占用多大的内存空间,可以进行哪些操作;int、float、double…
    • 存储类别:指明变量在内存中的存放区域;auto、static、register、extern

    在进程的地址空间中,可以用来存放变量的值的区域有:

    • 常量区:全局变量、静态变量、一般常量、字符串常量
    • 全局数据区:全局变量、静态变量、一般常量、字符串常量
    • 栈区:局部变量、函数参数

    常量区全局数据区的内存在程序启动时就已经由操作系统分配好,占用的空间固定,程序运行期间不再改变,程序运行结束后才由操作系统释放;

    栈区的内存在程序运行期间由系统根据需要来分配(使用到变量才分配内存;如果定义了变量但没有执行到该代码,也不会分配内存),占用的空间实时改变,使用完毕后立即释放,不必等到程序运行结束;

    生存期指的是在程序运行过程中,变量从创建到销毁的一段时间,生存期的长短取决于变量的存储类别,也就是它所在的内存区域

    知道了变量的存储类别,就可以知道变量的生存期。

  • auto

    自动或默认,定义变量时加不加auto都一样,一般可省略;

    int n = 10;
    # 等同于
    auto int n = 10;
    
  • static

    静态变量,不管是全局还是局部的,都存储在静态数据区(全局变量本来就存储在静态数据区,可以不加static

    静态数据区的数据在程序启动时就会初始化,直到程序运行结束;对于代码块中的静态局部变量,即使代码块执行结束,也不会销毁。

  • register

    寄存器变量

    变量的值是存储在内存中的,CPU 每次使用数据都要从内存中读取。如果有一些变量使用非常频繁,从内存中读取就会消耗很多时间,可以将使用频繁的变量放在CPU通用寄存器中,这样使用该变量时就不必访问内存,直接从寄存器中读取,大大提高程序的运行效率。

  • References

  1. C语言中文网
发布了753 篇原创文章 · 获赞 1021 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/The_Time_Runner/article/details/103779271