Cache
- Miss rate
- Conflict miss(thrash)抖动
- capacity miss由于缓存的容量大小有限而引起的缓存缺失
- miss panelty(缺失损失):缺失之后系统需要付出的额外时间Miss penalty = Fetch time + Replacement overhead + Conversion overhead
- pre-fetch and demand fetch:后者是当cpu发送一条数据的时候,去读取数据;前者是根据空间局部性原理进行预读取
- write through直写:马上协会内存
- write back写回:当替换的时候写回内存
- direct maped cache
- 高位index标识block号:丢失空间局部性
- 中间位index标识block号:保证空间局部性
- 线程会共享一个虚拟内存空间,但是进程不会
- 请求分页和除零都会导致一个错误,系统调用会导致一个缺陷,一个硬件的IO会导致一个中断
bss未初始化的全局变量
- 代码从源代码到执行的过程:
- 预处理:文本替换、宏展开、删除注释
- 编译器:将文本文件翻译成文本文件.s,得到汇编语言程序,把高级语言翻译成机器语言
- 汇编:将.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,把结果保存在.o文件中
- 链接:连接到动态库和静态库
- cpu和寄存器之间的数据交换通过
编译器
来完成 - 主存和硬盘之间的数据的传输是经过操作系统来完成的
- CPU时间是指CPU执行程序指令的时间
- 通过检查malloc和free的次数来判断是否存在内存泄漏的现象发生
- 当free掉一个结构体的时候,结构体内部的指针信息并不会被free掉
- 对堆的内存分配,再分配的时候并不知道占用空间的大小,但是
堆的空间一定是显式分配的
- 抽样统计性能信息:
- 不会知道具体的执行时间
- 不用过多的复杂指令的插入和执行
- 对程序整体实际的运行时间影响较小
- 链接器:是将多个目标文件整合成成一个可执行文件的工具。执行的过程为符号解析,文件的结合合并,再重定位
- 阿姆达尔定律:系统优化某个部件所获得的系统整体优化程度,取决于该部件使用的频率或所占总执行时间的比例 1/[ (1-p)+p/s]
- 优化的战术:代码移动、常量叠算、循环展开
- 全局变量在编译期就确定了地址这句话并非完全准确. 一般的程序是这样,确定地址的事情是在链接阶段才确定的。
- 局部变量的地址, 其在函数栈帧内的布局是由编译器确定的. 而其地址当然只有运行起来之后才能确定. 因为程序开始运行时栈才开始存在。局部变量的地址分配是运行时由函数中的指令分配的
- 注意数字相加的情况下过大越界的问题,
最后得到的结果可能会比原来的结果小
- 浮点数之所以尽量地左移是要尽可能保证数字的小数位足够多,保证数字的精度
- a->b == (*b).b
- ELF可执行和可链接文件
- 使用虚拟内存的方式,一个程序不会意外使用导致计算机崩溃
- 进程的使用:
- 减少了在上下文切换的过程中对于地址空间的改变
- 线程间共享内存进行使用
- 在分时系统中,为了限制对于输入/输出设备的访问程序应该通过一个陷阱指令执行一个系统调用
- 通常来说,,80/20原则是指大部分的执行时间花在很少的一部分代码上
- 之所以LRU算法很有效,是因为程序在运行的过程中展现出的局部性原理
- cache miss:高速缓存缺失,CPU在高速缓存器中没有找到所需的数据,需要访问主存。
- Activation record:活动记录,函数调用时分配的一块内存,它通常在栈内被产生,也被栈的框架所调用。
- ( Exception ):正常程序流程所不能处理或者没有处理的异常情况或异常事件。
- C++的代码的抽象化程度比C语言抽象化程度更高
浮点数不仅可以用来表示有理数rational real number,而且可以用来表示无理数irrational number
- 在内存窗口中监视得到的一个地址上保存的数据信息中包含的信息可能是一个整数、一个字符串、或者保存在内存上的一段指令的代码序列
- 在一个函数中的未初始化的指针变量的初始值为
undefined
- 再C语言中,最好的方法来判断是否一个指针被free两次的方法是:
将所有块标记为空闲或不空闲,并在调用free时检查标记
- 内存池是一个很大的内存块,可以根据需要通过将小对象从池中断开的方式逐个分配小对象,在
从池中分配的所有对象都大约在同一时间释放
的情况下有最好的效率 - 为了快速分配和释放很多公用的数据类型,我们可以保存一个保存着该数据大小的空闲块的链表
- 优化程序的性能的方法包括使用更快的程序算法,但是
不包括
:使用指针、使用占用更小空间的数据结构 -
用来进行重定位:符号表.symtable,
重定位中用不到的是:ELFheader、section header tables以及.rel.text和.rel.data段
- 当运行的用户进程或系统实用进程欲请求操作系统内核为其服务时,可以安排执行一条陷入指令引起一次特殊异常。
- 是一种特殊的异常
- 可以用来实现系统调用
- polling是轮询!!轮询!!!
- 计时器:用来保存时间,维护特定的时间片段信息,当时间片段耗尽的时候,程序由一种状态转变为另一种状态,或者当前运行的程序切换为另一个需要进行执行的程序。
- alignment对齐:是计算机中对于数据的放置位置的约束要求,通常要求数据的放置的首地址要为2、4、8等数字的倍数,可以减少内存分配中的外部碎片的出现,同时也能够简化后面对于内存和磁盘的管理工作
- COFF:通用对象文件,可执行文件和对象文件的32位编程的格式,该格式可以实现跨平台移植
链接器的执行:
连接器的工作就是解析未定义的符号引用,将他们输出到重定位表中,重定位部分,连接器将所有相同的类型节合并为聚合节,将运行时存储器地址付给它,以及赋给定义的每一个符号,完成后程序中每一个指令和全局变量都有位移运行时地址,然后在重定位每一个符号引用,流程为,1,符号解析,2,联合,3,重定位- 算术左移和逻辑左移右边的空位都是补充0,但是算数右移中左边补充符号位,逻辑右移中符号位和所有的位一起移动,而且左边补充0
浮点数加法最有可能导致精度丢失
- 汇编中的分支语句将程序计数器PC设置为两个可能值中的一个
- CPU寄存器指的是CPU中的一块内存被显式地加载和解除床在从主存中被
编译器生成的指令
- 内存泄露是指释放已经分配的内存失败而引起的错误
- C语言中未初始化的全局变量被置为0
- calloc相比较于malloc的区别在于会将最后分配完成的地址空间中的信息都置为0
- SRAM相比较于DRAM
- 平均每个容量单位更贵
- 更持久
- reentrant function可重入函数
- 能够随时被中断,并能够通过响应的机制来进行恢复
- 可以用在多线程的操作中
- 而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的
- 影响磁盘的存储空间大小的因素:
- 磁道密度
- 记录密度
- 内存别名就是指多个指针指向同一块地址空间,这种操作会对程序的执行和的自动优化造成影响
- 优化阻止程序就是指在程序编译器在优化中无法克服的因素,包括 potential memory aliasing (存储器别名使用)和 potential procedure side-effects (函数副作用)
- 当请求的内存地址的数据在缓存中,我们说缓存命中(Cache Hit)
简单的周期中断来计算程序执行时间:一个计时器周期性地中断程序并记录下计算机在指令序列里的位置,从而估计程序各部分所划分的时间
- 链接器对于多次定义的全局符号的处理:首先判断是强符号还是弱符号,再进行处理:
-
- 如果强符号有多个,则报错,因为同名的强符号只能有一个;
-
- 当有一个强符号和多个弱符号时,则选择强符号;
-
- 当有多个弱符号时,随机选择一个弱符号;
-
compiler应该注意被加法造成的溢出现象
- JAVA是如何进行内存分配的:Java有一个垃圾回收器
当所有需要分配的对象都有着相同的空间大小时,一个内存池的使用是有很大的帮助的
- sizeof是计算的整个数组的占的空间大小,而对于strlen来说计算的是字符串的长度,不包括’\0’
- mantissa尾数
- 当CPU执行了完了一个不是分支语句也不是跳转语句的代码后,PC的值会移动到下一个指令的位置
- 最好的方法检测一块地址是否两次被释放:标记所有的块是否被释放,在释放之前检查该标志的有效性。
- 一个
垃圾回收期
:释放哪些不能被不同的指针所引用的内容块 - TSC:A timer mechanism of x86 platform, which is the shortname of Time stamp counter(timestamp counter)
- exception:
- traps:返回时会执行下一条指令
- faults:返回时会重新执行该条指令
第一章:计算机系统漫游
- 编译器:将源代码转化为能够被机器所理解的代码,分配变量的内存空间
- 大端就是高地址为高位
在C语言中溢出不会被检测到,所以程序员必须小心选择整型的大小
- 有时可以使用判断sum和其中一个的大小关系来判断是否是溢出
- IEEE standard floating number encoding
- (-1)sM2E
- MSB是一个符号位
- Exponen是一个偏移量值,使用2e-1 - 1来表示,最后的e的值为加上偏移量的值,避免产生负数的现象
- fraction是要去掉头部的1的
- 在计算机中,所有的浮点数都是近似计算的
- 在浮点数中,相加的时候如果对应的指数不同,他们要移动位才能进行相加操作
汇编
-
能够引起PC寄存器改变的操作
- automatically increment
- call
- ret
- jump instructions:change the program counter
- branch instructions:change the program counter conditionally, according to PSW (program status word) Register (== FR, Flag Register )
-
编译器和汇编器的异同
- 相同之处:都是将一种语言转化为另一种语言
- 不同之处:编译器可以分析语言的语义,编译器也有一些选项可以选择,比如是否优化代码
-
寄存器分配
- 谁来分配:
- 汇编级别:程序员自己
- C语言级别:编译器
- 谁来分配:
-
指针的用处
- 不需要拷贝操作就能够进行变量之间的共享
- 指针使得我们可以进行动态的内存分配、
-
数组和数组内元素的地址相加地址之间的区别
- int a[5];
- &a + 1意思是1 * sizeof(a) + a
- a,&a的地址是一样的,但意思不一样,a是数组的首地址,而&a是数组对象的首地址
-
引用正确的联合体组件是一个程序员需要负的责任
-
对齐:
- 编译器通常对齐数据到字的边界或者双字的边界
- padding边界,是那些在一个控件的末尾和下一个空间的开头之间的没有利用到的部分
-
如下面一段代码
struct{
char a,b;
int c;
double a;
}
- 所占用到的空间大小为24bytes
- 会通过最大的数据成员来进行内存的对齐操作
内存调用和返回
- 全局变量可以被静态地定义
- 而局部变量必须被动态定义
- 之所以局部变量和全局变量的分配位置有如此大的差异,是因为编译器不知道局部变量到底要分配多少个的储存空间,所以其保留了很多的空间用于局部变量定义中的扩展
- 实参和局部变量需要被动态地在栈上分配内存空间
- activation record == stack frame栈帧
- 栈帧的结构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTjGyIKL-1578228390106)(./stackframe.png)] - 参数和返回地址都在调用方的栈帧的底部,一个栈帧从保存ebp的语句开始
内存分配
- 静态内存分配中的静态二字表示该过程发生在编译阶段和链接阶段,
而不发生在加载和执行阶段
- 静态变量再开始执行程序之前被分配空间并且初始化,并在直到内存被释放才被释放
- 静态分配的局部变量只在改对应的函数中有效,在任何的调用中不允许被重新初始化
- 在函数中定义声明的变量是在栈上所分配空间的
- 内存分配算法:
- first fit
- best fit
- worst fit:给当前的内存分配请求分配一个能够分配的最大的地址空间
- 地址0x00的位置保留下来用来表示不可访问的NULL指针的地址
- memory bug
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FvE48Zs3-1578228390108)(./memoryBug.png)] - *a–指的是指针减少,而不是指针里面的值减少(*a)–
- 内存泄漏
- The failure to deallocate (free) a block of memory when it is no longer needed is often called a memory leak.
- 释放结构体的时候要先释放在结构体里面定义的动态数据成员
garbage collection垃圾回收
- 怎么样追踪一个空闲块:
- 维护一个bitmap
- 维护一个链表
- 双向的空闲列表
- 维护一个平衡二叉树,每个空闲块的大小作为他们在树中的key
- 垃圾回收器
- 标记清除法:
- 标记阶段:从根节点开始,标记所有可以达到的内存块
- 清除阶段:扫描所有的内存块,释放那些没有被标记的内存块
- 根枚举:寄存器、全局或者静态变量、局部变量、、栈
- 保守的垃圾回收算法:可能有有些内存块是垃圾块,但是没有被标识出来
- 为什么:
- C不能标记所有任何类型的信息的内存块
- 无法推断一个内存空间保存的究竟是一个指针还是一个int值
- 为什么:
- 复制法:
- 维护了两个堆,一个用来被程序所使用,另外一个在垃圾回收阶段被使用
- 复制所有可以到达的内存块到未使用的堆上,然后请理那个程序正在使用的堆,然后两个堆互换角色
比标记清除法快速,因为只需要对所有内存块的一次遍历
- 引用计数法
- 维护一个对于所有对象的引用数量的列表
- 当对应的引用数量为0时,则该对象变成了无法到达的垃圾内存块
- 问题:
- 引用计数的花费太大
- 维护一个引用数量的列表有很大的开销
- 循环计数问题
- 递增和递减的操作通过是程序变得缓慢的原因之一
- 标记清除法:
- Generational GC 分代式垃圾回收法
empirical(根据经验的)
观察:- 如果一个对象很长时间都能够可达,则他很有可能保持可达
- 很多对象在分配后的不久就会变得不可达
- 频繁扫描那些刚分配的内存对象,不频繁扫描那些分配了很久的内存对象
- 各个阶段:
- G0:维护着新创建的对象,很有可能时垃圾块的对象
- G0比G1被扫描地频繁
- 大多数情况只有G0和G1两个阶段的划分
垃圾回收只会在内存变得紧张的时候进行,在内存宽裕的时候程序会全速运行,不会再释放内存上花费任何时间;采用垃圾回收的程序的开发和调试更快,因为不用开发、调试、测试或维护显式的释放代码。
- 垃圾回收的不足之处
内存回收何时运行是不可预测的,所以程序可能意外暂停。
运行内存回收的时间是没有上界的。尽管在实践中它的运行通常很快,但无法保证这一点。
除了回收程序以外的所有线程在回收进行时都会停止运行。
linker链接器
- 链接器将可重定向目标文件转化为可执行文件
- 链接是一个手机并且结合多个数据段和代码段然后整合为一个可执行文件的过程
- 链接所做的工作
- 动态链接:
- 加载时
- 运行时
- 静态链接
- 在编译之后
- 动态链接:
- 过程
- 符号解析
- 结合
- 重定向
- 目标文件:一个包含有多个段:代码段、数据段和其他信息段的二进制文件
- 分类:
- 可重定向目标文件
- 可执行目标文件
- 共享目标文件
- 结构
- header:描述整个文件的基本属性,比如文件版本,目标机器编号,大小端,程序入口地址,节头部表信息
- section header table:节头8部表,描述文件中包含的所有段的信息,比如每个节的名字,长度,偏移量,读写权限以及段的其他属性
- 分类:
- 符号解析
- 目的:让每一个符号的引用只关联到一个符号定义
- 根据每一个可重定向目标文件中的符号表来进行符号解析
- 规则:
- 多个强符号定义将会引起错误
- 一个强符号和若干个弱符号,选择强符号定义
- 多个弱符号,则随机选择一个若符号进行使用
- 加载和装载
- 将代码和数据从可执行文件拷贝到内存中然后将控制权传递给程序的起始点
- 加载:
- 创建进程
- 虚拟内存分配和映射
- 跳转到_start位置,而不是_main
- 需求分页
性能度量
- amdahl阿姆达尔定律:系统优化某部件所获得的性能的改善程度,取决于该部件被使用的频率,或所占总执行时间的比例
- speedup =
- 性能度量方法
- 时间
- wall clock:the time an ordinary clock on the wall or a wrist watch shows;
- wall time = user time + system time + other time
- CPU time = user CPU time + system CPU time
- 计时器:一个计算机中的组件,能够提供一定程度上测量时间的能力
TSC: Time stamp counter
- 数据统计抽样statistical sampling/profiling
- 程序周期性地中断记录程序计数器
- 可以估计时间都花在哪里了
优化程序性能
- 最好最重要的方法去优化一个程序是去使用正确的算法
- optimization blockers妨碍优化的因素
- potential memory aliasing存储器别名使用
- 编译器必须假设不同的指针可能会指向存储器的同一位置,造成妨碍优化的因素
- potential procedure side-effects函数副作用
- 编译器不会判断一个函数是否有副作用,它会假设最糟的情况,并保持所有的函数调用不变
- potential memory aliasing存储器别名使用
- 优化策略
- 代码移动
- 减少过程调用
- 减少不需要的内存引用
- 简化表达式
- 更快的运算技术
- 循环展开
- 避免表达式重复
- 使用汇编代码
- 充分利用寄存器
- 使用unsigned int如果知道对应的数字不会为负
内存操作和性能
- RAM
- 特点:随机访问、易失性
- SRAM:每一个存储单元由六个transistor晶体管组成
- DRAM:每一个存储单元由一个电容器和一个晶体管组成
- 取数据过程:
- row access strobe行访问脉冲选择一行
- 对应的行被拷贝到行缓存中
- 列访问脉冲选择一列
- 对应的超单元supercell中的数据被拷贝到数据线上,最终被传回CPU
- ROM
- 特点:只读和非易失性nonvolatile
- Disk storage
- 寻道时间、旋转延时、传输时间
- CPU访问磁盘中的信息:
- CPU初始化一个内存读事件使用写入对应的指令和地址到和磁盘控制器相关联的端口上
- 磁盘控制器读取对应的扇区并且使用一个直接内存访问将数据传输到主存
- 当传输完成时,DMA通过中断来通知CPU状态
- 容量、速度、价格
- 内存架构:
- 放置策略和置换策略
- 缓存缺失:
- 冷缺失:缓存是空的
- 容量缺失:工作集的大小大于缓存的大小
- 冲突缺失:大多数的k+1层的数据映射到k层的一小堆数据块上
- 各个缓存的管理者:
- registers:compiler
- L1、L2、TLB:hardware
- Virtual Memory、Buffer Cache:OS
- 局部性原则:程序会更多地访问刚刚访问过的数据项或者在刚刚访问过的数据项旁边的数据项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H97UgxXh-1578228390110)(./memoryMountain.png)]
缓存和基于缓存的编程过程
- 直接映射的缓存
- 组关联缓存
- 全关联缓存
- 基于缓存的编码和优化技巧
异常和进程/线程
- 异常控制流:突然的控制流转换去回应系统状态的变化
- 异常:
- 一场就是为了回应处理器的一些变化控制流的突然改变
- ISR:Interrupt Service Routine中断服务程序
- Interrupt:一直返回到下一条指令
- Trap:一直返回到下一条指令
- Fault:可能返回到当前指令
- Abort:永远不会返回
- 如何定位对应的异常处理程序
- 异常号
- 异常表
- ETR Exception Table Base Register异常表基地址寄存器
- 进程和线程
- 进程的构成:数据、代码、PCB/Context
- 决定那个进程下一个执行的操作叫做调度
- 多任务:
- 协作式多任务:正在执行的进程relinquishes放弃控制
- 抢占式多任务:进程被非自动地中断因为一个硬件中断然后控制交给其他进程
- Windows窗口编程
二刷
- 0x00 ^ 0x00的值为false,因为其是逻辑运算,如果使用~则会按照数学运算进行计算,然后得到的结果则是带符号的结果
- VC++ 的内存窗口中用多种方式来展示出了内存中所保存的内容,其中并不包括内存对应的变量名
- 在C语言中,当加法使用溢出时,将会产生一个错误的值,但执行过程依旧继续
- 要想快速给对象分配空间,则应该保留一个保存着很多相同大小空间的链表以此来实现快速分配
- 在函数的调用过程中,栈帧的变动过程,先call对应的函数,然后再在对应的子程序的汇编代码中首部对ebp的值进行保存操作
对齐
会带来的问题:- 对齐可能会导致未使用空间的问题
- 对齐可以帮助处理器更有效率地访问数据
- 注意是地址的值加一个数还是地址本身增加一定量
- 内存窗口中显示出的一串长的字符串啥都有可能是,可能是字符串,可能是CPU指令也可能是一个倒霉的int小哥
- 代码数据 采样的优点:
- 对应的代码片段能够被自动地感知
- 由于测度对性能的影响能够最小化
- 非零的数字取非都是零
- 被编译器编译而成的机器代码并没有保存所有源代码的信息到机器码中
- static定义的局部变量会被放入到.data段内,因为默认会被赋值为0
- C语言中的static:
- 标识了变量会被静态地分配
- 标识了变量或者函数在定义的该文件之内的可见性
- next-fit每次记录此次找到的内存分配地址,下次从此处开始查找并分配,
可以避免每次都扫描到内存空间中的前部的较小的内存片段
- QueryPerformanceFrequency返回硬件支持的高精度计数器的频率
- RTDSC记录的是自开机以来计算机所已经运行的周期数,是一个64位的值
- 那个能够计算cache line size的是因为自爱多次循环的过程中,在某一个临界值会超过缓存的单元大小,所以以此来计算对应的缓存的line size
栈帧
- 调用方:将调用函数的参数压入栈,将返回地址压入栈
- 被调用方:将调用者的ebp入栈,将esp的值赋值给ebp
- 保存的ret返回地址是对应的call指令的下一条地址,用以当栈帧返回时重新开始该过程
- 只有在栈顶的活动记录能够进行访问
- 栈帧包括的项:
- 传递给其的参数
- 返回到调用方的返回地址
- 调用方的ebp
- 给该过程分配的局部变量的空间
- 保存的寄存器
- 当调用返回时:
- 弹出寄存器
- 让栈指针esp指向帧指针ebp
- 弹出ebp
- 设置ebp为弹出的ebp
- 弹出返回地址
- 弹出对应的参数
- 几个寄存器:ebx,esi,edi
- rtn_addr返回地址
- 之所以奔腾处理些编译的代码在因特尔的计算机上不能使用是因为二者对于同一个操作码的理解不同
- 将数组传入函数中实际传输的是该数据首元素的地址
-
在比较的时候,unsigned和signed都会转化为signed进行比较
- 内核对象进行的同步:
- event事件内核对象
- 可等待的计时器内核对象waitable timer
- 信号量内核对象semaphore
- 互斥量内核对象mutex
- unary operator一元运算符
- 在C语言中,当释放指向一个结构体的指针时,其中的所有指针都不会自动释放
- 可执行文件格式:
- COFF
- ELF
- a.out
- PE
- 缺页:缺页中断就是要访问的页不在主存,需要操作系统将其调入主存后再进行访问。
- 空间局部性:
没有被严格的数学证明
,是一个典型的程序的质量 - 阿姆达尔定律:系统优化某部件所获得的系统性能的改善程度,取决于该部件被使用的频率,或所占总执行时间的比例
ssd复习
- 高层的抽象缺少细节,而底层的代码抽象又缺少简洁
- 编译器就是其他的源代码文件翻译成为硬件可以理解的语言的程序
- 分配变量空间
- 追踪变量的地址信息能够让硬件找到它
- hypothesis假设
- 使我们能够使程序暂停并且在执行中检查变量的工具叫做调试器
- vice versa反之亦然
- CPU寄存器就是被显式地被编译器生成的指令装载和释放的CPU内存
- 尝试将int传递给int*将会引发编译时错误
- JAVA怎么进行内存管理的:JAVA一直使用一个垃圾回收器
- 在进行软件优化的时候,首先的任务就是找到程序中的hotspot
- 垃圾回收:垃圾回收器会跟踪所有在使用的对象,在执行一条指令或者另一个程序所需空间不足时,垃圾回收器将会释放那些暂时用不到的空间来缓解内存压力。
-
在最后一题中的linker中,每一个R后面代表的是一个指针!!!
- C++是比C更高级别的抽象表示
- 在方法中未初始化指针的变量指向
undefined
- 静态变量会被默认初始化为0
- 链接发生在:
- 加载时
- 编译时
- 运行时
- Windows平台用户模式下的线程同步是通过
关键代码段
来实现的 - 优先级反转是指高优先级的线程间接地等待低优先级的线程
- 性能剖析(profiling)是专注于测量服务器时间花费在哪里的一种技术
- 存储器山是一种综合研究存储器层次结构的工具。它反映了存储器层次结构中不同层次的带宽。也反映了具有不同的时间局部性与空间局部性的程序的性能。
- 连接器的工作就是解析未定义的符号引用,将他们输出到重定位表中,重定位部分,连接器将所有相同的类型节合并为聚合节,将运行时存储器地址付给它,以及赋给定义的每一个符号,完成后程序中每一个指令和全局变量都有位移运行时地址,然后在重定位每一个符号引用,流程为,1,符号解析,2,联合,3,重定位
- 对于结构体中的每一个变量的地址来说,都是结构体的基地址加上一个对于每一个元素不同的偏移量来表示
- 只有
符号表
是用来重定向的 - 边界检查在程序设计中是指在使用某一个变量前,检查该变量是否处在一个特定范围之内。最常见的是数组的下标检查,防止下标超出数组范围而覆盖其他数据
- “分段错误是由访问“不属于您”的内存引起的一种特定错误。它是一种帮助机制,可以防止破坏内存并引入难以调试的内存错误
- C语言之所以很困难地实现显式内存分配的原因:
- 指针不总是被初始化
- 类型转换让一切都变得困难
- Windows平台的线程:
- 是作为内核级的线程来实现的
- 有多种同步的机制
- 缓存溢出(Buffer overflow),是指在存在缓存溢出安全漏洞的计算机中,攻击者可以用超出常规长度的字符数来填满一个域,通常是内存区地址
- 动态内存分配(Dynamic Memory Allocation)就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法
- 轮询之所以不能够使用在分时的操作系统中,是因为浪费太多的时间去做无用功
- 单精度浮点数的数值分布:1位-8位-23位,同时exponent位要加127来避免负数的存在
- Activation record:活动记录,函数调用时分配的一块内存,它通常在栈内被产生,也被栈的框架所调用
- 一个C语言函数只能由一个返回地址
- 如果将浮点数中的指数部分加多位数,将会造成的结果是能够得到更大或更小的数字,但是精度会下降
- 如果未经定义就引用对应的函数或者变量,将会造成
链接时
错误 - eip寄存器,用来存储cpu要读取指令的地址,通常会通过call指令来造成影响,call的下一条指令ret的将会使用到该寄存器,call设置了该寄存器的值
- call会将后面所带的地址压入栈中,作为当前的函数的返回地址来使用
- 程序的内存分配:
- 栈区
- 堆区
- 全局变量区
- 文字常量区
- 帧指针ebp:标记了该帧的底部地址和上一个调用者的顶部地址
- 栈指针esp
- 函数返回之后esp寄存器的值直接被重置为
参数压栈的位置的上面
- 只有引用计数会出现循环计数问题,只有复制法GC才会产生周期性的中断现象
- 在最好的情况下,与边界标记的合并在自由块的数量上不是线性的
- 隔离的自由列表通常接近最佳适合搜索
- 有效载荷必须对准某个边界
- 显式列表通常比隐式列表快
- 浮点数计算溢出回得到一个
_infinite
的值 - 隐式空闲链表的分配时间是块总数的线性关系,而显式的分配时间是空闲块的线性关系
- 扩展符号位,隐式转换为无符号数
- 一个函数只能由一个返回地址
- 活动记录就是函数调用在运行的过程中分配的一块内存,在栈上被分配,也被栈的框架所调用
- 分离的链表进行了排序???能够有近似最佳适配的效果