CSAPP考前题
一、系统漫游线上测试
1.以下说法正确的是( )。
a.处理器顺序执行机器指令。
b.主存储器包括寄存器。
c.总线系统只用来传输数据,不传输指令。
d.中央处理器(CPU)是特定指令集架构下的执行单元。
答案:D
解析:
- a项,目前多半是指令级并行、流水线技术,未必顺序执行;
- b项,著储存器与寄存器是两种不同的存储类型。寄存器是CPU内部的高速存储单元,主要用于临时存储数据和指令,而主存储器是外部于CPU的,通常用来存储程序和数据。
- c项,总线系统可以传输指令。
2.可执行目标程序是( )。
a.在目标机运行的汇编语言程序。
b.是机器指令被按照固定格式打包的二进制文件。
c.由编译器产生的汇编程序。
d.在目标机运行的高级语言程序。
答案:B
3.SHELL的功能是( )。
a.一个操作系统的命令行解释器。
b.只能接受系统命令。
c.一个操作系统。
d.不能运行可执行文件。
答案:A
4.以下关于内存管理的说法错误的是:( )。
a.显式链表分配时间与空闲块的数量成线性关系
b.标记清除垃圾收集算法是由程序员手动编码调用触发的
c.隐式链表双向合并可以常量时间完成
d.分离链表的First-fit 搜索近似于整个堆上的best-fit搜索
答案:B
解析:
- b项,标记清除垃圾收集算法通常是自动执行的,而不是由程序员手动调用触发的。。
5.可执行目标程序是( )。
a.是机器指令被按照固定格式打包的二进制文件。
b.在目标机运行的高级语言程序。
c.由编译器产生的汇编程序。
d.在目标机运行的汇编语言程序。
答案:A
6.有关存储的层次结构说法正确的是( )。
a.从上至下访问速度下降,存储容量增大。
b.磁盘存储和主存的地址空间连续。
c.寄存器与高速缓存都是对程序员透明的。
d.高速缓存和主存具有相同硬件结构。
答案:A
解析:
- b项,磁盘存储(例如硬盘)和主存(RAM)在地址空间上并不是连续的。主存和磁盘存储是两种不同的存储类型,操作系统管理它们的地址空间,并且它们的地址空间通常是分开的。
- c项,寄存器通常是 CPU 内部的存储单元,程序员可以通过汇编语言或编译器直接使用寄存器。而高速缓存(Cache)是对程序员透明的,程序员通常不需要直接管理或访问缓存。
- d项,高速缓存和主存的硬件结构是不同的。高速缓存通常采用更快的存储技术(如SRAM)以提高访问速度,而主存通常使用更大容量的存储技术(如DRAM)。
**7.磁盘文件a.txt由10个ASCII码字符“chinagood!”组成,下列程序运行后输出为( )。
#include “csapp.h”
int main(){
int fd1,fd2;
char c;
fd1=open(“a.txt”,O_RDONLY,0);
fd2=open(“a.txt”,O_RDONLY,0);
read (fd2,&c,1);
dup2(fd2,fd1);
read (fd1,&c,1);
printf(“c=%c\n”,c);
exit(0);
}
a.c=h
b.c=n
c.c=i
d.c=c
答案:A
解析:
打开文件:
- fd1 = open(“a.txt”, O_RDONLY, 0); // 打开 “a.txt” 文件,第一个描述符 fd1
- fd2 = open(“a.txt”, O_RDONLY, 0); // 打开 “a.txt” 文件,第二个描述符 fd2
这两行代码分别打开同一个文件 “a.txt” 两次,得到两个文件描述符 fd1 和 fd2。文件内容是 ASCII 字符 “chinagood!”,共 10 个字符。
从 fd2 读取一个字节:
- read(fd2, &c, 1); // 从 fd2 读取一个字节
这里从文件的当前指针位置读取一个字节到 c。此时文件指针指向 “c” 字符,因此 c 将被赋值为 ‘c’。同时,文件指针会向前移动一个字节,指向 “h”。
文件描述符复制 (dup2):
- dup2(fd2, fd1); // 使 fd1 复制为 fd2,fd1 和 fd2 都指向同一个文件描述符
dup2(fd2, fd1) 将 fd2 的文件描述符复制给 fd1,也就是说,fd1 和 fd2 都指向相同的文件。dup2() 不会影响文件指针的当前位置,因此文件指针依然指向原来 fd2 的位置(此时指向 “h”)。
从 fd1 读取一个字节:
- read(fd1, &c, 1); // 从 fd1 读取一个字节,实际上从文件的当前位置读取
由于 fd1 和 fd2 现在指向同一个文件描述符,而文件指针的位置是由上一个 read 操作改变的,因此此时读取的字节将是文件中的 “h” 字符。文件指针会再次向前移动一个字节,指向 “i”。
打印字符:
- printf(“c=%c\n”, c); // 打印读取的字符
由于第二次 read 从 fd1 读取的字符是 “h”,所以输出结果为:c=h
8.程序的生命周期是从( )开始的。
a.代码运行开始。
b.被程序员创建开始。
c.安装在系统后开始。
d.编译成可执行文件开始。
答案:B
9.程序运行性能的影响因素是:( )
A.算法、编程语言、编译器、指令集体系结构
B.指令序列和CPI
C.硬件是核心因素
D.操作系统是核心因素
答案:AB
10.进程上下文切换是有( )完成的。
A.用户代码和内核代码
B.操作系统
C.用户代码
D.内核代码
答案:BD
二、第二章线上测试
1.a,b是补码表示的有符号数整数,MIN_INT是最小整数,MAX_INT是最大整数,则表达式˜(˜a | (b ˆ (MIN_INT + MAX_INT)))等于:( )
A.a-b
B.a&b
C.a|b
D.a^b
答案:B
解析:
- MIN_INT+MAX_INT = -2^(n-1)+2^(n-1)-1=-1
- b^-1=~b
a|b=a&b
2.假定x,f,d分别为int,float,double数据类型,下列C语言中运算为真的是:( )。
A.x==(int)(double)x
B.(f+d)-f==d
C.x==(int)(float)x
D.d==(double)(float)d
答案:A
解析:
- b项,浮点数运算精度使之会有误差
- c项,先转换为float再转换为int,会丢失部分精度
- d项,先转换为float的时候会损失部分精度
3.假设8位浮点数采用IEEE格式,其中阶码4位,尾数3位。如果8位无符号数与浮点数比特位模式相同,则无符号数120代表哪个浮点数( )。
A.无穷大
B.非规格化数
C.规格化数
D.NaN
答案:A
解析:
符号位S,阶码E,尾数M
IEEE浮点数表示形式:(-1)^S * (1 + M) * 2 ^ (E - Bias)
Bias为偏置值,为2^(k-1)-1,k为阶码位数
对于无符号数120,表示为二进制:01111000
则S = 0, E = 15(全1), M = 000;实际阶码为8
判别规则:
- 规格化数:如果阶码不是全0(不是全1),并且尾数是规格化的(不是0),则是规格化数。
- 非规格化数:如果阶码为全0(即E=0),尾数不为0,则是非规格化数。
- 无穷大:如果阶码为全1且尾数为0,则表示无穷大。
- NaN:如果阶码为全1且尾数不为0,则表示NaN(非数)。
故选择A.无穷大。
**4.单精度浮点数f采用IEEE格式,unsigned u = (unsigned ) &f; int x = (u >> 31) & 0x1;则x表示f哪一部分 ( )。
A.尾数
B.偏置值
C.阶码
D.符号位
答案:D
解析:
IEEE格式中,浮点数的二进制表示由三部分组成:1位符号位,8位阶码,23位尾数
unsigned u = *(unsigned *) &f将浮点数 f 的内存表示(以位模式)转换为一个无符号整数 u。u 的二进制表示与 f 的二进制表示是相同的,只是解释的方式不同。
int x = (u >> 31) & 0x1;将u右移31位,得到符号位,然后将结果屏蔽掉其他位,最后得到的是符号位。
5.单选题:下列计算机存储和处理的信息表示说法正确的是:( )
A.不同类编码不能进行运算;
B.表示字符串是ASCII编码的字符数组,串长度是数组长度;
C.浮点数编码只包含阶码和尾数两部分;
D.表示代码是指令编码,不同芯片体系和操作系统指令编码不同;
答案:D
解析:
- a项,通过类型转换,将不同编码进行运算;
- b项,ASCII编码的字符串是以字符数组形式存储,但是字符串实际长度不等于有数组长度,其后有结束符\0
- c项,还包括符号位
6.单选题:关于除法说法正确的是:( )
A.补码除以2的k次幂时加上偏置导致向偶数舍入
B.补码除以2的k次幂时导致向0舍入
C.无符号数除法是算术右移
D.无符号除以2的k次幂是无符号右移k位
答案:D
解析:
- a、b项,补码进行除以2的k次幂操作加偏执会向0舍入;不加偏置的时候,如果除以负数未必是向0舍入。
- c项,无符号数除法是逻辑右移,
7.单选题:计算机存储和处理的信息表示为:( )
A.运算属性相同的三种编码二进制数。
B.无符号数,补码,浮点数;
C.十六进制数和二进制数;
D.通过无符号编码,补码编码,浮点数编码方式的二进制数;
答案:D
8.单选题:选择完成以下不同进制数之间转换表格空白项目的选项:( )
十进制 二进制 十六进制
188 (1) (2)
(3) 11110011 (4)
(5) (6) 0xE7
A.(1)10011100(2) BC (3)243 (4)0xF2 (5) 231(6)11110111
B.(1)10111101 (2) BC(3) 243(4) 0xF3(5) 231(6) 11110111
C.(1)10111111 (2)0xBC (3)243 (4) 0xF2(5)231(6) 11110111
D.(1)10111100 (2)0xBC (3)243 (4)0xF3 (5)231 (6) 11100111
9.无符号数x,y, 相加和为s,判断是否溢出的条件是:( )
A.s>x;
B.x+y>s;
C.不可判断;
D.s<x或y;
答案:D
解析:D项是一个必要条件。
10.当位模式w=4时,以下补码非正确的是:( )
A.-8的逆元是-8;
B.不存在;
C.反码减一是补码非;
D.8的补码非是-0;
答案:A
解析:
- a项,在4位补码中,-8表示1000,补码形式不可改变
- c项,举反例:对于-1,原码是1111,反码是1110,补码是1111,补码非是0000,不相等,故错误。
- d项,在4位补码表示法中,8是超出范围的,无法表示,自然不是-0。
三、第三章线上测试
1.根据以下递归程序,结论正确的是:( )
C递归程序如下:
long rfun (unsigned long x ){
if x==0
return 0 ;
unsigned long nx =(1);
long rv= rfun(nx);
return (2) ;
}
汇编代码如下:
1 rfun:
2 pushq %rbx
3 movq %rdi,%rbx
4 movl $0,%eax
5 testq %rdi, %rdi
6 je .L2
7 shrq $2,%rdi
8 call rfun
9 addq %rbx,%rax
10 .L2:
11 pop %rbx
12 ret
A.缺失表达式为:(1)x>>2,(2)rv
B.缺失表达式为:(1)x>>2,(2)x+rv
C.栈空间最多使用4字节
D.栈空间最多使用8字节
答案:B
解析:
- 根据代码,缺失的应该选择b。
- CD项目前未知。
2.根据代码,下列正确的是:( )
long scale2(long x, long y, long z){
long t=( );
Return t;
}
GCC汇编后如下(long x in %rdi, long y in %rsi, long z in %rdx):
scale2:
leaq (%rdi,%rdi,4) ,%rax
leaq (%rax,%rsi,2) ,%rax
leaq (%rax,%rdx,8) ,%rax
ret
A.long t=8z
B.long t=2y+8z
C.long t=4x+2y+8z
D.long t=5x+2y+8*z
答案:D
3.根据FIRST函数和LAST函数的反汇编代码,以及MAIN函数调用FIRST代码,如下:
1 400540
2 400540: 48 89 F8 MOV %RDI,%RAX L1
3 400543: 48 0F AF C6 IMUL %RSI, %RAX L2
4 400547: C3 L3
5 400548
6 400548: 48 89 77 01 LEA 0X1(%RDI), %RSI F1
7 40054C: 48 83 EF 01 SUB $0X1, %RDI F2
8 400550: E8 EB FF FF FF CALLQ 400540
9 400555: F3 C3 REPZ RETQ F4……
10 400560: E8 E3 FF FF FF CALLQ 400548
11 400565: 48 89 C2 MOV %RAX,%RDX M2
每条指令都有唯一标号(例如:L1),从main调用first(10)开始,到程序返回main为止,下表记录指令执行情况,以下选项对空白项填写正确的是:( )
标号 | PC | 指令 | 状态值(指令开始执行前) | 描述 |
---|---|---|---|---|
%rdi | %rsi | |||
M1 | 0x400560 | callq | 10 | - |
F1 | 0x400548 | (2) | 10 | - |
F2 | 0x40054c | sub | 10 | (4) |
F3 | 0x400550 | callq | 9 | 11 |
L1 | 0x400540 | mov | 9 | 11 |
L2 | 0x400543 | imul | 9 | 11 |
L3 | 0x400547 | retq | 9 | 11 |
F4 | (1) | repzretq | 9 | 11 |
M2 | 0x400565 | mov | (3) | 11 |
A.(1)0X40054C(2)mov(3)10(4) 11(5)9(6) 0X7FFFFFFFE810
B.(1)0X400565 (2)mov(3)10(4) 11(5)9(6) 0X7FFFFFFFE820
C.(1)0X400540 (2)mov(3)10(4) 11(5)9(6) 0X7FFFFFFFE810
D.(1)0X400555(2) LEA(3) 9(4)11(5) 9(6) 0X7FFFFFFFE818
答案:D
解析:
- 反汇编代码分析:
- 从 main 开始,进行 CALLQ 调用 FIRST,这时的指令是 M1。
- 进入 FIRST 函数(标号 F1 到 F4),并跟随其指令执行。
- FIRST 内部调用 LAST(标号 L1 到 L3)。
- LAST 完成后返回 FIRST,然后再次返回 main 并进行下一步指令(MOV %RAX, %RDX)。
- 空白表格填写
- F4 的 PC 列:这个位置应该是 0x400555,因为先执行 F3 的 CALLQ,再返回执行 F4 的 REPZ RETQ。所以 0x400555 正确。
- F1 的指令列:对应的 F1 行 的指令是 LEA,即48 89 77 01。
- M2 的 %rdi 值:从 LAST 返回后继续执行 main,此时 M2 行的 %rdi 是返回的结果 9。
- F2 的 %rsi: 在 F2 行,减去1后的 %rsi 值应为 11。
- L2 的 %rax: 在 L2 行,%rax 应为 99,因为计算乘积后的结果。
- M2 的 %rsp:从 MAIN 到 FIRST调用函数栈以及 RSP 值变化。
4.在x86-64上执行以下过程的代码片段:
int a[3];
char buf[4];
a[0]=0xF0F1F2F3;
a[1]=0xE3E2E1E0;
gets(buf);
在执行到gets时输入123456789,则执行后a[1]的内容为( )。
A.0xE3E23039
B.0xE3E20009
C.0xE3E20039
D.0xE3E2E1E0
答案:C
解析:
- 内存布局分析
- a[0]: 0xF0F1F2F3 (存储在偏移地址 0)
- a[1]: 0xE3E2E1E0 (存储在偏移地址 4)
- a[2]: 未初始化
- buf: [ ] [ ] [ ] [ ] (存储在偏移地址 12, buf 的大小是4个字节)
- 执行代码缓冲区溢出
- 1-4占据buf的四个位置,56789覆盖到a[1]
- 得到结果:0xE3E20039
- 覆盖过程目前未知。
5.如下结构体在x86-64上存储时,如果严格按照数据类型字节宽度对齐,不改变结构体成员存储顺序的情况下一个结构体需要多少字节?( )
struct T {
int i;
long l;
int* p;
};
A.16
B.12
C.20
D.24
答案:D
解析:
- int是4字节,long是8字节,指针是8字节.
- 但是还要加上字节对齐,都是8字节,3*8=24
6.选择给出操作数值的正确的选项:( )
地址 | 值 |
---|---|
0x100 | 0xFF |
0x104 | 0xAB |
0x108 | 0x13 |
0x10c | 0x11 |
寄存器 | 值 |
---|---|
%rax | 0x100 |
%rcx | 0x1 |
%rdx | 0x3 |
填写下表中操作数的值:
操作数 | 值 |
---|---|
0x104 | (1) |
$ 0x108 | (2) |
(%rax) | (3) |
9(%rax,%rdx) | (4) |
0xFC(,%rcx,4) | (5) |
(%rax,%rdx,4) | (6) |
A.(1)0xAB (2) 0x108(3) 0xFF(4) 0x11(5) 0xFF(6) 0x11 | |
B.(1)0xAB (2) 0x10c(3) 0xFF(4) 0x11(5) 0xFF(6) 0x11 | |
C.(1)0xAB (2) 0x108(3) 0xFF(4) 0x11(5) 0xAB(6) 0x11 | |
D.(1)0xFF (2) 0x108(3) 0xFF(4) 0x11(5) 0xFF(6) 0x11 |
答案:A
解析:
- 0x104:
- 直接查看地址0x104的值,得到 0xAB。
- (1) = 0xAB
- $0x108:
- 这是一个立即数,即直接取数值,得到 0x108。
- (2) = 0x108
- (%rax):
- %rax的值是0x100,查看地址0x100的值,得到 0xFF。
- (3) = 0xFF
- 9(%rax, %rdx):
- %rax = 0x100,%rdx = 0x3。
- 计算地址:0x100 + 0x3 + 9 = 0x100 + 0xC = 0x10C。
- 查看地址0x10C的值,得到 0x11。
- (4) = 0x11
- 0xFC(,%rcx,4):
- 这是一个带有乘法的地址计算,%rcx = 0x1。
- 计算地址:0xFC + 0x1 * 4 = 0xFC + 0x4 = 0x100。
- 查看地址0x100的值,得到 0xFF。
- (5) = 0xFF
- (%rax, %rdx, 4):
- %rax = 0x100,%rdx = 0x3。
- 计算地址:0x100 + 0x3 * 4 = 0x100 + 0xC = 0x10C。
- 查看地址0x10C的值,得到 0x11。
- (6) = 0x11
7.函数P生成a0-a7的局部变量,然后调用Q, P汇编代码如下,请判断以下说法正确的是:( )
Pushq %r15
Pushq %r14
Pushq %r13
Pushq %r12
Pushq %rbp
Pushq %rbx
Subq $24, %rsp
Movq %rdi,%rbx
Leaq 1(%rdi),%r15
Leaq 2(%rdi),%r14
Leaq 3(%rdi),%r13
Leaq 4(%rdi),%r12
Leaq 5(%rdi),%rbp
Leaq 6(%rdi),%rax
Movq %rax, (%rsp)
Leaq 7(%rdi),%rdx
Movq %rdx, 8(%rsp)
Movl $0, %eax
Call Q
A.a0-a5保存在了被调用者保存寄存器中
B.%rbx是被调用者保护寄存器
C.调用者栈帧中没有保存被调用者保护寄存器
D.有2个局部变量在调用者栈帧中
答案:ABD
解析:
前六条指令是压入栈的过程
接着分配24字节的栈空间
后面设置局部变量
然后调用函数Q
从leaq这部分可以看到他们都被保存在保护寄存器中,故A对C错
%rbx确实是被调用者保护寄存器
从倒数三五行代码看出a6和a7被保存在了调用者栈帧中。
8.在下面C函数中,OP操作定义不完整,根据编译代码,OP操作补充正确的是:( )
#define OP ——-
long arith(long x){
return x OP 8;
}
当编译时,GCC会产生如下汇编代码:
long arith(long x) x in %rdi
arith:
leaq 7(%rdi), %rax
testq %rdi, %rdi
cmovns %rdi, %rax
sarq $3,%rax
ret
A./
B.+
C.>>
D.-
答案:A
解析:注意到sarq那一行代码是要右移3位,即为除以8,那么这里选择A。
9.考虑下列C代码,通过汇编代码推断结果正确的是:( )
int comp(data_t a ,data_t b){
return a COMP b;
}
GCC编译后汇编代码:(a in %rdi,b in %rsi)
cmpw %si, %di
setge %al
A.后缀w和寄存器指示符表明是16位操作数,比较是对补码的>=,可推断data_t是short类型
B.无法推断
C.COMP是>操作
D.data_t是无符号数
答案:A
解析:注意到cmpw是对有符号数的比较,故D错,然后16位数是short。
10.已知C代码,以下编译后循环体描述正确的是:( )
long dw_loop(long x){
long y=x*x;
long p=&x:
long n=2x;
do {
x+=y;
(*p)++;
n–;
}while (n>0);
return;
}
gcc汇编代码如下:(x inintially in %rdi)
1 dw_loop:
2: movq %rdi, %rax
3: movq %rdi, %rcx
4: imulq %rdi, %rcx
5: leaq (%rdi, %rdi), %rdx
6: .L2:
7: leaq 1(%rcx,%rax), %rax
8: subq $1, %rdx
9: testq %rdx, %rdx
10: jg .l2
11: rep;ret
A.循环体中x变量在%rax中;
B.汇编代码未体现关于指针P的运算
C.汇编第7行代码,实现了x+=y+1
D.循环体中没有x的值
答案:AC
解析:
- b项:在汇编代码中,并没有直接看到对指针 p 的操作。(*p)++ 的效果是自增 x,而汇编代码通过 leaq 1(%rcx, %rax), %rax 实现了 x += y + 1,虽然不直接显示 p 的内容,但实际上 p 的自增效果通过 x 的更新表现出来。因此,这个选项不完全正确。
四、第四章线上测试
1.一个5级流水线(Fetch、Decode、Exuecution、Memory、Write Back),每段的组合逻辑耗时200ps,寄存器耗时50ps,整个流水线的吞吐率是:( )
a.4GIPS
b.5GIPS
c.20GIPS
d.10GIPS
答案:A
解析:吞吐率=1/时钟周期(秒) * 10^9
每阶段总耗时为200ps+50ps=250ps
吞吐率=1/250 * 10^9 = 4 * 10^9IPS = 4GIPS
2.以下属于精简指令集(RISC)的是:( )
a.Y86-64
b.所有都不是
c.MIPS
d.x86-64
答案:C
3.在PIPE流水线处理器中,对ret指令引起的控制冒险处理方式是( )。
a.取指阶段暂停,译码阶段插入气泡
b.在取指阶段插入气泡
c.取指阶段暂停,译码阶段暂停
d.取指阶段插入气泡,译码阶段插入气泡
答案:A
4.在PIPE流水线处理器中,对分支预测错误译码和执行流水线寄存器控制逻辑采取的动作是( )。
a.异常
b.气泡
c.暂停
d.正常
答案:BA
**5.在经典的五阶段流水线处理器中,load指令装载寄存器的操作发生在以下哪个阶段( )?
a.译码
b.写回
c.访存
d.执行
答案:B
解析:
流水线包括以下阶段:
取指阶段(IF):从内存中取出指令。
译码阶段(ID):解码指令并读取寄存器。
执行阶段(EX):进行算术或逻辑运算。
访存阶段(MEM):访问内存,读取数据。
写回阶段(WB):将数据写回到寄存器。load指令在协会阶段,从内存读取的数据才实际写入目标寄存器,故为写回。
五、第五、六章测试
1.以下关于代码优化的说法正确的是:( )
a.内存别名和函数调用对编译器优化带来较大挑战
b.面向一种体系架构优化后的代码也适用于其他的体系架构
c.编译器代码优化可以完全替代人工代码优化
d.高质量代码优化无需了解系统内部结构和运行过程
答案:A
解析:
- b项,代码优化通常是针对特定的体系架构进行的,不同的体系架构可能有不同的指令集、缓存结构等,因此在一种架构上优化的代码不一定在其他架构上表现良好。
- c项,虽然编译器可以进行很多自动化的优化,但某些情况下人工优化仍然是必要的。程序员可以根据具体的应用场景和算法特性进行更具针对性的优化,这些是编译器可能无法做到的。
- d项,高质量的代码优化通常需要对系统的内部结构和运行过程有一定的了解。了解硬件的特点(如缓存、内存管理等)和软件的执行流程可以帮助开发者做出更有效的优化决策。
2.以下关于循环展开和局部变量累加的说法错误的是:( )
a.循环展开次数应为并行累加次数的倍数
b.循环展开和并行累加优化效果受限于处理器流水线的深度
c.如果确定循环总次数是循环展开次数整数倍时,不需要添加第二个循环
d.多头并行累加一般使用多个局部变量完成
答案:B
解析:
- b项,处理器流水线的深度会影响并行计算的性能,并不影响优化效果。
3.在一台具有块大小16字节(B=16),1024字节的直接映射数据缓存机器上,运行以下代码,计算高速缓存性能正确的是:( )
struct algae_position{
int x;
int y;
};
struct algae_position grid[16][16];
int total_x = 0, total_y = 0;
int i, j;
For(i=0;i<16;i++){
For (j=0;j<16;j++){
total_x+=grid[i][j].x;
total_y+=grid[i][j].y;
}
}
a.读不命中次数为128次。
b.不命中率50%。
c.所有读操作有冲突不命中。
d.读缓存256次。
答案:A
解析:
缓冲块大小:16字节,总大小1024字节,缓存块数:1024/16=64个缓存块
总大小:16 * 16 * 8=2048字节,比1024字节大,会发生缓存替换
缓存的时候,同一行的访问会导致命中,不同行会不命中
不命中字数:16*14=224次不命中
实际不命中的次数应该是128次,因为缓存大小的限制和结构体排列方式。
a项,正确
b项,不命中率=127/512=25%
c项,并不是所有的都不命中
d项,要考虑命中和不命中的情况
4.在当代高性能处理器上进行程序优化的主要技术手段是:( )
a.使用临时变量进行累积
b.循环展开
c.所有都是
d.SIMD
答案:C
6.当处理器执行访存指令时,可能会触发的事件是:( )
a.所有都有可能
b.缺页中断
c.Cache填充
d.TLB填充
答案:A
六、第七、八章线上测试
1.A,B,C进程起始和结束时间如下表,以下进程对并发的是( )。
进程起始时间结束时间:
进程 | 开始时间 | 结束时间 |
---|---|---|
A | 0 | 1 |
B | 2 | 3 |
C | 2 | 4 |
a.BC。
b.AB。
c.无。
d.AC。
答案:BC
解析:并发要有同时工作的部分。
2.以下关于链接器的说法正确的是( )。
a.位置无关代码不需要链接。
b.动态链接发生在编译阶段。
c.静态链接生成的可执行目标文件中没有重定向节。
d.共享库只能在程序加载时链接。
答案:C
解析:
- a项,位置无关代码是一种编译策略,使得程序的代码可以加载到任何内存地址,不需要修改。它主要用于共享库,以便可以在程序运行时加载到不同的内存地址。
- 虽然位置无关代码在编译时生成,但它仍然需要链接。在编译时生成的目标文件通常包含符号引用,而链接器需要解决这些符号引用(包括函数地址、变量地址等),即使生成的是位置无关代码。
- b项,动态链接是指程序在运行时链接共享库,而不是在编译时就把所有的库链接到程序中。
- 动态链接通常发生在程序运行时,而不是编译时。编译时发生的是静态链接,链接器将所有需要的库文件和目标文件合并成一个可执行文件,而动态链接器在程序加载和执行时才加载共享库并链接符号。
- c项,静态链接 会将所有外部符号的引用(如库函数)替换为具体的地址,并将所有的目标文件合并成一个可执行文件。静态链接的结果是一个完整的可执行文件,其中已经包含了所有的库函数代码和符号地址。重定向节:静态链接过程中的一个重要步骤就是重定位,它将符号的地址替换为实际的内存地址。经过静态链接后,所有符号的地址都已经确定,因此不再需要重定位节(这些节只在目标文件中存在,执行文件已经不再需要)。
- d项,共享库(Shared Libraries) 是在程序运行时动态加载的库文件,它们可以通过动态链接在运行时被程序加载和链接,允许多个程序共享同一个库。
- 共享库不仅仅在程序加载时链接,它也可以在程序运行过程中进行动态加载(例如,使用 dlopen 函数在运行时显式加载共享库)。这种方式被称为运行时动态链接。
3.以下对异常控制流描述错误的是( )。
a.缺页中断不属于异常。
b.异常返回不一定到下一条指令。
c.中断是由硬件造成的异步异常。
d.陷阱是软件引起的同步异常。
答案:A
七、第九章线上测试
1.下列虚拟地址大小(n)和页大小(P)的组合所需要的PTE数量正确的是( )。
nP=2pPTE
数量164K
(1)12168K
(2)8324K
(3)512K328K
(4)1M
a.(4)
b.(1)
c.(3)
d.(2)
答案:D
解析:
- PTE数量=虚拟地址空间/页大小=2^n/p
2.以下关于虚拟内存的描述错误的是:( )
a.Intel i7处理器采用4级页表
b.查询TLB的输入为虚拟地址的一部分
c.多级页表中每级页表的内容相同
d.TLB丢失需要访问Cache或者内存
答案:C
解析:
- c项,多级页表在每一层中存储不同内容,因为表示的是不同级别的地址映射:
第一层(如 PML4)指向 PDPT。
第二层(如 PDPT)指向 PD。
第三层(如 PD)指向 PT。
最后一层(如 PT)直接指向物理内存页。
3.在一个TLB和L1d-cache的小系统上,内存按照字节寻址,一个字长1字节,虚拟地址14位,物理地址12位,页面大小64字节,TLB为四路组相联,16个条目,L1d-cache是物理寻址、直接映射,行大小4字节,共16组。对虚拟地址0x03d8的地址翻译正确的是( )。
参数值VPN(1)0x8TLB索引(2)0x8TLB标记(3)0x3TLB命中否?(4)否缺页?是PPN0x8
a.(3)
b.(2)
c.(1)
d.(4)
答案:A
解析:
- 首先解析虚拟地址,转换为2进制0x03d8 转换为二进制为:0000 0011 1101 1000。
- 偏移量:虚拟地址的低6位:110000(表示页内偏移,值为 0x38)。
- 虚拟页号(VPN):虚拟地址的高8位:00000011(表示虚拟页号,值为 0x03)。
- 接着分析TLB查找
- TLB索引:TLB有4个组,所以需要2位来作为索引($$2^2 = 4$$
)。虚拟页号 0x03(二进制:00000011)的低2位作为TLB索引,即 11(索引值为 0x3)。 - TLB标记:虚拟页号的高6位作为标记。虚拟页号 0x03(二进制:00000011)的高6位为 000000,所以 TLB 标记为 0x0。
- TLB索引:TLB有4个组,所以需要2位来作为索引($$2^2 = 4$$
- 查找TLB是否命中
- TLB查找:通过虚拟页号的高6位作为标记和低2位作为索引,TLB的索引为 0x3,标记为 0x0。
- TLB命中判断:根据题目中的信息,TLB 查找结果表明当前 TLB 没有命中(即该条目不在TLB中)。
- 处理TLB丢失(缺页)
- 因为 TLB 没有命中,处理器将访问内存中的页表。如果页表项有效,页表将返回物理页号(PPN)。从题目给出的信息,我们知道:
- 缺页:由于没有命中,发生了缺页。
- PPN:根据题目提供的信息,物理页号为 0x8。