深入理解操作系统(2)第二章:信息的表示和处理(1)信息存储(包括:有符号数和无符号/字节/虛拟地址空间/字/大小端 字节序/字符编码/逻辑运算/位运算/移位运算左移右移)
1. 概述
我们对计算机系统的探索是从学习计算机本身开始的,它由处理器和存储器子系统组成。
1.在核心部分,我们需要方法来表示基本数据类型,比如整数和实数运算的近似值。
2.然后,我们考虑机器级指令如何操作这样的数据,编译器如何将C程序编译成这样的指令。
3.接下来,我们研究几种实现处理器的方法,来更好地了解如何使用硬件资源来执行指令。
一旦我们理解了编译器和机器级代码,我们就能通过编写可以更高效编译的源代码,来分析如何最大化程序的性能。
4.我们以存储器子系统的设计来结束本部分,
这是现代计算机系统最复杂的部分之本书的这一部分将领着你深入了解应用程序是如何被表示和执行的。
5.你将学会大量编程技巧,从而可以编写更可靠并充分利用计算资源的程序。
1.1 二进制表示原因
现代计算机存储和处理以二值信号表示的信息。这些普通的二进制数字,或者位(bit),形成了数字革命的基础。
二值信号能够很容易地表示、存储和传输,例如,可以表示为穿孔卡片上有洞或无洞、导线上的高电压或低电压,或者磁场引起的顺时针或逆时针。基于二值信号的存储和执行计算的电子电路非常简单和可靠,使得制造商能够在一个单独的硅片上集成百万个这样的电路单独地来说,单个的位不是非常有用。然而,当我们把位组合在一起,再加上某种解释(interpretation),即给予不同的可能位模式以含意,我们就能够表示任何有限集合的元素。
1.2 无符号 二进制补码 浮点数
看之前请参考博客:详细讲解有符号数和无符号数
有符号数和无符号数详解
https://blog.csdn.net/lqy971966/article/details/106033332
有符号数和无符号数详解(2)补码详解
https://blog.csdn.net/lqy971966/article/details/106130830
1.无符号( unsigned)编码是基于传统的二进制表示法的,表示大于或者等于零的数字。
2.二进制补码(two' s-complement)编码是表示有符号整数的最常见的方式,
有符号整数就是为正或者为负的数字。
3.浮点数(floating-point)编码是表示实数的科学记数法的以二为基数的版本。
计算机用这些不同的表示方法实现算术运算,例如加法和乘法,类似于相应的整数和实数运算
2. 信息的存储
2.1 基本概念:
2.1.1 最小的可寻址的存储器单位-字节
大多数计算机使用8位的块,或叫做字节(byte),来作为最小的可寻址的存储器单位,而不是存储器中单独的位。
2.1.2 虚拟存储器
机器级程序将存储器视为一个非常大的字节数组,称为虚拟存储器(virtual memory)。
地址:
存储器的每个字节都个惟一的数字来标识,称为它的地址( address)
虛拟地址空间:
所有可能地址的集合就称为虛拟地址空间( virtual address space)
正如它的名字表明的,这个虚拟地址空间只是个展现给机器级程序的概念性映像( Image)。
实际的实现使用的是随机访问存储器RAM、磁盘存储、特殊硬件和操作系统软件的结合,来为程序提供一个看上去统一的字节数组编译器和运行时系统的一个任务。
就是将这个存储器空间划分为更可管理的单元,来存放不同的程序对象( program object),也就是,程序数据、指令和控制信息。
虚拟地址空间
https://blog.csdn.net/lqy971966/article/details/119378416
2.1.3 c指针:
例如,C中一个指针的值(无论它指向一个整数结构或是某个其他程序单元)都是某个存储块的第一个字节的虚拟地址
编译器还把每个指针和类型信息联系起来,这样它就可以根据指针值的类型,生成不同的机器级代码来访问存储在指针所指向位置处的值
2.2 十六进制表示法
十六进制(简写为“Hex”)使用数字“0”“9”,以及字符“A”~“F”来表示16个可能的值。
以0x或0X开头的数字常量被认为是十六进制的值
一个字节的取值范围为00~FF
例子:0x173A4C
0x1 7 3 A 4 C
0001 0110 0110 1010 0100 1100
2.2.1 字
每台计算机都有一个字长( word size),指明整数和指针数据的标称大小( nominal size)。
因为虚拟地址是以这样的字来编码的,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。
对于一个字长为n位的机器而言,虚拟地址的范围为0~2德n次方-1,程序最多访问2的n次方。
今天大多数计算机的字长都是32位。这就限制了虚拟地址空间为4千兆字节(写作4GB),也就是说,刚刚超过4x10字节。
虽然对大多数应用而言,这个空间足够大了,但是现在已经有许多大型的科学和数据库应用需要更大的存储了。因此,随着存储器价格的降低,字长为64位的高端机器正逐渐变得普遍起来。
2.3 数据大小
算机和编译器使用不同的方式来编码数字,比如不同长度的整数和浮点数,从而支持多种数格式。
比如,许多机器都有处理单个字节的指令,也有处理表示为两字节、四字节或者八字节整数的指令,还有些指令支持表示为四字节和八字节的浮点数。
2.2
在C语言中 32/64各类型占字节大小,cpu 的位
https://blog.csdn.net/lqy971966/article/details/117925766
2.4 寻址和字节顺序
2.4.1 大小端
可以看这篇博客:
通俗易懂说字节序,大小端,网络序和主机序
https://blog.csdn.net/lqy971966/article/details/93602177
字节序:
顾名思义字节的顺序,
就是大于一个字节类型的数据在内存中的存放顺序。
大小端:
大端:就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端
小端:就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端
例子:
数据: 0x1234
地址: 0x00 0x01
大端: 12 34
小端: 34 12
网络字节序:
TCP/IP各层协议将字节序定义为Big-Endian
2.4.2 反汇编器
反汇编器是一种确定可执行程序文件所表示的指令序列的工具。
示例,从某个文件中摘出了下面这行代码,该文件给出了一个针对 Intel处理器的机器级代码的文本表示
80483bd 01 05 64 94 04 08 add %eax, 0x8049464
这一行是由反汇编器( disassembler)生成的
这行表述了十六进制字节串010564940408是一条指令的字节级表示,这条指令是增加一个字宽的数据到存储在主存地址0x8049464的值上。
取出这个序列的最后四字节:64940408,并且按照相反的顺序写出,我们得到08049464。
去掉开头的零,我们就得到0x8049464,就是右边写着的数值。
当阅读像此例中一样的小端法机器生成的机器级程序表示时常会将字节按照相反的顺序显示。
2.4.3 typedef
typedef 是用来定义一种数据类型的别名,增强程序的可读性
typedef 是编译过程的一部分
typedef 有正确性和安全性检查
typedef 定义的是语句,所以要加分号;
例子:
typedef char * PCHAR1;
/* c1、c2 都为char *,typedef为char *引入了一个新的别名*/
PCHAR1 c1, c2;
typedef 和 #define 的区别
https://blog.csdn.net/lqy971966/article/details/110946843
2.4.4 sizeof
sizeof 是运算符;strlen 是函数。
strlen 只能用char *做参数,而且必须是以"\0"结尾的。
sizeof 编译时执行;strlen 运行时计算出来,计算字符串的长度
例子在这里
sizeof 和 strlen区别
https://blog.csdn.net/lqy971966/article/details/99615545
2.5 表示字符串
C中的字符串被编码为一个以NULL(其值为零)字符结尾的字符数组。
每个字符都由某种编码来表示,最常见的就是ASCI字符码。
ASCII Unicode 等字符编码
通俗易懂说–字符编码(ASCII,GB2312,GBK,Unicode,UTF-8/16/32,BOM)
https://blog.csdn.net/lqy971966/article/details/89284662
2.6 表示代码
2.1.6
这里我们发现除了NT和 Linux机器以外,指令编码是不同的。
不同的机器类型使用不同的且不兼容的指令和编码方式。
NT和 Linux机器使用的都是 Intel处理器,因此支持相同的机器级指令。
二进制代码很少能在不同机器和操作系统组合之间移植。计算机系统的一个基本概念就是从机器的角度来看,程序仅仅只是字节序列。机器没有关于原始源程序的任何信息,除了可能有些用来帮助调试的辅助表以外。
2.7 布尔代数简介
起源于1850年左右的乔治.布尔的工作,因此也称为布尔代数(Bool algebra)。
2.7.1 布尔代数运算:
取反 ~ 如:~0=1
与 & 如:0&1=0 1&1=1
或 | 如:0|1=1
异或 ^ 如:0^0=0 0^1=1 同为0,异为1
2.7.2 整数环合布尔代数
后来创立信息理论领域的 Claude shannon首先建立了布尔代数和数字逻辑之间的联系。
整数集合,用Z来表示,形成了一个称为环的数据结构,
2.8 c语言中的位级运算
C的一个很有用的特性就是它支持按位布尔运算。
或|就是OR
与&就是AND
非~就是NOT,
异或^就是 EXCLUSIVE-OR
2.9
2.8.1 位级运算应用(X&0xFF 和 ~0)
位级运算的一个常见用法就是实现掩码运算
-
掩码0xFF(最低有效八位为1)表字的低位字节
位级运算 X&0xFF 生成一个由X的最低有效字节组成的值,而其他的字节就被置为了0x=0x89ABCDEE 则x&0xFF=0xEE
~0 全1的掩码
例子:#define QUEUE_INFINITE_DEPTH (~0UL)
0UL是无符号长整型 0, ~ 表示按位取反,即:0xffffffff 最大值
参考:#define(1)define常用操作
https://blog.csdn.net/lqy971966/article/details/105043345
2.9 c语言中的逻辑运算
C还提供了一系列的逻辑运算符‖、&&和!,分别对应于命题逻辑中的OR、AND和NOT运算。
逻辑运算很容易和位级运算相混淆,但是它们的功能是完全不同的。
逻辑运算认为所有非零的参数都表示TRUE,而参数零表示 FALSE。它们返回1或者0,分别表示结果为TRUE或者为 FALSE。
例子:
!0x33 = 1
!0x00 = 1
!!0x33 = 1
0x33&&0x44 = 1
0x33&&0x00 = 0
0x33||0x00 = 1
逻辑运算符&&和与它们对应的位级运算&和之间第二个重要的区别是,如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符不会对第二个参数求值。因此,例如,表达式a&&将不会造成被零除,而表达式p&&*p++也不会导致间接引用空指针
2.9.1 移位运算(‖、&&和!)与逻辑运算(| & ~ ^)的区别
区别1:功能不同,移位运算只有1和0两种结果;而逻辑是对位的操作
区别2:如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符不会对第二个参数求值。
2.10 c语言中的移位运算
C还提供了一系列的移位运算,以向左或者向右移动位模式。
2.10.1 左移,右边补0
2.10.2 右移,分为逻辑右移和算术右移
逻辑右移,左边补0
算术右移,左边补最高有效位的拷贝(应用于有符号位的运算)
规定:
对于无符号数据,右移必须是逻辑的
对于有符号数据,几乎所有编译器对其进行算术右移,程序员也是这样使用;
例子:
int iTest = 8;
unsigned int uiTest = -8;
printf("iTest=%x\n",iTest>>1);
printf("uiTest=%x\n",uiTest>>1);
结果:
iTest=4
uiTest=7ffffffc
说明:8 1000 右移1位 0100 4
-8 源码:0x10 00 00 08
反码:0xff ff ff f7
补码:0xff ff ff f8(1111 1000)
右移1位 0xff ff ff fc(1111 1100)
C语言-移位,位域,和运算符优先级
https://blog.csdn.net/lqy971966/article/details/99682879