本系列内容整体框架借鉴了 👉《深入理解计算机系统 第三版》👈 但是许多内容并不限于这本书,计算机操作系统的理解并不是一蹴而就的,往往需要结合实际项目深入理解。希望通过这部分专栏的内容能够让读者深入的了解并理解计算机组成以及计算机操作系统。 此外,博主觉得尽管计算机硬件的设计是计算机操作系统的根本,但是不妨换一个思路,从操作系统方向理解计算机😺从操作系统理解计算机😸,如果硬件的设计是为了软件更好的运行,这种设计是否可以实现系统更加高效运行,这个问题值得深思
那么现在开始我们的学习之旅
每章都配有思维导图哟,阅读上1万后期会出视频 首先上一段极为经典的代码:
#include <stdio.h>
int main()
{
printf("Hello, World !\n");
return 0;
}
这段程序就是计算机中所有程序的代表,狭义上学习操作系统就是来理解程序在执行过程中操作系统都干了什么。为了帮助读者更好的理解程序与操作系统之间的,这里引出一个我自创的定义(其实是借鉴产品生命周期管理)——PLM(程序生命周期管理 Program Lifecycle Management) 程序的一般运行过程如上图,理解一个程序与系统之间的关系,首先需要理解这些程序代码在系统中是如何存放的,为此引出本章内容——程序结构与存储
本节内容导读
本节内容涉及到一部分数论中同余的内容,重点和难点是反码的设计原理,理解了反码的设计原理,补码设设计原理也就不难理解。
本节思维导图如下:
一、定点数的表示
进制的学习在计算机学习中是极为重要的,在嵌入式、硬件设计、电器自动化、电子电路甚至金融行业都有应用。十进制是是最常用的,满10进1的概念是一个基本的常识。所以十进制就说这么多。进制学习这块儿应该深入学习的是二进制。
1.1 进位计数与相互转换
(1)进位计数
进位计数制是利用固定的数字符号和统一的规则来计数的方法。在进位计数法中每个数位用到的不同数码的个数称为基数。例如,十进制的每个位上可以使用的数为0~9,所以十进制的基数为10,二进制每个位只能有0和1所以二进制的基数为2。 对于一个基数为
r
r
r的数
K
n
K
n
?
1
…
K
0
.
K
?
1
K
?
2
…
K
?
m
K_nK_{n-1}…K_0.K_{-1}K_{-2}…K_{-m}
Kn?Kn?1?…K0?.K?1?K?2?…K?m?,进位计数法与10进制转换可以进行如下表示
K
n
r
n
+
K
n
?
1
r
n
?
1
+
…
+
K
0
r
0
+
K
?
1
r
?
1
+
K
?
2
r
?
2
+
…
+
K
?
m
r
?
m
=
∑
i
=
n
?
m
K
i
r
i
K_n r^n+K_{n-1} r^{n-1}+…+K_0r^0 +K_{-1}r^{-1}+K_{-2}r^{-2}+…+K_{-m}r^{-m}=\sum_{i = n} ^{-m}K_i r^i
Kn?rn+Kn?1?rn?1+…+K0?r0+K?1?r?1+K?2?r?2+…+K?m?r?m=i=n∑?m?Ki?ri 例如对于二进制数10 0110.1,其十进制数的值为:
1
?
2
5
+
0
?
2
4
+
0
?
2
3
+
1
?
2
2
+
1
?
2
1
+
0
?
2
0
+
1
?
2
?
1
=
38.5
1*2^5+0*2^4+0*2^3+1*2^2+1*2^1+0*2^0+1*2^{-1}=38.5
1?25+0?24+0?23+1?22+1?21+0?20+1?2?1=38.5
-二进制的数码:0和1 -八进制的数码:0、1、2、3、4、5、6、7 -十六进制的数码:0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F
(2)十进制转换其他进制
1.整数部分转换——短除取余法
这里介绍短除法十进制转换到其他进制 这里介绍十进制转换二进制的方法,使用短除法,每次将商除以2,取余数作为二进制数值,具体实例如下图: 同理可以有十进制转换为八进制: 十进制转换为十六进制:
2.小数部分转换——乘基取整法
十进制小数转换为二进制小数运算如下: 小数部分乘以基数,将乘积取整最先取得的整数就是最高位,乘积为1.0或者满足精度时结束运算,最后得到的结果为0.1011 十进制小数转换为八进制与十六进制小数的方法与二进制相同,但是在这里需要着重强调一点: 并不是所有小数都可以使用二进制、八进制或者十六进制的,例如0.3,转换为二进制时,并不能取得精确的值,这时候可以使用其他方式来保证精度,这点在后面的内容会进一步叙述。
(3)二进制与其他进制的转换
二进制与十进制的转换使用进位计数法的公式就阔以。我们编程时使用的位运算大多是情况会使用二进制,在位数较多的情况下会使用八进制和十六进制,所以我们通常情况下只需要明白二进制与八进制与十六进制的转换计算方式即可。
1. 二进制转换八进制 转换过程其实很简单 每三位一组转换成对应的值,例如, 001转换成对应值为1,111转换成对应的值为7,这里举101转换值后的对应关系 具体示例如下:
2. 二进制转换十六进制 每四位一组转换成对应的值,例如, 0001转换成对应值为1,1111转换成对应的值为F,这里举1011转换值后的对应关系 具体示例如下:
(4)总结
1.常用数值
这里给出一般用的进制数值转换表:
十进制数值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|
十六进制数值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 八进制数值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 二进制数值 | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 |
十进制数值 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|
十六进制数值 | 8 | 9 | A | B | C | D | E | F | 八进制数值 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 二进制数值 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
除上表所示的值之外,我们对以下的值也应该敏感: 八位二进制的真值
八位二进制 | 1111 1111 | 十六位二进制 | 1111 1111 1111 1111 | 十五位二进制 | 0111 1111 1111 1111 |
---|
十进制 | 255 | 十进制 | 65535 | 十进制 | 32767 |
2. 各种进制前导符
二进制是Binary,很多地方我们也可以看到使用BIN简写 八进制是Octal,很多地方我们也可以看到使用OCT简写 十进制为Decimal,很多地方我们也可以看到使用DEC简写 十六进制为Hexadecimal,很多地方我们也可以看到使用HEX简写 在C语言中,需要使用前导符标识数值的进制,不指定的话一般默认十进制,具体,比如:
int a = 0b11;
几种进制的前导符如下:
进制 | 前导符 |
---|
二进制 | 0b或者0B | 八进制 | 0 | 十六进制 | 0x或者0X |
1.2 定点数的表示与运算
1.2.1 同余
同余的概念在数制学习中是极为重要的。
同余——两个整数
x
x
x,
y
y
y,若它们除以整数
z
z
z所得的余数相等,则称
x
x
x,
y
y
y对于
z
z
z同余记为
x
≡
y
(
m
o
d
?
z
)
x \equiv y ( mod {\,}z )
x≡y(modz)
这里强调一下余数的定义,对于A mod B=C,结果C满足以下条件
C
=
A
?
?
A
/
B
?
B
C=A-\lfloor A/B\rfloor B
C=A??A/B?B 通过这个式子能够计算负数对正数的余数(在许多编译器中求余并不是使用以上公式,但是也不影响同余地的操作) 例如:-2 mod 12=10,
为了给接下来的内容铺垫,这里举一个时钟的例子: 指针式时钟的进制是固定的,这里有一个奇妙的现象,时钟针从9拨到3有两种方式,顺时针拨到3和逆时针拨到3,这里我们可以发现一个现象(9-6)mod 12=(9+6)mod 12=3,由此这里给出同余的一个性质
如果a ≡ b (mod m),c ≡ d (mod m) 那么: a ± c ≡ b ± d (mod m)(证明过程可以自行百度或者谷歌) 在这里可以得出一个结论,在数值长度指定的条件下可以使用同余的方法是使用加法代替减法。 还是举一个时钟的例子——长度12固定,(6-2) mod 12=(6+10) mod 12=4,这里的-2与10对12同余 根据同余,可引出原码、反补码以及移码的概念。
1.2.2 机器字长、有符号数和无符号数
- 机器字长:机器字长是指计算机进行一次整数运算所能处理的二进制数据的位数(整数运算即定点整数运算)。需要注意一点的是决定机器字长的重要参数是系统虚拟空间地址(会在后面章节介绍),这点问题读者可以留到后面的章节解答
- 无符号数:没有符号位的整数,与有符号数区分
- 有符号数:带有符号位的数,计算机中,无法直接区分整数的正负,所以单独空出来一位作为正负的标识,一般的使用“0”表示整数,使用“1”表示负数
在计算机中有符号数有四种表示方法——原码、补码、反码和移码
1.2.3 定点数
定点数顾名思义其实就是小数点位置固定的数。计算机中有两种数据格式:定点数和浮点数。由于小数点位置固定不变,所以存储时小数点不进行存储,按照约定的位置计算数值。原则上说,小数点的位置可以位于任何位置,但通常将定点数表示成纯小数或纯整数,这样在硬件设计中更为实用。 为方便讨论数值范围,本文只讨论有符号数的一些性质,无符号数较为简单,读者可以根据有符号数的相关内容和自行得出。
(1)定点小数,假设机器字长为n
定点小数,只有小数部分,或者说定点小数的整数部分默认为0 有符号定点小数的基本格式如下图 对于机器字长为
n
n
n的一串数字,第1位为符号位,后
n
?
1
{n-1}
n?1位为数字信息,其能够表示的最大值为0.1111…11(
n
?
1
{n-1}
n?1个1),其真值(十进制)为——
1
?
2
?
(
n
?
1
)
{1-2^{-(n-1)}}
1?2?(n?1) 相对的,定点小数的最小值的真值为1(符号位).1111…11,其真值为——
?
(
1
?
2
?
(
n
?
1
)
)
{-(1-2^{-(n-1)})}
?(1?2?(n?1)) 这里补充真值的计算方法,可以使用等比数列加法公式计算,这里介绍另一种较为简单的方法—— 全1的小数最后一位加1之后得到的结果如下图,
n
n
n位有符号小数的最大值就是
1
?
2
?
(
n
?
1
)
{1-2^{-(n-1)}}
1?2?(n?1)相对的最小值是
?
(
1
?
2
?
(
n
?
1
)
)
{-(1-2^{-(n-1)})}
?(1?2?(n?1))
(2)定点整数,假设机器字长为n
定点整数只含有整数部分,对于机器字长为
n
n
n的一串数字,第1位为符号位,后
n
?
1
{n-1}
n?1位为数字信息,其能够表示的最大值为01111…11(
n
?
1
{n-1}
n?1个1),其真值(十进制)为——
2
(
n
?
1
)
?
1
{2^{(n-1)}-1}
2(n?1)?1 相对的,定点小数的最小值的真值为1(符号位)1111…11(
n
?
1
{n-1}
n?1个1),其真值为——
?
(
2
(
n
?
1
)
?
1
)
{-(2^{(n-1)}-1)}
?(2(n?1)?1) 这里的计算方法和小数部分的计算过程相同,注意一点整数的最低位为
2
0
2^0
20
(3)原码、反码、补码和移码,假设机器字长为n
说明:在现代计算机系统中原码和反码都没有补码和移码重要,对于计算机而言原码和反码有许多弊端,这些内容在下面的内容会娓娓展开
1.原码
这里给出小数与整数原码的函数定义:
-
小数
x
原
码
=
{
x
1
>
x
≥
0
1
?
x
=
1
+
∣
x
∣
0
≥
x
>
?
1
(1)
x_{原码}= \begin{cases} x\quad 1> x \geq 0\\ 1-x=1+\lvert x\rvert \quad 0\geq x>-1 \end{cases} \tag{1}
x原码?={x1>x≥01?x=1+∣x∣0≥x>?1?(1) -
整数
x
原
码
=
{
0
,
x
2
n
?
1
>
x
≥
0
2
n
?
1
?
x
=
2
n
?
1
+
∣
x
∣
0
≥
x
>
?
2
n
?
1
(2)
x_{原码}= \begin{cases} 0,x\quad 2^{n-1}> x \geq 0\\ 2^{n-1}-x=2^{n-1}+\lvert x\rvert \quad 0\geq x>-2^{n-1} \end{cases} \tag{2}
x原码?={0,x2n?1>x≥02n?1?x=2n?1+∣x∣0≥x>?2n?1?(2) 原码的定义其实很好理解,正小数的原码是符号位加上真值也就是符号位的0加上真值, 负小数的原码就是最高位加上真值,有符号小数的机器数结构如下,所以负小数的原码就是1加上真值的绝对值或者真值的相反数 根据以上内容,那么整数的原码的计算也不难,正整数的原码也就是符号位为0加上真值,负整数与负小数类似,负整数的原码就是符号位加上真值的绝对值,这里需要与小数区分的一点是整数的机器数结构如下图,其符号位表示的真值为
2
n
?
1
2^{n-1}
2n?1 -
原码总结:无论小数还是整数,原码的形式就是符号位标明正负,数值位表示真值的绝对值大小
2.反码
这里给出小数与整数反码的函数定义,不容易理解,可以先看公式下面的解释:
- 小数
x
反
码
=
x
真
值
?
m
o
d
?
(
2
?
2
?
(
n
?
1
)
)
=
{
x
,
1
>
x
≥
0
2
?
2
?
(
n
?
1
)
+
x
,
0
≥
x
>
?
1
(3)
x_{反码}= x_{真值}\:mod\:(2-2^{-(n-1)})= \begin{cases} x,\quad 1> x \geq 0\\ 2-2^{-(n-1)}+x, \quad 0\geq x>-1 \end{cases} \tag{3}
x反码?=x真值?mod(2?2?(n?1))={x,1>x≥02?2?(n?1)+x,0≥x>?1?(3) - 整数
x
反
码
=
x
真
值
?
m
o
d
?
(
2
n
?
1
)
=
{
0
,
x
2
n
?
1
>
x
≥
0
2
n
?
1
+
x
0
≥
x
>
?
2
n
?
1
(4)
x_{反码}= x_{真值}\:mod\:(2^n-1)= \begin{cases} 0,x\quad 2^{n-1}> x \geq 0\\ 2^{n}-1+x \quad 0\geq x>-2^{n-1} \end{cases} \tag{4}
x反码?=x真值?mod(2n?1)={0,x2n?1>x≥02n?1+x0≥x>?2n?1?(4) - 反码的存在主要是为了解决计算机减法,仅仅使用原码计算机无法实现减法,因为电路设计可以实现电平信号的叠加,但是却无法直接实现减法
- 重点难点——反码的设计原理(为什么设计反码)
1.2.1节介绍了同余,固定机器字长的计算机能够表示的数据长度是固定的,这里给出负数一般的同余方程解(其中一解)的公式 —— 对方程
x
≡
b
(
m
o
d
?
m
)
x≡b(mod\:m)
x≡b(modm) 其可行解可以表示为
x
=
b
+
m
x=b+m
x=b+m或者
x
=
b
m
o
d
?
m
x=b mod \:m
x=bmodm
(
b
≤
0
,
∣
b
∣
<
m
)
(b\leq0,\lvert b\rvert<m)
(b≤0,∣b∣<m)——这也就是负数反码的定义 综合有符号正数,其实反码的计算就是真值对机器字长模运算也就是
x
反
码
=
x
真
值
?
m
o
d
?
(
2
n
?
1
)
x_{反码}=x_{真值}\:mod\:(2^n-1)
x反码?=x真值?mod(2n?1) 对小数而言,其m为小数能够表示的字长数,也就是1(符号位).111……11(一共n个1)也就是
m
=
2
?
2
?
(
n
?
1
)
m=2-2^{-(n-1)}
m=2?2?(n?1)其结论与整数相同
比如机器字长为8位的计算机,能够表示最大长度的数据是——0X FF,在8位计算机中计算"2-8",使用同余定理,我们需要找出“-8”对255的相同余数的数,也就是247(-8反码的十进制表示),于是有“(2-8)mod 255=(2+247)mod 255=249”——249的2进制为1111 1001这是有符号数的反码,其真值为1000 0110也就是-6
- 反码总结:无论小数还是整数,反码的形式就是符号位标明正负,正数反码与原码相同,负数符号位不变,原码的数值位全部取反
之所以能够取以上形式,是由定义决定的。
但是反码的缺点也是显而易见的, 首先,反码表示的0并不唯一,有+0(0000……0)和-0(11111……11)的区别 其次,反码在运算减法时存在溢出的问题, 比如机器字长为8位的计算机,能够表示最大长度的数据是——0X FF,在8位计算机中计算"8-2",使用同余定理,我们需要找出“-2”对255的相同余数的数,也就是253(-2反码的十进制表示),于是有“(8+253)mod 255=6” 以上过程在计算机中运算会出现问题,8+253=261,但是在8位的计算机中,无法存放261这个数,261的二进制为0001 0000 0101,这种计算结果超过计算机能够表示的最大值或者最小值的情况称为溢出,此时计算机中存放的只有261的低八位——0000 0101=5 根据以上情况,使用反码计算时,需要专门记录溢出情况,当发生溢出时需要对结果最低位进行+1处理
为什么要对溢出的结果进行+1处理呢
对于n位字长的计算机,发生溢出时,情况如下: 最高位溢出时相当于增加一个
2
n
2^n
2n,为什么只能增加一个
2
n
2^n
2n呢,是因为最大的机器数相加如下: 1111…11(n个1)+1111…11(n个1)=1 1111…10(n+1个1),所以溢出时最高位只能进一位,相当于只能多一个
2
n
2^n
2n 这时再看之前举得例子对261 mod 255运算时相当于——(溢出位+机器数上的8位)mod 255 溢出位的值为
2
8
=
256
,
256
?
m
o
d
?
255
=
1
2^8=256,256\:mod\:255=1
28=256,256mod255=1,所以反码计算时,如果发生溢出需要对计算后的结果的最低位+1 小数与整数的设计方案相同,只不过小数的长度与整数不同罢了,读者可以自行推出相关结论。(这里就一万三千字了,重复的就不写了😚)
3.补码
根据以上反码设计原理的描述,我们可以引出一种新的码制——补码。 既然反码计算发生溢出时需要对结果最低位+1,那么可以对码制的最低位提前+1,这里给出补码的函数定义:
- 小数
x
补
码
=
x
真
值
?
m
o
d
?
2
=
{
x
,
1
>
x
≥
0
2
+
x
=
2
?
∣
x
∣
,
0
>
x
≥
?
1
(5)
x_{补码}= x_{真值}\:mod\:2= \begin{cases} x,\quad 1> x \geq 0\\ 2+x=2-|x|, \quad 0> x\geq-1 \end{cases} \tag{5}
x补码?=x真值?mod2={x,1>x≥02+x=2?∣x∣,0>x≥?1?(5) - 整数
x
补
码
=
x
真
值
?
m
o
d
?
(
2
n
)
=
{
0
,
x
2
n
>
x
≥
0
2
n
+
x
=
2
n
?
∣
x
∣
0
>
x
≥
?
2
n
?
1
(6)
x_{补码}= x_{真值}\:mod\:(2^n)= \begin{cases} 0,x\quad 2^n> x \geq 0\\ 2^{n}+x=2^n-|x| \quad 0> x\geq-2^{n-1} \end{cases} \tag{6}
x补码?=x真值?mod(2n)={0,x2n>x≥02n+x=2n?∣x∣0>x≥?2n?1?(6) - 补码完美的弥补了上面反码的缺点,补码中真值0的表示是唯一的,也正因为真值0的表示唯一
- 所以小数的补码可以使用1(符号位).0000…00(n-1个0)表示-1,小数的范围就是——
[
?
1
,
1
?
2
?
(
n
?
1
)
]
[-1,1-2^{-(n-1)}]
[?1,1?2?(n?1)]
- 整数的补码可以使用1(符号位)0000…00(n-1个0)表示
?
2
n
?
1
-2^{n-1}
?2n?1,所以整数的补码范围是——
[
?
2
n
?
1
,
2
n
?
1
?
1
]
[-2^{n-1},2^{n-1}-1]
[?2n?1,2n?1?1]
- 重点难点——补码的设计原理(为什么设计补码)
从某种意义上说,真正符合同余运算的是补码,在拨码中,对模运算的m加1后,弥补了溢出时需要加1的缺点。现在计算机基本使用补码作为运算基础。如果理解了反码的设计,补码的理解就极为美好了。
还是以整数为例——当计算发生溢出时, 此时溢出的数对
2
n
2^n
2n模运算,相当于丢弃了溢出位,因为码值的本身已经+1,所以使用补码的运算发生溢出时可以直接抛弃溢出的位即可。
补码总结:无论小数还是整数,补码其实都可以看作从反码转换而来,所以,补码的的形式就是符号位标明正负,正数补码与原码相同,负数符号位不变,原码的数值位全部取反然后最低位+1。 补码几乎是现代计算机底层最基本的数据码制的表示方式,在计算机中,补码的运算较为高效后面介绍的计算机的加减法、乘法都用到了补码。
4.移码
下面来一些轻松的内容(一万五千字了,🙏,学习到这里的同学可以鼓掌啦)
移码只能表示整数
首先给出移码的函数定义:
x
移
码
=
x
?
+
?
2
n
?
1
(
2
n
?
1
?
1
≥
x
≥
?
2
n
?
1
)
x_{移码}= x\:+\:2^{n-1}(2^{n-1}-1\geq x\geq-2^{n-1})
x移码?=x+2n?1(2n?1?1≥x≥?2n?1)
移码又叫做增码或者偏置码,它是在数
x
x
x 上增加一个偏移量(一般取
2
n
?
1
2^{n-1}
2n?1)来定义的,通常用来表示浮点数的阶码。移码其实相当于
x
x
x相对于
x
x
x最小值的偏移量,其表示形式类似于补码,只是移码符号位用 1 来表示正数,0 来表示负数,则数值表示部分则是与补码相同。
以8位机器字长为例,这里给出补码,移码对应表:
十进制 | 补码 | 移码 |
---|
-128 | 1000 0000 | 0000 0000 | -127 | 1000 0001 | 0000 0001 | -126 | 1000 0010 | 0000 0002 | … | … | … | -3 | 1111 1101 | 0111 1101 | -2 | 1111 1110 | 0111 1110 | -1 | 1111 1111 | 0111 1111 | 0 | 0000 0000 | 1000 0000 | 1 | 0000 0001 | 1000 0001 | 2 | 0000 0010 | 1000 0010 | 3 | 0000 0011 | 1000 0011 | … | … | … | 125 | | 1111 1101 | 126 | | 1111 1110 | 127 | 0111 1111 | 1111 1111 |
移码总结 1.移码中的0表示唯一 2.移码与码符号位表示相反,移码符号位用 1 来表示正数,0 来表示负数,则数值表示部分则是与补码相同。 3.移码全0时,对应真值的最小值
?
2
n
?
1
-2^{n-1}
?2n?1;移码全1时,对应真值的最大值
2
n
?
1
?
1
2^{n-1}-1
2n?1?1 4.移码保持了数据原有的大小顺序,移码大真值就大,移码小真值就小。
|