跳转至

笔记

Jupyter AI

Jupyter 简介

Jupyter是一个开源的、交互式的计算环境,可以让用户创建和共享包含实时代码、可视化和文本的文档。

  • 它的名字来源于三个主要的编程语言:Julia、Python和R,这三种语言的开头字母构成了“Jupyter”。
  • Jupyter最初是IPython项目的一部分,旨在提供Python的交互式计算环境。随着时间的推移,它不仅支持Python,还扩展到其他编程语言,包括R、Julia、Scala等。Jupyter的灵感来自于IPython的交互式shell,但在其基础上增加了更多功能和可扩展性。

Jupyter最显著的特点:用户可以通过Web浏览器打开Jupyter笔记本,然后在其中编写代码、运行代码并直接查看代码的输出结果。笔记本中的代码和文本可以交叉编排,使得写作、数据分析和可视化变得非常直观和便捷。

主要的Jupyter组件包括:

  1. Jupyter Notebook:这是最常见的Jupyter界面,以.ipynb后缀的文件保存。它支持多种编程语言的代码运行,交互式地执行和编辑代码块,并支持在代码块中插入Markdown格式的文本以及图像、链接等内容。
  2. Jupyter Lab:这是Jupyter Notebook的下一代界面,提供了更加现代化和灵活的界面。Jupyter Lab将各种组件整合到一个集成的界面中,使得多个笔记本、终端和文件浏览器可以在一个窗口中同时运行。
  3. Jupyter Kernel:Jupyter支持多种编程语言的内核,通过内核,Jupyter可以与特定编程语言进行交互。例如,使用Python内核可以在笔记本中运行和编写Python代码,同样,使用R内核可以运行和编写R代码。

Jupyter在教育、数据科学、机器学习、数据分析等领域得到广泛应用。它提供了一个方便、实用的平台,帮助用户探索数据、实验算法、展示结果,并通过共享笔记本方便地与其他人交流和合作。

Jupyter vs python

Jupyter 的核心在于 数据分析的 计算-分析-可视化 的快速迭代。

如果不是数据科学,就不太需要Jupyter

Installation in Linux

安装Jupyter Lab

web-forward to local machine

远程访问服务器

Jupyter-AI

  1. Installation
  2. test

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

  1. python 一大特点就是容易可视化,既然这样,我为什么不用jupyter呢?
  2. chatgpt 类的工具都是基于付费API,有两大问题
  3. 国内难以付费
  4. 国内ip一旦访问是很容易封号的。
  5. 一种解决办法是使用有免费API的工具,并且在全流量走cloudflare的wg的服务器上配置服务。

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

https://jupyter-ai.readthedocs.io/en/latest/users/index.html#installation

Parallel_sort

并行排序算法

to learn

PSRS算法

并行正则采样排序算法

PSRS

复杂度分析

简单地讨论一下 PSRS 算法的复杂度。

  • 在第一部分的快速排序中,时间复杂度为O(klogk),k=n/p
  • 然后,各处理器选择 p-1 个代表元素,代价为O(p)
  • 再由 Proc#0 对所有送来的代表元素进行排序,然后选出主元,这里若使用快速排序,代价为O(p^2 logp^2)
  • 而若使用归并排序,则所需代价为O(p^2)
  • 每个处理器接收到主元后,再对有序数组进行划分,代价为O(k+p)
  • 最后,各个处理器全局交换,并行归并排序,
  • 每个处理器是串行的多路归并问题,时间复杂度为O(k*logp)

考虑到实际应用中,需要排序的数据长度 n 一定远远多于现有的处理器 p,此时可以视 p 为一个小常数,那么 PSRS 排序算法的时间复杂度,就可以简化为 O(klogk+k*logp)~O(klogk)

从消息复杂度的角度看,

  • 播送主元的复杂度为 O(p^2+p)
  • 分区合并(全局交换)部分的消息复杂度与具体算法实现相关,但其最大值不会超过 O(n)

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

https://dingfen.github.io/mpi&openmp/2021/01/23/psrs_sort.html

ParallelIntroduction

战略层

并行算法设计(详细见陈国良教材)

PCAM

设计并发程序的四个阶段(PCAM设计方法学):

  1. 划分(Partitioning):分解成小的任务,开拓并发性
  2. 通讯(Communication):确定诸任务间的数据交换,监测划分的合理性;
  3. 组合(Agglomeration):依据任务的局部性,组合成更大的任务;
  4. 映射(Mapping):将每个任务分配到处理器上,提高算法的性能。

三种基本方法

串改并:现有串行算法改成并行
  1. 从问题开始全新设计并行算法
  2. 前缀和改成线性方程组的问题来并行
  3. 有向环的k-着色并行算法
    1. 将coreId作为颜色,进行二进制处理来颜色压缩。压缩到0-5之后再单独消除颜色
借用法:借用已有算法
  1. 最短路径动态规划转换成矩阵乘法
其他基础方法
  1. 平衡树
    1. 求n个最大值,先串行求部分最大,再用树,成本(处理器个数*时间)最低
    2. 访问存储次数/成本也不是最低的
  2. 倍增技术,指针跳跃
  3. 分治策略
  4. 划分原理(以两个有序数列到归并排序为例)
    1. 均匀划分
    2. 对数划分
    3. 方根划分
    4. 功能划分:基于硬件的Batcher实现,奇偶归并排序,双调序列的实现可以简化网络
  5. 流水线技术
    1. 脉动阵列
  6. 加速级联策略
    1. 先采用最快的方法将问题规模先减小到一个阈值,然后用其余最优的算法求出原问题的解。
    2. 思想其实类似机器学习里的变学习率。例子有平衡树的
  7. 破对称技术
    1. 打破数据的对称,便于分类

指令级并行 ILP

编译器和硬件级别的,一般不会引起程序员的注意。1

挑战: 数据相关

  • 真数据相关
  • 名称相关:两条指令使用了相同的寄存器或者存储器位置,但实际并没有数据流动。寄存器重命名处理
  • 控制相关:主要指指令的执行与分支指令存在先后关系。

解决方案(CPI to 1):“硬件推测”(Hardware Speculation)

