第二章 第七节 编译与底层

16 December 2021  |  home page


C++源文件从文本到可执行文件经历的过程

摘抄至Mr_H9527

include 头文件顺序

对于include的头文件来说,如果在文件a.h中声明一个在文件b.h中定义的变量,而不引用b.h。那么要在a.c文件中引用b.h文件,并且要先引用b.h,后引用a.h,否则汇报变量类型未声明错误。

变量声明

变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。
当您使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用 extern 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。 类似于函数的prototype

include的”“与<>

双引号和尖括号的区别:编译器预处理阶段查找头文件的路径不一样。

malloc原理

malloc函数用于动态分配内存。为了减少内存碎片和系统调用的开销,malloc其采用内存池的方式,先申请大块内存作为堆区,然后将堆区分为多个内存块,以块作为内存管理的基本单位。当用户申请内存时,直接从堆区分配一块合适的空闲块。Malloc采用隐式链表结构将堆区分成连续的、大小不一的块,包含已分配块和未分配块;同时malloc采用显示链表结构来管理所有的空闲块,即使用一个双向链表将空闲块连接起来,每一个空闲块记录了一个连续的、未分配的地址。

隐式链表

credit to qqliyunpeng Malloc在申请内存时,一般会通过brk或者mmap系统调用进行申请。其中当申请内存小于128K时,会使用系统函数brk在堆区中分配;而当申请内存大于128K时,会使用系统函数mmap在映射区分配。

C/C++的虚拟内存管理

C C++ 内存分配

3G用户空间和1G内核空间

32bitCPU可寻址4G线性空间,每个进程都有各自独立的4G逻辑地址,其中0~3G是用户态空间,3~4G是内核空间,不同进程相同的逻辑地址会映射到不同的物理地址中。其逻辑地址其划分如下:

各个段说明如下:

C++ 储存类型

如何判断内存泄漏

segment fault

通常发生在访问非法内存地址的时候,如:

memory leakage

new vs malloc

| new | malloc | | --- | ------ | | 内存按照数据类型进行分配 | 按照指定大小分配 | | 返回对象指针 | 返回null | | delete 调用析构函数 | free 不调用| | new 是类可以重载的操作符 | malloc 是库函数 | | new 不能扩容 | 可以用remalloc扩容 | | 分配失败返回bad_malloc | 分配失败返回null | | new []/delete [] 多次调用构造函数/析构函数 | malloc需要集体分配 |

共享内存 API

Linux允许不同进程访问同一个逻辑内存,提供了一组API,头文件在sys/shm.h中。

reactor(事件驱动模型)模型

reactor 模型

单线程方式处理高并发(属于阻塞)

  1. 复用I/O来处理多个请求
  2. 用事件驱动检测事件
  3. 用异步回调处理事件

select

poll

通过一个可变长度的数组解决了select文件描述符受限的问题。数组中元素是结构体,该结构体保存描述符的信息,每增加一个文件描述符就向数组中加入一个结构体,结构体只需要拷贝一次到内核态。poll解决了select重复初始化的问题。轮寻排查的问题未解决

epoll

epoll:轮寻排查所有文件描述符的效率不高,使服务器并发能力受限。因此,epoll采用只返回状态发生变化的文件描述符,便解决了轮寻的瓶颈。

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

epoll 使用方法

C++ STL 内存分配

  1. 第一级配置器 (credit to honeyRJ )
    • 分配内存大于128 byte时,直接使用malloc, free, realloc,分配的内存位于映射区
    • 第一级分配失败时,会尝试使用oom_alloc函数重新尝试分配内存,如果未指定new-handler,抛出__THROW_BAD_ALLOC这个异常
  2. 第二级配置器 (credit to honeyRJ )
    • 分配内存小于128 byte时,使用第二级配置器,分配的内存位于堆区
    • 二级内存池 二级配置器采用内存池管理(次层配置)。每次配置一大块内存,并维护16个空闲链表(8 - 128)。
      • 空间配置函数allocate 先判断是否小于128byte,若小于等于则直接从空闲链表里面取(取走链表第一个,把第二个node置为顶点)。若没有则调用refill填充空间
    • refill 调用refill来填充某个空闲链表,内存取自于二级内存池。default取20个数据块,如果内存池不足则尽可能取多的节点,把第一个节点直接返回,剩下的如果有则直接挂到对应空闲链表里。 如果一个节点都取不出则将二级内存池剩余内存挂到对应的空闲链表里,并重新用malloc给二级内存分配空间(一次所申请的内存大小为2 * 所 节点内存大小(提升后)* 20 + 一段额外空间)。 如果malloc失败则从去比该需要内存大的空闲链表拔出一个节点使用。 如果查找失败,则调用一级内存的oom_allocate()
    • deallocate 若释放内存小于等于128bytes,直接插到对应空闲链表内;否则直接free

同步/异步 vs 阻塞/非阻塞

credit to honeyRJ




Hosted on GitHub Pages — Theme by orderedlist