内存模型

文章来源:Intel software developer’s manul V1: Chapter 3.3 MEMORY ORGANIZATION

当应用程序要访问内存时,并不能直接对物理内存进行寻址,而是需要根据实际情况(例如操作系统的具体实现)来选择以下三种内存模型中的其中一种。另外,在现代的Linux 内核中,通常使用第一种也就是平坦内存模型。

Flat memory model

flat(平坦) 内存模型下,在应用程序的视角里,内存有一个独立且连续的内存空间。这种地址空间又被称作linear address space(线性地址空间)。这种模型中,所有的代码、数据和栈都在同一个地址空间中,且直接提供字节级别的寻址。在没有启用64-bit 的情况下,最大可寻址范围是 0 ~ 2^{32}-1. 在线性地址空间下的每一个字节的地址都被称作线性地址。

Segmented memory model

Segmented(分段)内存模型下,在应用程序的眼中,内存被组织为被称为“段”的一组独立地址空间。代码、数据和栈在这种模型下通常被置于独立的内存段中。应用程序通过logical address(逻辑地址)来对每个不同段里的字节进行寻址,逻辑地址通常又被叫做”far pointers”。逻辑地址包括segment selector(段选择器)和offset(偏移量),段选择器用于标识要访问的字节所在的内存段,而偏移量指向字节在该内存段中的位置。在32-bit 的处理器中,最多支持16383个不同大小和类型的内存段,且每个段最大都可以支持2^{32} 个字节大小的内存。

在处理器内部,实际上所有的内存段都被映射到线性地址空间中,在访问内存时,处理器会将逻辑地址翻译为线性地址,只不过这个过程对应用程序而言是透明的。

起初设计分段内存模型的目的,是为了给应用程序和操作系统提供更高的可靠性,通过将不同用途的内存置于各自的内存段中,可以有效的进行隔离。例如,如果在这种模型下发生栈溢出,不用担心栈溢出后会覆盖代码段或数据段,避免运行错误的指令或损坏数据。

Real-address mode memory model

实地址模式内存模型主要用于运行早期的为8086 处理器编写的程序,目的是为了提供兼容性。它是一种分段内存模型的特殊的实现,对于每个要运行的程序或操作系统,它可以提供一组每个大小不超过64KB 的内存段。这种模式下线性地址空间最大仅能支持到2^{20} 字节。

最后提供一张图片来更好的理解这三种不同的内存模型:

cpu 术语:core, die 和package

processor core:真正可以运行线程的运行单元,一个core可以有多个线程。
processor die:芯片,可以叠放一或多个core 的半导体材料,组成cpu 的晶体管真正存放的地方。
processor package:带金属引脚的成品cpu,也即物理cpu,可以包含一或多个die,已经可以插在主板socket 上。

一个处理器至少需要1个core,1个die 和1个package,即单核CPU。因为你至少需要一个运行单元core,一个容纳晶体管的die 和一个能和主板沟通的package.

双核处理器可以有1个package,1或2个die,对应的每个die 有2或1个core.

/proc/cpuinfo

physical id 记录package,是真正对应于socket 的物理cpu 编号,如果package 是多core 的,则可以有相同的physical id。
core id 记录物理core,如果core是多线程的,则可以有相同的core id.
processor 记录逻辑核心,每个线程一个编号。

例1,一个四核四线程的core i5-6300HQ:

# cat /proc/cpuinfo |grep -e processor -e "physical id" -e "core id"
processor   : 0
physical id : 0
core id     : 0
processor   : 1
physical id : 0
core id     : 1
processor   : 2
physical id : 0
core id     : 2
processor   : 3
physical id : 0
core id     : 3

例2,单核双线程:

processor : 0
model name : Intel(R) Pentium(R) D CPU 3.00GHz
cache size : 2048 KB
physical id : 0 / same physical id
siblings : 2
core id : 0 / different core id
cpu cores : 2
processor : 1
model name : Intel(R) Pentium(R) D CPU 3.00GHz
cache size : 2048 KB
physical id : 0 / same physical id
siblings : 2
core id : 1 / different core id
cpu cores : 2

参考链接:

[1]/doc.callmematthi.eu/static/webArticles/Understanding%20Linux%20_proc_cpuinfo.pdf
[2](941) 726-0661

(805) 714-2675

原文地址:isotope

早在巴比伦时期人们就已经知道怎么用逼近的方法去求平方根。例如求\sqrt{2}的近似值。

首先需要从一个接近的值开始,我们取x_1 = 3/2 = 1.5,那么x_1 的平方是9/4, 显然是一个大于2 的数字,也就是说3/2 > \sqrt{2}. 然后我们在考虑2/x_1 = 4/3,它的平方是16/9,小于2,所以2/x_1 < \sqrt{2}。

进一步的,我们取它们的平均值,记为x_2:

x_2 = \frac{1}{2}(x_1+\frac{2}{x_1})=\frac{17}{12}.

再对x_2 进行平方,我们得到了289/144,这是一个比2 大的数字。如果我们再考虑2/x_2 = 24/17,它的平方是576/289 < 2.

再取它们的平均值x_3:

x_3 = \frac{1}{2}(x_2+\frac{2}{x_2}) = \frac{577}{408}.

x_3 已经是一个2 的平方根的很好的接近了:

x_3^2 = 332929/166464 \approx 2.000006007305,

如果对这个精度仍不满意,我们只需要重复之前的动作,直到得到足够的精度。

牛顿和拉夫森用微积分的思想,把这个古老的方法扩展到了求任意方程的零点

f(x) = 0.

底层的思路是用函数f(x)的切线去进行逼近。

令r为函数f(x)的一个零点,我们有f(r)=0. 假设f'(r) \ne 0. 选择离r较接近的一个数字,记为x_1(例如从函数图像上来找到这样一个点)。f(x) 在点(x_1,f(x_1)) 处的切线在x 轴的截距记为x_2.

从图像中我们看到x_2 距离r更近一些,同时很容易计算

x_2=x_1-\frac{f(x_1)}{f'(x_1)}.

因为我们已经假设了f'(r) \ne 0, 这里不会有0 作为除数的问题。我们继续这个过程来获得x_3

x_3=x_2-\frac{f(x_2)}{f'(x_2)}.

继续重复这个动作,我们可以获得一个数列{x_n} 趋近于r.

这个通过连续趋近的方式找到真正的零点的方法就叫做牛顿法,或者牛顿-拉夫森法。

例如,我们用牛顿法找到一个精确到十位小数的\sqrt{5} 的趋近值。

首先要注意到\sqrt{5} 是一个无理数,小数点后的位数是无尽的。很明显r=\sqrt{5} 是函数f(X)=x^2-5 在区间[1,3] 上的唯一零点,如图所示。

用牛顿法获取逐渐趋近的数列{x_n}:

x_{n+1} = x_n-\frac{f(x_n)}{f'(x_n)} = x_n – \frac{x_n^2-5}{2x_n}.

从x_1=2 开始这个过程,我们有:

x_1=2

x_2=2.25

x_3=2.236111111111111111111111111111111

x_4=2.236067977915804002760524499654934

x_5=2.236067977499789696447872828327110

x_6=2.236067977499789696409173668731276

仅仅五次迭代我们就找到了精度超过小数点后十位的结果!