硬件推测是一种技术,通过它,处理器可以在不完全确定某些操作结果的情况下,提前执行后续指令。这种技术主要用于提高处理器的性能和执行效率。以下是硬件推测的几个关键方面:

  1. 分支预测(Branch Prediction):处理器使用分支预测来猜测条件跳转指令的结果(即跳转或不跳转)。如果预测正确,提前执行的指令就可以直接使用,从而避免等待分支决策的延迟。
  2. 数据依赖性推测(Data Dependency Speculation):处理器可能会提前执行依赖于尚未计算完成的数据的指令。例如,即使前一条指令的结果尚未确定,它也会继续执行依赖于该结果的后续指令。
  3. 乱序执行(Out-of-Order Execution):这是硬件推测的另一种形式。在这里,处理器根据资源的可用性而不是指令在程序中的顺序来调度指令的执行。
  4. 内存访问推测(Memory Access Speculation):处理器可能会在所有必要的内存访问权限检查完成之前开始执行依赖于特定内存操作的指令。

这些推测性技术的共同目标是减少因等待数据依赖、分支决策或其他延迟而导致的空闲处理器周期。如果推测正确,这可以显著提高执行速度。然而,如果推测错误,处理器必须“倒回”并重新执行正确的指令路径,这可能导致性能损失。因此,现代处理器设计的一个关键方面是优化这些推测机制以最大限度地减少错误预测的影响。

编译器技术:基本流水线与循环展开
分支预测器
动态调度/乱序执行(out-of-order execution)
  • 有多个功能单元和流水化单元。使得流水线能同时执行多个指令
  • 乱序执行, 循序提交: 记分板 -> Tomasulo算法

  • 重排缓存区ROB:保存已经执行完成,但是还没有提交的指令结果。是乱序执行处理器的核心组件之一。

  • 三点作用:
    1. 指令顺序的维护:尽管指令被乱序执行(以利用处理器资源并提高性能),但最终的结果需要按照程序的原始顺序提交。ROB跟踪每条指令的执行状态,并确保它们在最终提交时是按顺序的。
    2. 分支与异常的支持处理: 区分已提交指令,保证分支预测失败时正常回退,和异常和中断时程序的一致性。
    3. 资源管理:ROB还参与动态地管理处理器资源,如识别哪些指令可以并行执行,以及在资源有限时优先执行哪些指令。

升级方案(CPI lower 1):多发射架构(Multiple Issue)

与前面的区分

前面的技术是为了消除数据与控制停顿,使得CPI达到理想值1,如果我们想CPI小于一,每个时钟周期就需要发射多条指令。

多发射是一个更广泛的术语,指的是在一个时钟周期内发射(开始执行)多条指令的能力。

主要有三类:

  1. In order superscalar processor
  2. out-of-order superscalar processor
  3. VLIW
超标量架构(Superscalar)

超标量处理器通常具有多个执行单元,如多个整数、浮点和其他专用执行单元,以及复杂的调度和分支预测机制来支持同时处理多条指令。

VLIW

VLIW(Very Long Instruction Word)是一种处理器架构设计,其特点是使用非常长的指令字来编码多个操作,这些操作可以在单个处理器周期内并行执行。VLIW架构的关键特征如下:

  1. 长指令字:VLIW架构的指令字长度远超常规处理器。这些长指令字包含了多个操作(如算术、逻辑操作),这些操作在一个时钟周期内同时执行。
  2. 编译器优化:在VLIW架构中,指令的并行性是在编译时确定的,而不是在运行时。这意味着编译器负责识别可以并行执行的操作,并将它们组合成单个长指令字。
  3. 硬件简化:由于指令级并行性是在编译时处理的,VLIW处理器的硬件可以相对简化,因为它们不需要复杂的运行时指令调度和分支预测机制。这使得VLIW处理器在设计上更简单,功耗更低。
  4. 应用依赖:VLIW架构的效率高度依赖于编译器的优化能力和应用程序代码的特性。在指令流中并行性高的应用中,VLIW架构可以实现很高的性能。

VLIW架构在某些特定的应用场景(如数字信号处理DSP)中效果显著,但在通用计算领域的适用性受到限制,主要是因为编译器在处理普通程序时面临更大的挑战来有效地利用指令级并行性。

实例(超标量处理器能在一个时钟周期内同时发射和执行多条指令来实现指令级并行性。)

  • ARM Cortex-A8 Core 双发射、静态调度(In-order)超标量处理器
  • Intel Core i7 四发射、动态调度(out-of-order execution)超标量处理器
    • 微指令融合。性能更强,但是能耗比显著降低。

数据级并行 DLP

  • 向量张量、SIMD、以及GPU的结构
  • 区别和MLP 概念的不同:MLP(访存并行)是一种通过同时处理多个内存访问来实现并行性的概念。MLP的目标是提高对存储器系统的效率,减少内存访问的延迟时间。它可以通过预取、缓存和内存操作的重叠等技术来实现。

线程级并行 TLP

  • 单机多核系统
  • 运行一组紧密耦合的线程,协同完成任务
  • 缓存一致性协议

超算或者仓库级计算机(WSC Warehouse-Scale Computers) 请求级并行 RLP

  • 请求级并行:由一个或者多个用户发起的多个相对独立的进程
  • 环境: 云计算。
  • 关注成本与收益

并行分类

微处理器器中的并行

ILP 指令级并行

TLP 线程级并行

SMT 同步多线程(Simultaneous Multi-Threading,SMT)是一种在一个CPU 的时钟周期内能够执行来自多个线程的指令的硬件多线程技术。

CMP 单芯片多处理器(Chip multiprocessors)

常用的四种并行模式(这样分感觉不是很对)

  1. 共享内存模式(The shared memory model)
  2. 多线程模式(The multithread model)
  3. 分布式内存/消息传递模式(The distributed memory/message passing model)
  4. 数据并行模式(The data parallel model)

实际的经验

  1. IPCC Preliminary SLIC Optimization 4: EnforceLabelConnectivity

并行常见名词

SM : shared Memory

LM : Local Memory

DM :distribute memory

并行计算模型

并行计算访存模型(强调时间)

均匀访存模型(UMA)、非均匀访存模型(NUMA)、全高速缓存访存模型(COMA)、一致性高速缓存非均匀存储访问模型(CC-NUMA)和非远程存储访问模型(NORMA)。

UMA(Uniform Memory Access)均匀存储訪问:物理存储器被全部处理器均匀共享,全部处理器对全部SM訪存时间相同,每台处理器可带有快速私有缓存,外围设备共享。

