深入理解计算机系统
Tyrion-Lannister
这个作者很懒,什么都没留下…
展开
-
编写函数srl用算术右移来完成逻辑右移;函数 sra 用逻辑右移来完成算术右移。
int sra(int x, int k){ int xsrl = (unsigned) x >> k; int w = sizeof(int) << 3; return xsrl |= (-1 << (w-k));} unsigned srl(unsigned x, int k){ unsigned xsra = (int) x >> k;原创 2012-06-28 15:45:46 · 4338 阅读 · 9 评论 -
随笔二十六:IA32 机器代码和它的反汇编表示的特型值得注意
1、IA32 指令长度从 1 到 15 个字节不等。常用的指令以及操作数较少的指令所需的字节数少,而那些不太常用或操作数较多的指令所需字节数较多。2、设计指令格式的方式是,从某个给定位置开始,可以将字节唯一地解码成机器指令。3、反汇编器只是基于机器代码文件中的字节序列来确定汇编代码。它不需要访问程序的源代码或汇编代码。4、反汇编器使用的指令命名规则与 GCC 生成的汇编代码使用的有些细原创 2012-07-01 11:34:56 · 2772 阅读 · 0 评论 -
Return 1 when x can be represented as an n-bit, 2's complment number; 0 otherwise
int fits_bits(int x, int n){ int count = sizeof(int) << 3 - n; return ((x > count) == x;}原创 2012-06-30 10:23:30 · 2705 阅读 · 1 评论 -
编写函数实现“循环”右移
方法1:unsigned rotate_right(unsigned x, int n){ int mask = ~(0 - (1 << n - 1) - (1 << n -1)); mask &= x; int w = sizeof(unsigned) << 3; mask <<= w - n; x >>= n; return x | m原创 2012-06-30 09:56:35 · 1180 阅读 · 0 评论 -
编写函数实现:clear all but least signficant n bits of x
方法1:int lowe_bits(int x, int n){ int w = sizeof(int) << 3; unsigned y = ~0; y >>= (w - n); return y & x; }方法2:int lower_bits(int x, int n){ int mask = ~(0 - (1 << n - 1原创 2012-06-30 08:48:35 · 965 阅读 · 0 评论 -
编写函数,找到位向量最左边的1,将其后所有位置0
代码如下:int leftmost_one(unsigned x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return (x & ((~x >> 1) | 0x80000000)); }原创 2012-06-29 15:23:47 · 972 阅读 · 0 评论 -
从整数参数 x 中抽取出最高有效字节
int get_msb(int x){ int shift_val = (sizeof(int) - 1) << 3; int xright = x >> shift_val; return xright && 0xff;}原创 2012-06-28 14:48:15 · 1528 阅读 · 0 评论 -
编写一个函数int_shifts_are_logical(),在对 int 类型的数使用算术右移的机器上运行时,这个函数生成1,而其他情况下生成0
方法1:int int_shifts_are_logical(void){ int i = -1; i >>= 4; char *p = (char *)&i; return -(*p);}方法2:int int_shifts_are_arithmetic(){ int shift_val = (sizeof(int)-1)<<3; int原创 2012-06-28 14:45:54 · 2110 阅读 · 0 评论 -
随笔二十二:C 语言中的浮点数
C 语言标准不要求使用 IEEE 浮点,所以没有标准的方法来改变舍入方式或者得到诸如-0、+无穷、-无穷或者 NaN 之类的特殊值。大多数系统提供include('.h')文件和读取这些特征的过程库,但是细节因为西哦同的不同而不同。例如,当程序文件中出现下列句子时,GNU 编译器 GCC 会定义程序常数 INFINITY (表示+无穷)和NaN (表示 NaN)。#define _GNU_SO原创 2012-06-28 07:11:10 · 1009 阅读 · 0 评论 -
判断一无符号整数的二进制形式中是否包含偶数个1
题目要求:若二进制无符号整数x包含偶数个1,返回1,否则返回0.要求:只能使用位运算、加减法和==、!=,最多包含12个算术运算、位运算和逻辑运算,可以假设sizeof(unsigned)==4不能使用乘除模运算,不能使用条件分支,循环,函数调用,大小比较等(详见《深入理解计算机系统》第二章习题)代码如下:[cpp] view plaincopy转载 2012-06-28 16:27:40 · 1293 阅读 · 0 评论 -
写出下面 C 函数的代码,它会返回一个无符号值,其中参数 x 的字节 i 被替换成字节 b
方法1、unsigned put_byte(unsigned x, unsigned char b, int i){ assert(i >= 0 && i < sizeof(unsinged)); return ~(0xFF << 8 * i) & x | (b << 8 * i);}方法2、unsigned put_byte(unsigned x,原创 2012-06-28 14:10:41 · 2504 阅读 · 0 评论 -
随笔二十一:浮点运算
实数上的加法也形成了阿尔贝群,但是必须考虑舍入对这些属性的影响。加法运算是可交换的,另一方面,这个运算是不可结合的。浮点加法布局结合性,这是缺少的最重要的群属性。另一方面,浮点加法满足了单调性属性:如果 a >= b,那么对于任何 a、b 以及 x 的值,除了 NaN,都有 x + a >= x + b。无符号或补码加法不具备这个实数(和整数)加法的属性。 浮点乘法也遵循通常乘法原创 2012-06-28 06:47:26 · 1449 阅读 · 0 评论 -
大小端机器的判断-引发的思考
深入理解计算机系统中第二章节有一习题:编写过程is_little_endian,当在小端机器上编译和运行时返回1,在大端法机器上编译运行时则返回0。这个程序应该可以运行在任何机器上,无论机器的字长是多少?其参考答案如下:There are many ways to solve this problem. The basic idea is to create some multib转载 2012-06-28 08:42:48 · 1598 阅读 · 0 评论 -
随笔三十:特殊的算术操作
下表描述的指令支持产生两个 32 位数字的全 64 位乘积以及整数除法。指令效果描述 imull S mull S R[%edx]:R[%eax] S * R[%edx] R[%edx]:R[%eax]原创 2012-07-09 16:58:36 · 1304 阅读 · 0 评论 -
编写函数,当正溢出时,返回TMax,负溢出时,返回TMin。
int saturating_add(int x, int y){ int nbit = sizeof(int) << 3; int result = x + y; /* `of' means overflow.It will be 111...11111 if overflowed,0000...000 otherwise */ int of = ( ( x ^原创 2012-06-30 11:55:35 · 4580 阅读 · 3 评论 -
随笔二十三:为什么要学习机器代码?
1、通过阅读汇编代码,理解编译器的优化能力,分析代码中隐含的低效率。2、了解有关程序运行时行为的信息。(高级语言的抽象层隐藏了这些) eg:1)当用线程包写并发程序时,知道存储器存储不同的程序变量的区域是很重要的。这些信息在汇编代码级是可见的。 2)程序遭受攻击的许多方式中,都涉及程序存储运行时控制信息方式的细节。许多攻击利用了系统程序中的漏洞原创 2012-06-30 15:00:06 · 845 阅读 · 0 评论 -
CPU的大小端模式
不同体系结构的CPU,数据在内存中存放的排列顺序是不一样的。存储器中对数据的存储是以字节(Byte)为基本单位的,因此,字(Word)和半字(Half-Word)在存储器中就有两种次序,分别称为:大端模式(Big Endian)和小端模式(Little Endian)。大端存储模式是指字或半字的最高字节(Most Significant Bit,MSB)存放在内存的最低位字节地址上转载 2012-07-30 10:14:05 · 1431 阅读 · 0 评论 -
应考虑字节顺序(大小端问题)的几种情况
(From《Computer Systems,A Programer's Perspective》)在几乎所有的机器上,多字节对象被存储为连续的字节序列,对象的地址为所使用字节序列中最低字节地址。某些机器选择在存储器中按照从最低有效字节到最高有效字节的顺序存储对象,这种最低有效字节在最前面的表示方式被称为小端法(little endian)(e.g.:Intel处理器上运行Linux,NT);某些转载 2012-07-30 10:08:47 · 1963 阅读 · 0 评论 -
随笔三十六:条件传送指令
实现条件操作的传统方法是利用控制的条件转移。这种机制简单而通用,但是在现代处理器上,它可能回非常的低效率。 数据的条件转移是一种替代的策略。这种方法先计算一个条件操作的两种结果,然后再根据条件是否满足从而选取一个。只有在一些受限制的情况下,这种策略才可行,但是如果可行,就可以用一条简单的条件传送指令来实现它。条件传送指令更好地匹配了现代处理器的性能特征。 基于条原创 2012-07-17 17:46:19 · 2395 阅读 · 0 评论 -
随笔三十五:循环
1、do-while 循环 do-while 语句的通用形式如下:do body-statementwhile(test-expr); do-while 的通用形式可以翻译成如下所示的条件和 goto 语句:loop: body-statement t = test-expr; if (t) goto l原创 2012-07-17 10:37:35 · 1081 阅读 · 2 评论 -
随笔三十一:条件码
除了整数寄存器,CPU还维护着一组单个位的条件码(condition code)寄存器,它们描述了最近的算术和逻辑操作的属性。可以检测这些寄存器来执行条件分支指令。最常用的条件码有: CF:进位标志。最近的操作使最高位产生了进位。可以用来检查无符号操作数的溢出。 ZF:零标志。最近的操作得出的结果为 0。 SF:符号标志。最近的操作得到的结果为原创 2012-07-16 11:23:49 · 1994 阅读 · 0 评论 -
随笔三十四:翻译条件分支
将条件表达式和语句从 C 语言翻译成机器代码,最常用的方式是结合有条件和无条件跳转。(有些条件可以用数据的条件转换实现,而不是控制的条件转移来实现。) C 语言中的 if-else 语句的通用形式模板是这样的:if (test-expr) then-statementelse else-statement 对于这种通用形式,原创 2012-07-17 07:58:34 · 808 阅读 · 0 评论 -
随笔三十三:跳转指令及其编码
跳转指令有两种跳转方式: 1)直接跳转 。即跳转目标是作为指令的一部分编码的; 2)间接跳转。即跳转目标是从寄存器或存储器位置中读出的。 直接跳转是给出一个标号作为跳转目标的,间接跳转的写法是‘*’ 后面跟一个操作数指示符。 表中所示的其他跳转指令都是有条件的——它们根据条件码的某个组合,或者跳转,或者继续执行代码序列原创 2012-07-16 15:05:44 · 1665 阅读 · 0 评论 -
随笔三十二:访问条件码
条件码通常不会直接读取,常用的使用方法有三种:1)可以根据条件码的某个组合,将一个字节设置为 0 或者 1;2)可以条件跳转到程序的某个其他的部分;3)可以有条件地传递数据。 对于第一种情况,下表描述的指令根据条件码的某个组合,将一个字节设置为 0 或者 1.这一类指令称为 SET 指令;它们之间的区别就在于他们考虑的条件码的组合是什么,这些名字的不同后缀指明了它们所原创 2012-07-16 14:26:00 · 1575 阅读 · 0 评论 -
随笔二十八:invalid instruction suffix for push'
主机环境 Fedora 17 X86_64编写pushl %ebp想执行cc -O1 -c test.sobjdump -d test.o想看看在目标代码中 pushl 指令编码为几个字节,但在执行第一个命令时出现错误:invalid instruction suffix for push'在网上查找信息,得到问题的原因及解决方法:64位汇编与32位汇编不大一样,原创 2012-07-03 08:55:17 · 12023 阅读 · 4 评论 -
寻址模式
大多数指令有一个或多个操作数,指示出执行一个操作中要引用的源数据值,以及放置结果的目标位置。IA32 支持的操作数格式: 1)立即数(imm,常数值),在 ATT 格式的汇编中,书写方式是‘$’ 后面跟一个用标准 C 表示法表示的整数。 2)寄存器,它表示某个寄存器的内容。用符号Ea来表示任意寄存器 a,用引用 R[Ea] 来表示它的值,这是将寄存原创 2012-07-02 14:10:20 · 4708 阅读 · 0 评论 -
随笔二十五:IA32 机器代码中一些对 C 程序员隐藏的处理器状态可见
1、程序计数器 (在 IA32 中,通常称为 “PC”,用 %eip 表示)指示将要执行的下一条指令在存储器中的地址。2、整数寄存器文件 包含 8 个命名的位置,分别存储 32 位的值。这些存储器可以存储地址(相对于 C 语言的指针)或整数数据。有的寄存器被用来记录某些重要的程序状态,而其他的寄存器则用来保存临时数据,例如过程的局部变量和函数的返回值。3、条件码寄存器保存着最近执行的算数或原创 2012-07-01 10:21:52 · 1056 阅读 · 0 评论 -
随笔二十四:逆向工程
通过研究系统和逆向工作,来试图了解系统的创建过程。原创 2012-06-30 15:04:22 · 685 阅读 · 0 评论 -
检测两个整数相乘是否会产生溢出
1、对于 int 型:int tmult_ok(int x, int y){ int p = x * y; return !x || p / x == y;}2、如果使用64位表示,乘法则不会溢出。int tmult_ok(int x, int y){ long long pll = (long long) x * y; return pll =原创 2012-06-24 15:11:42 · 4190 阅读 · 0 评论 -
随笔十九:除以 2 的幂
在大多数机器上,整数除法要比整数乘法更慢——需要30个或者更多的时钟周期。除以 2 的幂也可以用移位运算来实现,只不过用的是右移,而不是左移。无符号和补码数分别使用逻辑移位和算术移位来达到目的。 整数除法总是舍入到零。对于使用算术右移的补码机器, C 表达式 (x > k 除以 2 的幂可以通过逻辑右移和算术右移来实现。这也是为什么大多数机器上原创 2012-06-25 08:47:21 · 1073 阅读 · 0 评论 -
交换两个数的方法总结
方法一:使用宏#define Exchage(a, b) \do{ \ a += b;\ b = a - b; \ a = a - b \}while(0)方法二:使用第三个变量void inplace_swap(int *a, int *b){ int tmp; tmp =原创 2012-06-22 14:52:38 · 723 阅读 · 0 评论 -
随笔九:操作系统的几个基本抽象
操作系统有两个基本功能:1)防止硬件被失控的应用程序滥用。2)向应用程序提供简单一致的机制来控制复杂而又通常大相径庭的低级硬件设备。操作系统通过几个基本的抽象概念(进程、虚拟存储器和文件)来实现这两个功能。 文件是对 I/O 设备的抽象表示,虚拟存储器是对主存和硬盘 I/O 设备的抽象表示,进程则是对处理器、主存和 I/O 设备的抽象表示。原创 2012-06-19 15:36:07 · 2538 阅读 · 0 评论 -
随笔八:存储器层次结构
存储器层次结构的主要思想是一层上的存储器作为低一层存储器的高速缓存。原创 2012-06-19 15:25:54 · 739 阅读 · 0 评论 -
随笔七:高速缓存的局部性原理
高速缓存的局部性原理,即程序具有访问局部区域的数据和代码的趋势。通过让高速缓存里存放可能经常访问的数据的方法,大部分的存储器操作都能在快速的高速缓存中完成。 重要结论:意识道高速缓存存在的应用程序员可以利用高速缓存将程序的性能提高一个数量级。原创 2012-06-19 14:52:12 · 2063 阅读 · 0 评论 -
随笔十一:并发与并行
并发(concurrency)是一个通用的概念,指一个同时具有多个活动的系统;而术语并行(parallelism)指的是用并发使一个系统运行更快。并行可以在多个抽象层次上运行用。按照系统层次结构中由高到低的顺序重点强调三个层次。 1、线程级并发; 2、指令级并发; 3、单指令、多数据并行;原创 2012-06-19 18:52:59 · 1180 阅读 · 0 评论 -
随笔十:虚拟存储器
虚拟存储器的运作需要硬件和操作系统软件之间精密复杂的交互,包括对处理器生成的每个地址的硬件翻译。其基本思想是把一个进程虚拟器的内容存储在硬盘上,然后用主存作为磁盘的高速缓存。原创 2012-06-19 17:17:12 · 565 阅读 · 0 评论 -
随笔五:处理器的指令集结构和微体系结构
指令集结构描述的是每条机器代码指令的效果;而微体系结构描述的是处理器实际上是如何实现的。原创 2012-06-19 14:32:16 · 1700 阅读 · 0 评论 -
随笔四:寄存器文件
寄存器文件是一个小的存储设备,由一些1字长的存储器构成,每个寄存器都有唯一的名字。原创 2012-06-19 14:29:41 · 1238 阅读 · 0 评论 -
随笔三:中央处理单元
处理器的核心是一个字长的存储设备(或寄存器),称为程序计数器(PC)。在任何时刻,PC 都指向主存中的某条机器语言指令(即含有该条指令的地址)。原创 2012-06-19 14:28:22 · 754 阅读 · 0 评论 -
随笔二十七:数据传送指令
将数据从一个位置复制到另一个位置的指令是最频繁使用的指令。下面介绍下 MOV 类指令,MOV 类由三条指令组成, movb、movw 和 movl。这些指令都执行同样的操作;不同的只是它们分别是在大小为 1、2 和 4 个字节的数据上进行操作。 指令效果描述 MOV S, D D S原创 2012-07-03 11:59:29 · 2323 阅读 · 0 评论