龙三对《深入理解计算机系统(原书第2版)》的笔记(10)

深入理解计算机系统(原书第2版)
  • 书名: 深入理解计算机系统(原书第2版)
  • 作者: [美] Randal E.Bryant/[美] David O' Hallaron
  • 页数: 702
  • 出版社: 机械工业出版社
  • 出版年: 2011-1-1
  • 第23页 2.信息的表示和处理

    <!---------------原文开始:公式不支持原文的分界线-------------------> 当值x是2的非负整数n次幂时,也就是x=($2^n$), 我们可以很容易地将x写成十六进制形式,只要记住x的二进制表示就是1后面跟n个0。 十六进制数字0代表4个二进制0。所以,当n表示成i+4j的形式,其中0<=i<=3时,我们可以把x写成开头的十六进制数字为1(i=0)、2(i=1)、4(i=2)或者8(i=3),后面跟随着j个十六进制的0。比如,x=2048=($2^{11}$),我们有n=11=3+4*2,从而得到十六进制表示为0x800。 <!---------------原文结束:公式不支持原文的分界线-------------------> 很六比!512=($2^9$),n=9=1+4*2,所以十六进制表示为0x200。

    十进制和十六进制表示之间的转换需要使用乘法或者除法来处理一般情况。将一个十进制数字x转换为十六进制数,可以反复用16除x,得到一个商q和一个余数r,也就是x=q*16+r。 然后,我们用十六进制数字表示的r作为最低位数字,并且通过对q反复进行这个过程得到剩下的数字。 例如,考虑十进制314156的转换: 314156 = 19634 * 16 + 12 (C) 19634 = 1227 * 16 + 2 (2) 1227 = 76 * 16 + 11 (B) 76 = 4 * 16 + 12 (C) 4 = 0 * 16 + 4 (4) 从这里,我们能读出十六进制表示为 0X4CB2C
    引自 2.信息的表示和处理
    2012-12-12 21:38:22 回应
  • 第33页 练习题2.9

    通过混合三种不同颜色的光(红色、绿色和蓝色),计算机可以在视频屏幕或者液晶显示器上颜色色彩的画面。设想一种简单的方法,使得三种不同颜色的光,每种光都能打开和关闭,投射到玻璃屏幕上,如图:

    那么,基于光源R(红)、G(绿)、B(蓝)的关闭(0)或打开(1),我们就能得到8种不同的颜色:

    这此颜色中的每一种都能用一个长度为3的位向量来表示,我们可以对它们进行布尔运算。

    2012-12-13 15:35:46 回应
  • 第45页 2.2 整型表示

    在($x$)的补码表示中,位($x_{w-1}$)决定了($x$)是否为负,得到:

    在($u$)的无符号表示中,位($u_{w-1}$)决定了u是否大于或等于($2_{w-1}$),得到:

    有点绕,要求负值绝对值,求反加1,就对了。

    2012-12-14 14:55:31 回应
  • 第117页 3.程序的机器级表示
    int exchange(int *xp, int y){
        int x = *xp;
        *xp = y;
        return x;
    }
    movl 8(%ebp), %edx
    movl (%edx), %eax
    movl 12(%ebp), %ecx
    movl %ecx, (%edx)

    当过程开始执行时,过程参数xp和y 存储在相对于寄存器 %ebp 中地址偏移8 和 12的地方。指令1从存储器当中读出参数xp,把它存放到寄存器 %edx 中。指令2使用寄存器 %edx,并将x 读到寄存器 %eax中(调用函数后返回值都是存储在eax中)。 指令3和4将原参数y的值写入了xp指针指向的存储器位置,实现了 *xp = y。 ()相当于解引用? --------------------------------------------一些寄存器的通常作用: eax 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。存储返回值。 ebx 是"基地址"(base)寄存器, 在内存寻址时存放基地址。 ecx 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。 edx 则总是被用来放整数除法产生的余数。 esi/edi 分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, ds:esi指向源串,而es:edi指向目标串。 ebp 是"基址指针"(base pointer), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer)。 esp 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。在32位平台上,ESP每次减少4字节。

    2012-12-17 16:21:25 回应
  • 第161页 3.程序的机器级表示

    习题3.3.7 考虑正面的源代码,其中M和N是用#define声明的常数:

    int mat1[M][N];
    int mat2[N][M];
    
    int sum_element(int i, int j){
        return mat1[i][j] +mat2[j][i];
    }

    在编译这个程序中,GCC产生如下汇编代码:(i at %ebp +8, j at %ebp +12)

    movl 8(%ebp), %ecx
    movl 12(%ebp), %edx
    leal 0(,%ecx,8), %eax
    subl %ecx, %eax
    addl %edx, %eax
    leal (%edx, %edx, 4), %edx
    addl %ecx, %edx
    movl mat1(, %eax, 4), %eax
    addl mat2(, %edx, 4), %eax

    -------解: 在倒数第二行之前,eax 和 edx 中的值为: eax = i*7+j edx = j*5 + i 最后两行是对两个数组的具体元素取下标地址,4是int 元素的长度。 代入C中,mat1的行数M是7,mat2的行数N是5。

    2012-12-18 22:01:06 回应
  • 第196页 3.程序的机器级表示
    最多可以有6个整形(整数和指针)参数可以通过寄存器进行传递。寄存器按照指定的顺序来使用,使用的寄存器名对应于所传递的数据的大小。参数按照它们在参数列表中的顺序依次分配到这些寄存器中。小于64位的参数可以用64位寄存器相应的部分来访问。 例如,如第一个参数为32位的,就可以使用%edi来访问它。
    引自 3.程序的机器级表示

    书中的代码都是AT&T语法格式的汇编,而非常见的Intel格式,看一段时间就适应了。 --------------------一些小差别: 1 引用寄存器要在寄存器号前加百分号% 2 操作数排列是从源(左)到目的(右),如“movl %eax(源), %ebx(目的)” 3 使用立即数,要在数前面加符号$, 如“movl $0x04, %ebx” 更多可参见:AT&T汇编语法 -------------------关于lea: lea, load effective address, 加载有效地址. 指令形式是从存储器读数据到寄存器, 效果是将存储器的有效地址写入到目的操作数, 简单说, 就是C语言中的”&”. 假如某一变量 ,地址为0x00001234h,它里面存放的值为0xAABBCCDD,那么 mov $0x00001234h,(%eax) ;结果为eax=0xAABBCCDD mov $0x00001234h,%eax ;结果为eax=0x00001234h lea $0x00001234h, %eax ;结果为eax=0x00001234h 不管它里面存的是地址还是个地址还是什么,lea x(%eax), %eax 就是对%eax里的值进行加上x的运算,再赋给%eax,仅此而已,可以理解为优化加法运算的命令。 决定这个指令是取址还是取值的,是寄存器外是否有()修饰符。别被它给迷惑了!

    2012-12-20 14:41:37 回应
  • 第423页 编写调整缓存友好的代码
    1)让最常见的情况运行得快。程序通常把大部分时间都花在少量的核心函数上,而这些函数通常把大部分时间都花在了少量循环上。所以要把注意力集中核心函数的循环上,而忽略其他部分。 2)在每个循环内部缓存不命中数量最小。在其他条件(例如加载和存储的总次数)相同情况下,不命中率低的循环运行得更快。
    引自 编写调整缓存友好的代码

    参考函数sumvec:

    int sumvec(int v[N]){
     int i, sum = 0;
    
     for(i = 0; i < N; i++)
      sum += v[i];
     return sum;
    }

    假设v是块对齐的,字为4字节,调整缓存块引用都会得到下面的命中和不命中模式:

    扩展到多维数组:

    int sumarrayrows(int a[M][N]){
     int i,  j, sum = 0;
    
     for(i = 0; i < M; i++)
      for(j = 0; j< N; j++)
       sum += a[i][j];
     return sum;
    }

    如果行列倒置,则每次都不命中。

    2012-12-27 15:58:43 回应
  • 第464页 7)链接

    7.12 中main的汇编代码出现这么一句:

    sub $0x8,%ebp
    引自 7)链接

    很费解。在esp的百科中找到了答案: “使得栈指针自减,自减得到的内存应当能够被用来存储被调用函数的本地状态”。 .text节中<swap>过程,全局指针变量的地址(和引用的外部全局数组变量值)被存储在.data节——这个间接引用图示,很好地解释了%edx和(%edx)的区别: --------------------------------------------------.data --little endian 8049454 <buf>: 8049454: 01 00 00 00 02 00 00 00 804945C <bufp0>: 804945C: 54 94 04 08 -------------------------------------------------- --------------------------------------------------.text mov 0x804945C, %edx ... mov (%edx), %ecx -------------------------------------------------- :mov传送的是bufp0指向的地址中的值,即buf数组的首元素值(mov在不指定类型的时候,32位系统默认传送4字节,即 01 00 00 00)。 --------------------------------------------------.text mov 0x804945C, %edx mov 0x8049458, %eax ... mov %esp, %ebp movl $0x8049458, 0x8049458 mov %ebp, %esp mov (%edx), %ecx mov %eax, (%edx) mov 0x8049548, %eax mov %ecx, (%eax) -------------------------------------------------- :%edx即bufp0指针。 :%eax即*bufp1(值为02 00 00 00) :栈指针备份 :buf[1]存了自己的地址(据看雪和ChinaUnix说法,把立即数传送到存储地址,不是跨平台的行为 :栈指针还原 :%ecx即temp,获取*bufpo(01 00 00 00) :%eax即原buf[1]值(02 00 00 00 )传给bufpo指针首址,即buf[0] :将地址0x8049548的值(立即数$0x8049548)给%eax,即*bufp1 :%eax指向地址,即*bufp1,赋temp值。至此,完成 buf[0]和buf[1]的交换。 呵呵,Orz。

    2012-12-30 00:33:17 回应
  • 第502页 8.异常控制流
    编写一个叫做myecho的程序,它打印出它的命令行参数和环境变量。例如: unix> ./myecho arg1 arg2 Command line arguments: argv[ 0]: myecho argv[ 1]: arg1 ... Enviroment variables: envp[ 0]: PWD=/user0/droh/ics/code/ecf envp[ 1]: TERM=emacs ...
    引自 8.异常控制流
    int main(int argc, char **argv, char **envp){	
    	int exit_status = 0;
    
    	puts("Command line arguments:");
    	if(argc > 0)
    		for(int i=0; i<argc; i++)
    			printf("argv[%2d]:%s\n", i, argv[i]); 
    	puts("\nEnviroment variables:");
    	char **env = envp;
    	int j=0;
    	while(*env){
    		printf("envp[%2d]:%s\n", j, *env++);
    		j++;
    	}
    
    	return exit_status;
    }
    2013-01-02 16:54:02 3回应
  • 第567页 9.9.7 放置已分配的块
    一些常见的策略是 首次适配(first fit)、下一次适配(next fit)和最佳适配(best fit)。 首次适配是从头开始搜索空闲链表,选择第一个合适的空闲块。 下一次适配和首次适配很相似,只不过不是从链表的起始处开始每次搜索,而是从上一次查询结束的地方开始。 最佳适配检查每个空闲块,选择适合所需请求大小的最小空闲块。 …… 下一次适配是由Donald Knuth作为首次适配的一个替代品最早提出的,源于这样一个想法:如果我们上一次在某个空闲块里已经发现了一个匹配,那么很有可能下一次我们也能在这个剩余块中发现匹配。下一次适配比首次匹配运行起来明显快一些,尤其是当链表的前面布满了很多小的碎片时。 然而,一些研究表明,下一次适配的存储利用率要比首次适配低得多。 研究还表明,最佳适配比首次适配和下一次适配的存储器利用率都要高一些。
    引自 9.9.7 放置已分配的块

    ^ ^ 空间和时间,还是一个平衡的问题。

    2013-01-04 16:40:26 回应

龙三的其他笔记  · · · · · ·  ( 全部182条 )

徹底攻略 応用情報技術者教科書 令和7年度
2
ゼロから分かる! 図解クラシック音楽
1
AWS教科書 AWS認定ソリューションアーキテクトアソシエイト テキスト&問題集
1
日本語の森 JLPT N1
1
【令和6年度】 いちばんやさしい 基本情報技術者 絶対合格の教科書+出る順問題集
4
海德格尔导论
1
火花
1
苦手が消える作文スタイル: 型を使って4つの段落で書こう
1
日本語の森 JLPT N3
2
拯救与逍遥
4
阅读是一座随身携带的避难所
1
条顿骑士团
1
别再问我什么是嘻哈②
1
红宝书大全集 新日本语能力考试N1-N5文字词汇详解(超值白金版 最新修订版)
1
再見楊德昌
2
Bass Culture
1
叫魂
1
必须保卫社会
1
程序员的自我修养
1
给青年小说家的信
1
历史的终结与最后的人
2
货币金融学
1
Types and Programming Languages
15
无政府、国家和乌托邦
1
x86汇编语言
5
汇编语言(第3版)
3
Topology Without Tears
2
Category Theory
1
快学Scala
1
Cassandra Data Modeling and Analysis
1
西方的没落
5
Linux Network Administrator's Guide
1
图解TCP/IP (第5版)
1
Functional Thinking
1
You Don't Know JS: this & Object Prototypes
4
AngularJS: Up and Running, 2nd Edition
3
Solr in Action
1
Behind Closed Doors
3
经过省察的人生
1
RESTful Web Services Cookbook
1
Getting Started with OAuth 2.0
1
High Performance JavaScript
4
Algorithms on Strings, Trees and Sequences
1
商业无边界
1
Node.js in Action
3
Maven
1
右派国家
1
Learning Java
2
Apache Cookbook
1
SQL Antipatterns
6
Pro Git
2
The Definitive Guide to Django, 2nd Edition
4
PostgreSQL
1
Learning Python, 5th Edition
9
声西击东
2
HTTP权威指南
2
Dive Into Python 3
2
Pro Python
2
UNIX编程艺术
2
Objective-C 2.0程序设计
3
邓小平时代
3
C++程序设计语言
7
算法导论(原书第2版)
1
C和指针
9
Go语言编程
4
我的Flex我精通
1
iOS软件开发揭密
8
童年的消逝
7