NUMA非均匀存储訪问:共享的SM是由物理分布式的LM逻辑构成,处理器訪存时间不一样,訪问LM或CSM(群内共享存储器)内存储器比訪问GSM(群间共享存储器)快

COMA(Cache-Only MA)全快速缓存存储訪问:NUMA的特例、全快速缓存实现

CC-NUMA(Coherent-Cache NUMA)快速缓存一致性NUMA:NUMA+快速缓存一致性协议。实际是分布共享的DSM机器

NORMA(No-Remote MA)非远程存储訪问:无SM,全部LM私有。通过消息传递通信

NUMA

NUMA : NUMA (non-uniform memory access) is a method of configuring a cluster of microprocessor in a multiprocessing system so that they can share memory locally, improving performance and the ability of the system to be expanded. NUMA is used in a symmetric multiprocessing ( SMP ) system.

在NUMA下,處理器存取它自己的本地記憶體的速度比非本地記憶體快一些。 非統一記憶體存取架構的特點是:被共享的記憶體物理上是分散式的,所有這些記憶體的集合就是全域位址空間。

RDMA

Remote Direct Memory Access (RDMA) is an extension of the Direct Memory Access (DMA) technology, which is the ability to access host memory directly without CPU intervention. RDMA allows for accessing memory data from one host to another.

远程直接内存访问(英语:Remote Direct Memory Access,RDMA)是一种从一台计算机的内存到另一台计算机的内存的直接内存访问,而不涉及任何一台计算机的操作系统。这允许高吞吐量、低延迟联网,这在大规模并行计算机集群中特别有用。

重点是zero-copy, 不再需要机器缓存,然后拷贝传递信息。 InfiniBand网络默认支持,另一种就是RoCE

relationship between RDMA and NUMA

Most high performance computing clusters are nowadays composed of large multicore machines that expose Non-Uniform Memory Access (NUMA), and they are interconnected using modern communication paradigms, such as Remote Direct Memory Access (RDMA).

结构类型

  • SISD:单指令流单数据流计算机(冯诺依曼机)
  • SIMD:单指令流多数据流计算机
  • MISD:多指令流单数据流计算机, 实际不存在
  • MIMD:多指令流多数据流计算机
  • SIMD-SM

    PRAM(Parallel Random Access Machine)模型是单指令流多数据流(SIMD)并行机中的一种具有共享存储的模型。

    它假设有对其容量大小没有限制的一个共享存储器,并且有多个功能相同的处理器,在任意时刻处理器可以访问共享存储单元。根据是否可以同时读写,它又分为以下三类:PRAM-EREW,PRAM-CREW,PRAM-CRCW(其中C代表Concurrent,意为允许并发操作,E-代表Exclusive,意味排斥并发操作)。在PRAM中有一个同步时钟,所有的操作都是同步进行的。

    具有局部存储器的PRAM模型称作LPRAM模型,具有异步时钟的PRAM模型称作APRAM模型。

    在《并行算法的设计和分析》的第二十章-并行计算理论有额外的定义:

    • 允许任意处理器自由读写的 SIMD-SM。简记为 APRAM-CRCW
    • 只允许所有处理器并发写同一数的SIMD-SM。简记为CPRAM-CRCW
    • 只允许最小号码处理器优先写的SIMD-SM。称作优先PRAM-CRCW。简记为PPRAM-CRCW
    • 一个具有p个处理器的优先PRAM-CRCW模型。称作p-处理器的PPRAM-CRCW。

    几种MIMD

  • PVP并行向量处理机:多VP(向量处理器)通过交叉开关和多个SM(共享内存)相连
  • SMP对称多处理机:多P/C(商品微处理器)通过交叉开关/总线和多个SM(共享内存)相连
  • MPP大规模并行处理机:处理节点有商品微处理器+LM(分布式本地内存)。节点间通过高带宽低延迟定制网络互联,异步MIMD,多个进程有自己的地址空间,通过消息传递机制通信
  • COW工作站机群:节点是完整操作系统的工作站,且有磁盘
  • DSM分布共享存储处理机:快速缓存文件夹DIR确保缓存一致性。将物理分布式LM组成逻辑共享SM从而提供统一地址的编程空间
  • 需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    参考文献


    1. 计算机体系结构 - 量化研究方法第5 版 

    Benchmark : GUPS

    Introduction

    GUPS (Giga十亿 UPdates per Second) is a measurement that profiles the memory architecture of a system and is a measure of performance similar to MFLOPS.

    The HPCS HPCchallenge RandomAccess benchmark is intended to exercise the GUPS capability of a system, much like the LINPACK benchmark is intended to exercise the MFLOPS capability of a computer. In each case, we would expect these benchmarks to achieve close to the "peak" capability of the memory system. The extent of the similarities between RandomAccess and LINPACK are limited to both benchmarks attempting to calculate a peak system capability.

    definition of GUPS

    GUPS is calculated by identifying the number of memory locations that can be randomly updated in one second, divided by 1 billion (1e9).

    • The term "randomly" means that there is little relationship between one address to be updated and the next, except that they occur in the space of one half the total system memory. (只用一半内存?)
    • An update is a read-modify-write operation on a table of 64-bit words. An address is generated, the value at that address read from memory, modified by an integer operation (add, and, or, xor) with a literal value, and that new value is written back to memory.

    Extensibility

    • We are interested in knowing the GUPS performance of both entire systems and system subcomponents --- e.g., the GUPS rating of a distributed memory multiprocessor the GUPS rating of an SMP node, and the GUPS rating of a single processor.
    • While there is typically a scaling of FLOPS with processor count, a similar phenomenon may not always occur for GUPS.

    Principle

    Select the memory size to be the power of two such that 2^n <= 1/2 of the total memory. Each CPU operates on its own address stream, and the single table may be distributed among nodes. The distribution of memory to nodes is left to the implementer. A uniform data distribution may help balance the workload, while non-uniform data distributions may simplify the calculations that identify processor location by eliminating the requirement for integer divides. A small (less than 1%) percentage of missed updates are permitted.

    Installation

    Download

    official web or from GitHub

    Usage

    需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    开题缘由、总结、反思、吐槽~~

    参考文献

    上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

    Echart

    echart

    快速上手

    • 在github仓库dist目录下拷贝echart.js 和 echart.min.js到index.html目录下。

    Vue-ECharts

    参考中文文档

    sudo apt-get install npm
    npm install echarts vue-echarts
    

    实践

    简单柱状图

    option = {
      title: {
        text: 'Percentage of page walk time overhead caused by data TLB misses',
        subtext: 'GUPS RandomAccess benchmark'
      },
      tooltip: {
        trigger: 'axis',
        axisPointer: {
          type: 'shadow'
        }
      },
      xAxis: {
        name: 'input-data \nsize',
        type: 'category',
        data: ['2^29', '2^30', '2^31', '2^32'],
      },
      yAxis: {
        // name: 'Percentage',
        type: 'value',
        title: 'align'
      },
      series: [
        {
          name: "Percentage",
          data: [13.20, 19.50, 45.22,66.81],
          type: 'bar',
          label: {
            show: true,
            position: 'top'
          },
        }
      ]
    };
    

    需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    开题缘由、总结、反思、吐槽~~

    参考文献

    Double 2 int8

    将范围double映射到Int8

    解释PPT:除开int8的映射,还考虑了误差的计算,和重新计算M+空间的double并排序。

    注释:

    1. 采用的是欧式距离(两点直线距离),不是切比雪夫距离(max(delta x, delta y)
    2. C_2^n ,由于目标函数是所有边的距离和,所以要乘以边的数量。C_2^n 是从n个点中取两个点的组合数,也就是边的数量。
    3. C_k^n , k是支撑点个数,n 是总个数。
    4. 修正M个,因为题目要求求TopM,由于第M个可能是 +delta来的,M+1个可能是-delta导致的,所以要修正M个后面2*delta的范围。

    上面不知道具体怎么实现的(需要看代码)。下面同时要注意溢出的处理。int8溢出加法,可以转化为int16, 再相加。

    _mm256_cvtepi8_epi16

    AVX的操作Int寄存器也是分有无符号

    _epi8 signed char, or _epu8 unsigned char

    去年决赛冠军-上交队的思路

    这是我搜集这么多PPT里的,少有的思路

    👏我要😘开吹了👏

    本来IPCC2022 拿了第二名,我还心有不甘。直到我的风神大人教育了我。

    拿到风神大人的PPT的时候,我醍醐灌顶。

    这么体贴人,不愧是我温迪大人.

    需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    开题缘由、总结、反思、吐槽~~

    参考文献

    Disassembly file: objdump

    objdump file

    Disassembly of section .plt:
    
    0000000000402020 <.plt>:
      402020: ff 35 e2 bf 02 00     pushq  0x2bfe2(%rip)        # 42e008 <_GLOBAL_OFFSET_TABLE_+0x8>
      402026: ff 25 e4 bf 02 00     jmpq   *0x2bfe4(%rip)        # 42e010 <_GLOBAL_OFFSET_TABLE_+0x10>
      40202c: 0f 1f 40 00           nopl   0x0(%rax)
    
    0000000000402030 <_Znam@plt>:
      402030: ff 25 e2 bf 02 00     jmpq   *0x2bfe2(%rip)        # 42e018 <_Znam@GLIBCXX_3.4>
      402036: 68 00 00 00 00        pushq  $0x0
      40203b: e9 e0 ff ff ff        jmpq   402020 <.plt>
    
    0000000000402040 <_ZNSo3putEc@plt>:
      402040: ff 25 da bf 02 00     jmpq   *0x2bfda(%rip)        # 42e020 <_ZNSo3putEc@GLIBCXX_3.4>
      402046: 68 01 00 00 00        pushq  $0x1
      40204b: e9 d0 ff ff ff        jmpq   402020 <.plt>
    

    .plt节主要实现了使用过程链接表(Procedure Linkage Table)实现延迟绑定的功能。

    openmp相关的执行

    omp_outlined函数

    问题:objdump 程序 有许多 类似 <.omp_outlined..16>: 的函数,但是main函数里并没有调用。实际openmp是怎么执行这些代码的呢?

    在使用了OpenMP指令的C/C++程序编译后,编译器会自动生成一些名为.omp_outlined.的函数。这些函数是OpenMP所需要的运行时支持函数,不是直接在main函数中调用的,其执行方式主要有以下几种:

    1. 一些关键的运行时函数,如线程创建、同步等,是在程序启动时由OpenMP运行时库初始化和直接调用的。
    2. 对于并行region,编译器会在主线程发起并行时调用对应的.omp_outlined.函数创建线程并发布工作。
    3. 一些工作分配,同步等调用也是隐式通过运行时库的支持函数实现的。
    4. 对于并行循环,编译器会将循环体移动到.omp_outlined.函数,在循环分配工作时调用。
    5. 减少指令,作用域变化等也会引发这些函数的调用。

    所以.omp_outlined.函数的执行是隐式通过运行时库触发和调度的,不需要用户代码直接调用。它们是OpenMP实现所必须的,由编译器和运行时库协调完成。用户只需要编写OpenMP指令,不必关心具体的调用细节。

    总体来说,这是一种让并行执行透明化的实现机制,减少了用户的工作量。

    OpenMP的汇编代码

    不同平台不同,有GOMP_parallel_start开头的。也有如下x86平台的

      405854:   48 c7 84 24 a0 00 00    movq   $0x4293b9,0xa0(%rsp)
      40585b:   00 b9 93 42 00 
      405860:   48 8d bc 24 90 00 00    lea    0x90(%rsp),%rdi
      405867:   00 
      405868:   ba 10 5f 40 00          mov    $0x405f10,%edx
      40586d:   be 02 00 00 00          mov    $0x2,%esi
      405872:   4c 89 f9                mov    %r15,%rcx
      405875:   4c 8b 44 24 20          mov    0x20(%rsp),%r8
      40587a:   31 c0                   xor    %eax,%eax
      40587c:   e8 ff cb ff ff          callq  402480 <__kmpc_fork_call@plt>
      405881:   48 8b 7c 24 60          mov    0x60(%rsp),%rdi
    

    这段汇编代码实现了OpenMP中的并行构造,主要执行了以下几个步骤:

    1. 在栈上写入一个常量0x4293b9,可能是team的参数 (48 c7 84 24)
    2. 准备参数,获取rsp+0x90地址到rdi作为第1参数 (%rdi)
    3. 设置edx为0x405f10,可能是kmp_routine函数地址
    4. esi设置为2,可能表示有2个参数
    5. r15设置到rcx,传入线程号参数
    6. r8传入栈上第0x20个参数,可能是void* shareds参数
    7. 清空eax,一些调用约定使用
    8. 调用 __kmpc_fork_call函数,这是OpenMP的runtime库函数,用来并行执行一个函数
    9. kmpc fork multiple parallel call?
    10. 最后将返回值保存在rdi指定的栈空间上

    所以这段代码实现了调用OpenMP runtime并行执行一个函数的操作,准备参数,调用runtime API,获取返回值的一个流程。

    利用runtime库的支持函数可以实现汇编级别的OpenMP并行性。

    readelf

    各section位置以及含义,参考文档

    $ readelf -S bfs.inj
    There are 37 section headers, starting at offset 0xbe8e8: 
    在文件内 0xbe8e8字节开始
    
    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      序号 节名称               节类型          节的虚拟地址偏移量      节在文件中的偏移量
    节大小         每个条目的大小(如果大小固定)  节的标志  节的链接信息    节的额外信息    节的信息对齐方式
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .interp           PROGBITS         00000000004002a8  000002a8
           000000000000001c  0000000000000000   A       0     0     1
      [ 2] .note.gnu.build-i NOTE             00000000004002c4  000002c4
           0000000000000024  0000000000000000   A       0     0     4
      [ 3] .note.ABI-tag     NOTE             00000000004002e8  000002e8
           0000000000000020  0000000000000000   A       0     0     4
      [ 4] .gnu.hash         GNU_HASH         0000000000400308  00000308
           000000000000005c  0000000000000000   A       5     0     8
      [ 5] .dynsym           DYNSYM           0000000000400368  00000368
           00000000000007e0  0000000000000018   A       6     1     8
      [ 6] .dynstr           STRTAB           0000000000400b48  00000b48
           0000000000000b1d  0000000000000000   A       0     0     1
      [ 7] .gnu.version      VERSYM           0000000000401666  00001666
           00000000000000a8  0000000000000002   A       5     0     2
      [ 8] .gnu.version_r    VERNEED          0000000000401710  00001710
           0000000000000110  0000000000000000   A       6     5     8
      [ 9] .rela.dyn         RELA             0000000000401820  00001820
           00000000000000f0  0000000000000018   A       5     0     8
      [10] .rela.plt         RELA             0000000000401910  00001910
           00000000000006c0  0000000000000018  AI       5    24     8
      [11] .init             PROGBITS         0000000000402000  00002000
           000000000000001b  0000000000000000  AX       0     0     4
      [12] .plt              PROGBITS         0000000000402020  00002020
           0000000000000490  0000000000000010  AX       0     0     16
      [13] .text             PROGBITS         00000000004024b0  000024b0
           0000000000026475  0000000000000000  AX       0     0     16
      [14] .fini             PROGBITS         0000000000428928  00028928
           000000000000000d  0000000000000000  AX       0     0     4
      [15] .rodata           PROGBITS         0000000000429000  00029000
           0000000000001180  0000000000000000   A       0     0     16
      [16] .eh_frame_hdr     PROGBITS         000000000042a180  0002a180
           00000000000002ac  0000000000000000   A       0     0     4
      [17] .eh_frame         PROGBITS         000000000042a430  0002a430
           0000000000001780  0000000000000000   A       0     0     8
      [18] .gcc_except_table PROGBITS         000000000042bbb0  0002bbb0
           00000000000005d0  0000000000000000   A       0     0     4
      [19] .init_array       INIT_ARRAY       000000000042dbc8  0002cbc8
           0000000000000010  0000000000000008  WA       0     0     8
      [20] .fini_array       FINI_ARRAY       000000000042dbd8  0002cbd8
           0000000000000008  0000000000000008  WA       0     0     8
      [21] .data.rel.ro      PROGBITS         000000000042dbe0  0002cbe0
           00000000000001f0  0000000000000000  WA       0     0     8
      [22] .dynamic          DYNAMIC          000000000042ddd0  0002cdd0
           0000000000000220  0000000000000010  WA       6     0     8
      [23] .got              PROGBITS         000000000042dff0  0002cff0
           0000000000000010  0000000000000008  WA       0     0     8
      [24] .got.plt          PROGBITS         000000000042e000  0002d000
           0000000000000258  0000000000000008  WA       0     0     8
      [25] .data             PROGBITS         000000000042e258  0002d258
           0000000000000010  0000000000000000  WA       0     0     8
      [26] .bss              NOBITS           000000000042e280  0002d268
           0000000000000180  0000000000000000  WA       0     0     64
      [27] .comment          PROGBITS         0000000000000000  0002d268
           000000000000004a  0000000000000001  MS       0     0     1
      [28] .debug_info       PROGBITS         0000000000000000  0002d2b2
           000000000002a06e  0000000000000000           0     0     1
      [29] .debug_abbrev     PROGBITS         0000000000000000  00057320
           0000000000000a57  0000000000000000           0     0     1
      [30] .debug_line       PROGBITS         0000000000000000  00057d77
           000000000000af9a  0000000000000000           0     0     1
      [31] .debug_str        PROGBITS         0000000000000000  00062d11
           0000000000010328  0000000000000001  MS       0     0     1
      [32] .debug_loc        PROGBITS         0000000000000000  00073039
           0000000000042846  0000000000000000           0     0     1
      [33] .debug_ranges     PROGBITS         0000000000000000  000b587f
           00000000000054c0  0000000000000000           0     0     1
      [34] .symtab           SYMTAB           0000000000000000  000bad40
           00000000000018c0  0000000000000018          35   106     8
      [35] .strtab           STRTAB           0000000000000000  000bc600
           0000000000002177  0000000000000000           0     0     1
      [36] .shstrtab         STRTAB           0000000000000000  000be777
           000000000000016c  0000000000000000           0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
      L (link order), O (extra OS processing required), G (group), T (TLS),
      C (compressed), x (unknown), o (OS specific), E (exclude),
      l (large), p (processor specific)
    

    字段含义

    • Type 字段,具体含义参考文档1-10
    • Link 字段中的值是节头表中节头条目的索引,索引从0开始,表示第一个节头表条目,依此类推。比如5 代表与[ 5] .dynsym 有关

    值得注意

    One section type, SHT_NOBITS described below, occupies no space in the file, and its sh_offset member locates the conceptual placement in the file.

    so the number "2d258" remains unchanged.

      [25] .data             PROGBITS         000000000042e258  0002d258
           0000000000000010  0000000000000000  WA       0     0     8
      [26] .bss              NOBITS           000000000042e280  0002d268
           0000000000000180  0000000000000000  WA       0     0     64
    

    .got

    global offset table

    .plt

    This section holds the procedure linkage table. See ‘‘Special Sections’’ in Part 1 and ‘‘Procedure Linkage Table’’ in Part 2 for more information.

    Function symbols (those with type STT_FUNC) in shared object files have special significance. When another object file references a function from a shared object, the link editor automatically creates a procedure linkage table entry for the referenced symbol.

    参考文档2-17 page48

    需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    开题缘由、总结、反思、吐槽~~

    参考文献

    上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

    Linux Executable file: Structure & Running

    可执行文件历史溯源

    • COFF是32位System V平台上使用的一种格式。
    • 它允许使用共享库和调试信息。
    • 然而,它在节的最大数量和节名称的长度限制方面存在缺陷。
    • 它也不能提供C++等语言的符号调试信息。
    • 然而,像XCOFF(AIX)和ECOFF(DEC,SGI)这样的扩展克服了这些弱点,并且有一些版本的Unix使用这些格式。
    • Windows的PE+格式也是基于COFF的。 可见可执行文件在不同平台上的规则还是有所不同的,后续会以UNIX ELF来分析

    ELF 可执行目标文件

    exe

    可执行目标文件的格式类似于可重定位目标文件的格式。

    1. ELF 头描述文件的总体格式。它还包括程序的入口点(entry point),也就是当程序运行时要执行的第一条指令的地址。
    2. .text.rodata.data 节与可重定位目标文件中的节是相似的,除了这些节已经被重定位到它们最终的运行时内存地址以外。
    3. .init 节定义了一个小函数,叫做 _init,程序的初始化代码会调用它。
    4. 因为可执行文件是完全链接的(已被重定位),所以它不再需要 .rel 节。

    可重定位目标文件

    • 下面内容来自 深入理解计算机系统(CSAPP)的7.4 可重定位目标文件一节
    • 图 7-3 展示了一个典型的 ELF 可重定位目标文件的格式。ELF 可重定位目标文件的格式
    • ELF 头(Executable Linkable Format header)Executable Linkable Format
    • 以一个 16 字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。
    • ELF 头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。
      • 其中包括 ELF 头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如 X86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。
    • 节头部表描述不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目(entry)。

    夹在 ELF 头和节头部表之间的都是节。一个典型的 ELF 可重定位目标文件包含下面几个节:

    • .text:已编译程序的机器代码。
    • 通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
    • 代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。
    • .rodata:只读数据,比如 printf 语句中的格式串和开关语句的跳转表。
    • .data:已初始化的全局和静态 C 变量。
    • 已经初始化的全局变量、已经初始化?的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。
    • 局部 C 变量在运行时被保存在栈中,既不岀现在 .data 节中,也不岀现在 .bss 节中。
    • .bss:未初始化的全局和静态 C 变量,以及所有被初始化为 0 的全局或静态变量。
    • 在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。
    • 目标文件格式区分已初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。运行时,在内存中分配这些变量,初始值为 0。
      • 用术语 .bss 来表示未初始化的数据是很普遍的。它起始于 IBM 704 汇编语言(大约在 1957 年)中“块存储开始(Block Storage Start)”指令的首字母缩写,并沿用至今。
    • 区分 .data.bss 节的简单方法是把 “bss” 看成是“更好地节省空间(Better Save Space)” 的缩写。
    • .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。
    • 一些程序员错误地认为必须通过 -g 选项来编译一个程序,才能得到符号表信息。实际上,每个可重定位目标文件在 .symtab 中都有一张符号表(除非程序员特意用 STRIP 命令去掉它)。
    • 然而,和编译器中的符号表不同,.symtab 符号表不包含局部变量的条目。
    • .rel.text:一个 .text 节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。
    • 一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。
    • 注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非用户显式地指示链接器包含这些信息。
    • .rel.data:被模块引用或定义的所有全局变量的重定位信息。
    • 一般而言,任何已初始化的全局变量,如果它的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改。
    • .debug:一个调试符号表,其条目是
    • 程序中定义的局部变量和类型定义,
    • 程序中定义和引用的全局变量,
    • 以及原始的 C 源文件。
    • 只有以 -g 选项调用编译器驱动程序时,才会得到这张表。
    • .line:原始 C 源程序中的行号和 .text 节中机器指令之间的映射。
    • 只有以 -g 选项调用编译器驱动程序时,才会得到这张表。
    • .strtab:一个字符串表,其内容包括 .symtab.debug 节中的符号表,以及节头部中的节名字。字符串表就是以 null 结尾的字符串的序列。

    符号和符号表

    每个可重定位目标模块 m 都有一个符号表.symtab,它包含 m 定义和引用的符号的信息。在链接器的上下文中,有三种不同的符号:

    • (出)由模块 m 定义并能被其他模块引用的全局符号。
    • 全局链接器符号对应于非静态的 C 函数和全局变量。
    • (入)由其他模块定义并被模块 m 引用的全局符号。
    • 这些符号称为外部符号,对应于在其他模块中定义的非静态 C 函数和全局变量。
    • 只被模块 m 定义和引用的局部符号。
    • 对应于带 static 属性的 C 函数和全局变量。这些符号在模块 m 中任何位置都可见,但是不能被其他模块引用。

    • 本地链接器符号和本地程序变量的不同是很重要的。

    • .symtab 中的符号表不包含对应于本地非静态程序变量的任何符号。
    • 这些符号在运行时在栈中被管理,链接器对此类符号不感兴趣。
    • 有趣的是,定义为带有 C static 属性的本地过程变量是不在栈中管理的。
    • 相反,编译器在 .data 或 .bss 中为每个定义分配空间,并在符号表中创建一个有唯一名字的本地链接器符号。

    实践:readelf

    使用命令readelf -s simple.o 可以读取符号表的内容。

    示例程序的可重定位目标文件 main.o 的符号表中的最后三个条目。

    • 开始的 8 个条目没有显示出来,它们是链接器内部使用的局部符号。
    • 全局符号 main 定义的条目,
    • 它是一个位于 .text 节
    • 偏移量为 0(即 value 值)处的 24 字节函数。
    • 其后跟随着的是全局符号 array 的定义
    • 位于 .data 节
    • 偏移量为 0 处的 8 字节目标。
    • 外部符号 sum 的引用。

    • type 通常要么是数据,要么是函数。

    • 符号表还可以包含各个节的条目,以及对应原始源文件的路径名的条目。
    • binding 字段表示符号是本地的还是全局的。
    • Ndx=1 表示 .text 节
    • Ndx=3 表示 .data 节。
    • ABS 代表不该被重定位的符号;
    • UNDEF 代表未定义的符号,也就是在本目标模块中引用,但是却在其他地方定义的符号;

    实践: 查看exe信息相关命令

    file test

    # read ELF header
    readelf -h naive
    

    进一步思考

    • 小结:开辟局部变量、全局变量、malloc空间会影响可执行文件大小吗?对应汇编如何?存放的位置?运行时如何?
    • 设计一个代码量小但是占空间很大的可执行文件。
      • 因为已经初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)会存储在data段,所以这些变量的大小会影响可执行文件的大小。
    • static 与 const效果一样。
    • 设计一个代码量小但是运行时占内存空间很大的可执行文件。
      • malloc的空间会影响运行时的内存空间,但是不会影响可执行文件的大小。
    • 将exe各节内容可视化解释(虽然现在是二进制)
    • 编译的时候,头文件是怎么处理的?

    • data 与 bbs在存储时怎么区分全局与静态变量

    • 符号表为什么有全局变量的符号,这些静态局部变量不需要吗?应该是需要的
    • 请给出 .rel.text .rel.data的实例分析

    线程与进程

    • 调度:进程是资源管理的基本单位,线程是程序执行的基本单位。
    • 切换:线程上下文切换比进程上下文切换要快得多。
      • TLB是每个核私有的,如果一个核从一个进程切换到另一个进程,TLB要全部清空。
      • 但是线程不需要,因为线程共享相同的虚拟地址空间。
      • 所以线程切换开销远小于进程切换开销。
    • 拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。
    • 系统开销: 创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。

    (软件)多线程与(CPU)超线程

    线程和进程都可以用多核,但是线程共享进程内存(比如,openmp)

    超线程注意也是为了提高核心的利用率,当有些轻量级的任务时(读写任务)核心占用很少,可以利用超线程把一个物理核心当作多个逻辑核心,一般是两个,来使用更多线程。AMD曾经尝试过4个。

    单核多进程切换

    进程结构

    正在运行的程序,叫进程。每个进程都有完全属于自己的,独立的,不被干扰的内存空间。此空间,被分成几个段(Segment),分别是Text, Data, BSS, Heap, Stack。

    esp ebp

    • push pop %ebp 涉及到编译器调用函数的处理方式 application binary interface (ABI).
    • 如何保存和恢复寄存器
    • 比如:cdecl(代表 C 声明)是 C 编程语言的调用约定,被许多 C 编译器用于 x86 体系结构。 在 cdecl 中,子例程参数在堆栈上传递。整数值和内存地址在 EAX 寄存器中返回,浮点值在 ST0 x87 寄存器中返回。寄存器 EAXECXEDX 由调用方保存,其余寄存器由被叫方保存。x87 浮点寄存器 调用新函数时,ST0ST7 必须为空(弹出或释放),退出函数时ST1ST7 必须为空。ST0 在未用于返回值时也必须为空。

    0000822c <func>:
        822c: e52db004  push {fp}  ; (str fp, [sp, #-4]!) 如果嵌套调用 push {fp,lr}
        8230: e28db000  add fp, sp, #0
        8234: e24dd014  sub sp, sp, #20
        8238: e50b0010  str r0, [fp, #-16]
        823c: e3a03002  mov r3, #2
        8240: e50b3008  str r3, [fp, #-8]
        8244: e51b3008  ldr r3, [fp, #-8]
        8248: e51b2010  ldr r2, [fp, #-16]
        824c: e0030392  mul r3, r2, r3
        8250: e1a00003  mov r0, r3
        8254: e24bd000  sub sp, fp, #0
        8258: e49db004  pop {fp}  ; (ldr fp, [sp], #4) 如果嵌套调用 pop {fp,lr}
        825c: e12fff1e  bx lr          ; MOV PC,LR
    
    00008260 <main>:
        8260: e92d4800  push {fp, lr}
        8264: e28db004  add fp, sp, #4
        8268: e24dd008  sub sp, sp, #8
        826c: e3a03019  mov r3, #25
        8270: e50b3008  str r3, [fp, #-8]
        8274: e51b0008  ldr r0, [fp, #-8]
        8278: ebffffeb  bl 822c <func>
        827c: e3a03000  mov r3, #0
        8280: e1a00003  mov r0, r3
        8284: e24bd004  sub sp, fp, #4
        8288: e8bd8800  pop {fp, pc}
    

    arm PC = x86 EIP ARM 为什么这么设计,就是为了返回地址不存栈,而是存在寄存器里。但是面对嵌套的时候,还是需要压栈。

    栈区(stack)

    由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

    WIndow系统一般是2MB。Linux可以查看ulimit -s ,通常是8M

    栈空间最好保持在cache里,太大会存入内存。持续地重用栈空间有助于使活跃的栈内存保持在CPU缓存中,从而加速访问。进程中的每个线程都有属于自己的栈。向栈中不断压入数据时,若超出其容量就会耗尽栈对应的内存区域,从而触发一个页错误。

    函数参数传递一般通过寄存器,太多了就存入栈内。

    大数组seg fault

    栈区(stack segment):由编译器自动分配释放,存放函数的参数的值,局部变量的值等。

    局部变量空间是很小的,我们开一个a[1000000]就会导致栈溢出;而全局变量空间在Win 32bit 下可以达到4GB,因此不会溢出。

    或者malloc使用堆的区域,但是记得free。

    堆区(heap)

    用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时有可能由OS回收。

    分配的堆内存是经过字节对齐的空间,以适合原子操作。堆管理器通过链表管理每个申请的内存,由于堆申请和释放是无序的,最终会产生内存碎片。堆内存一般由应用程序分配释放,回收的内存可供重新使用。若程序员不释放,程序结束时操作系统可能会自动回收。

    用户堆,每个进程有一个,进程中的每个线程都从这个堆申请内存,这个堆在用户空间。所谓内训耗光,一般就是这个用户堆申请不到内存了,申请不到分两种情况,一种是你 malloc 的比剩余的总数还大,这个是肯定不会给你了。第二种是剩余的还有,但是都不连续,最大的一块都没有你 malloc 的大,也不会给你。解决办法,直接申请一块儿大内存,自己管理。

    除非特殊设计,一般你申请的内存首地址都是偶地址,也就是说你向堆申请一个字节,堆也会给你至少4个字节或者8个字节。

    堆有一个堆指针(break brk),也是按照栈的方式运行的。内存映射段是存在在break brk指针与esp指针之间的一段空间。

    在Linux中当动态分配内存大于128K时,会调用mmap函数在esp到break brk之间找一块相应大小的区域作为内存映射段返回给用户。

    当小于128K时,才会调用brk或者sbrk函数,将break brk向上增长(break brk指针向高地址移动)相应大小,增长出来的区域便作为内存返回给用户。

    两者的区别是

    内存映射段销毁时,会释放其映射到的物理内存,

    而break brk指向的数据被销毁时,不释放其物理内存,只是简单将break brk回撤,其虚拟地址到物理地址的映射依旧存在,这样使的当再需要分配小额内存时,只需要增加break brk的值,由于这段虚拟地址与物理地址的映射还存在,于是不会触发缺页中断。只有在break brk减少足够多,占据物理内存的空闲虚拟内存足够多时,才会真正释放它们。

    栈堆的区别

    1. 产生碎片不同 对堆来说,频繁的new/delete或者malloc/free势必会造成内存空间的不连续,造成大量的碎片,使程序效率降低。

    对栈而言,则不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。

    设计考虑

    1. 代码段和数据段分开,运行时便于分开加载,在哈佛体系结构的处理器将取得更好得流水线效率。
    2. 代码时依次执行的,是由处理器 PC 指针依次读入,而且代码可以被多个程序共享,数据在整个运行过程中有可能多次被调用,如果将代码和数据混合在一起将造成空间的浪费。
    3. 临时数据以及需要再次使用的代码在运行时放入栈中,生命周期短,便于提高资源利用率。
    4. 堆区可以由程序员分配和释放,以便用户自由分配,提高程序的灵活性。

    缓冲区溢出攻击(代码注入攻击

    • 缓冲区溢出(Buffer Overflow)是一种常见的软件漏洞,它发生在程序中使用缓冲区(一块内存区域)来存储数据时,输入的数据超过了缓冲区的容量,导致多余的数据溢出到相邻的内存区域。
    • 常见栈上分配空间,然后溢出直接覆盖前面的返回地址,使得返回到任意代码片段执行。如果开启了栈上执行代码,甚至能栈上注入代码并执行。

    虚拟内存

    用户进程内存空间,也是系统内核分配给该进程的VM(虚拟内存),但并不表示这个进程占用了这么多的RAM(物理内存)。这个空间有多大?命令top输出的VIRT值告诉了我们各个进程内存空间的大小(进程内存空间随着程序的执行会增大或者缩小)。

    Linux虚拟地址空间分布

    虚拟地址空间在32位模式下它是一个4GB的内存地址块。在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,如下图。而Windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3)。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,根据需要将其映射到物理内存。

    值得注意的是,每个进程的内核虚拟地址空间都是映射到相同的真实物理地址上,因为都是共享同一份物理内存上的内核代码。除此之外还要注意内核虚拟地址空间总是存放在虚拟内存的地址最高处。

    其中,用户地址空间中的蓝色条带对应于映射到物理内存的不同内存段,灰白区域表示未映射的部分。这些段只是简单的内存地址范围,与Intel处理器的段没有关系。

    上图中Random stack offset和Random mmap offset等随机值意在防止恶意程序。Linux通过对栈、内存映射段、堆的起始地址加上随机偏移量来打乱布局,以免恶意程序通过计算访问栈、库函数等地址。

    execve(2)负责为进程代码段和数据段建立映射,真正将代码段和数据段的内容读入内存是由系统的缺页异常处理程序按需完成的。另外,execve(2)还会将BSS段清零。

    top

    VIRT = SWAP + RES # 总虚拟内存=动态 + 静态

    RES >= CODE + DATA + SHR. # 静态内存 = 代码段 + 静态数据段 + 共享内存

    MEM = RES / RAM

                              DATA CODE  RES VIRT
    before allocation:         124    4  408 3628
    after 5MB allocation:     5008    4  476 8512           //malloc 5M, DATA和VIRT增加5M, RES不变
    after 2MB initialization: 5008    4 2432 8512           //初始化 2M, DATA和VIRT不变, RES增加2M
    
    
    //如果最后加上free(data),  DATA, RES, VIRT又都会相应的减少,回到最初的状态
    

    top 里按f 可以选择要显示的内容。

    SWAP

    • Swapping的大部分时间花在数据传输上,交换的数据也越多,意味时间开销也随之增加。对于进程而言,这个过程是透明的。
    • so(swap out):由于RAM资源不足,PFRA会将部分匿名页框的数据写入到交换区(swap area),备份之。
    • si(swap in) : 当发生内存缺页异常的时候,缺页异常处理程序会将交换区(磁盘)的页面又读回物理内存。
    • 每次Swapping,都有可能不只是一页数据,不管是si,还是so。Swapping意味着磁盘操作,更新页表等操作,这些操作开销都不小,会阻塞用户态进程。所以,持续飚高的si/so意味着物理内存资源是性能瓶颈。
    • 在内存空间设计早期只有分段没有分页时,SWAP还可以用来内存交换(暂存内存数据,重新排列内存),来消除内存碎片。

    需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    开题缘由、总结、反思、吐槽~~

    参考文献

    Light-weight Contexts: An OS Abstraction for Safety and Performance

    https://blog.csdn.net/zy986718042/article/details/73556012

    https://blog.csdn.net/qq_38769551/article/details/103099014

    https://blog.csdn.net/ywcpig/article/details/52303745

    https://zhuanlan.zhihu.com/p/23643064

    https://www.bilibili.com/video/BV1N3411y7Mr?spm_id_from=444.41.0.0