HNU-计算机系统-CSAPP作业答案
时间:2024-04-24 16:15:27 来源:网络cs 作者:付梓 栏目:卖家故事 阅读:17
计算机系统CSAPP课后作业答案
计科210X wolf 202108010XXX
第2章
2.61
解:
(!~x) || (!x) || (!~(x|0x00ffffff)) || (!(x&0x000000ff))
或者:
(!~x) || (!x) || (!~(x>>24)) || (!(x<<24))
2.71
A.
实现的是逻辑位移,扩展后前面全是0,不符合符号扩展的要求
B.
int xbyte(packed_t word,int bytenum){ return ((int)word<<((3-bytenum)<<3))>>24;}
2.87
格式A | 格式B | ||
---|---|---|---|
位 | 值 | 位 | 值 |
1 01110 001 | -9/16 | 1 0110 0010 | -9/16 |
0 10110 101 | 208 | 0 1110 1010 | 208 |
1 00111 110 | -7/2^10 | 1 0000 0111 | -7/2^10 |
0 00000 101 | 5/2^17 | 0 0000 0001 | 1/2^10 |
1 11011 000 | -2^12 | 1 1111 0000 | -inf |
0 11000 100 | 768 | 0 1111 0000 | +inf |
2.88
前置知识:
在IEEE754下,
32位包括符号位1位,阶码8位,尾数23位,其阶码偏置量为127;
64位包括符号位1位,阶码11位,尾数52位,其阶码偏置量为1023;
下面的讨论都在上述条件下进行
A. 不总是为1;
该式等价于(double)(float)x==(double)x,但因为将x转换为double和float时有可能发生float无法精确表示该int而double可以精确表示该int的情况,而从float到double这一步不会发生问题,可以精确表示。所以可能等式不成立,下面给出反例。
反例:取x=67,108,863
其64位表示为0 10000011000 11111111111111111111111110……
(尾数有25个1,其阶码E=1048=1023+25,尾数M=1.11……(小数点后25个1))
而对其进行32位表示时由于32位的尾数只有23位,无法精确表示该数,所以会发生舍入,导致(float)x!=(double)x
B. 不总是为1;
该式等价于(double)x+(double)y==(double)(x+y)
反例:取x=y=(1.11……1)*2^1023,即所能表示最大的数,此时右边溢出,等式不成立
【更正】反例:取x=y=MAX_INT=2^32-1(本机器上),x+y发生溢出,导致产生意外的double结果,实际上等式不成立。
C. 总是为1;
由加法交换律。即使溢出,其溢出值也相同,等式左右相等。
D. 不总是为1;
从一个角度说,若溢出,其溢出值可能不同,等式左右不一定相等。
从另一个角度,double无法精确表示2^64以内的所有整数,所以在第一次乘法后就已经开始产生误差了。
E. 不总是为1;
反例:取x=0,y≠0,有左边=inf≠右边
第3章
3.34
A.
是调用者接收到的x(也就是未被右移过的x)
B.
int rfun(unsigned x){ if (x == 0) return 0; unsigned nx = x >> 1; int rv = rfun(nx); return ((x & 0x1) + rv);}
C.
递归计算二进制数x中有多少个1(或表述为计算参数x中位的和)
3.56
A.
%esi保存x
%ebx保存n
%edi保存result
%edx保存mask
B.
Result初值为0x55555555
Mask初值为0x80000000
C.
测试条件为mask是否为0
D.
Mask右移(n&0x000000ff)位
E.
Result^= (mask & x)
F.代码如下
int loop(int x, int n){ int result = 0x55555555; int mask; for (mask = 0x80000000; mask != 0; mask = ((unsigned)mask) >> (n & 0x000000ff)) { result ^= (mask & x); } return result;}
3.59
补全代码如下:
int switch_prob(int x, int n){ int result = x; switch (n) { case 40: result <<= 3; break; case 41: result += 0x11; break; case 42: result <<= 3; break; case 43: result = result >> 3; break; case 44: result = ((result << 3) - x); result *= result; result += 0x11; break; case 45: result *= result; result += 0x11; break; default: result += 0x11; break; } return result;}
3.66
解:
先写答案
A.CNT=7
B.完整声明如下
typedef struct{ int idx; int x[6];} a_struct;
这题有点意思,我还想写一下****解题的过程****。
第13行的mov将%eax移到一个地址,此时的%eax里应该是计算出的n,所以结合第11和第12行,发现这两行实际上算的是int n = bp->left + bp->right,对应bp->left地址就是一开始存在%ecx中的(bp),而bp->right是(bp+0xc8),也就是(bp+200),所以可以推断a_struct[CNT]共占196字节。(这里bp表示0xc%ebp,为方便简写为bp)
然后我们可以分析含信息量最多的第10行,第9行我们可以看到%edx内是7i。来到第10行,%edx内存入的是【(bp+4+28i)所在内存的值+7i】,这里为什么加上7i有点费解,我留在后面探究,但是(bp+4+28i)应该是比较好看懂的,这个应该就是a[i]所在的地址,由此我们知道a_struct一个应该占28字节,用196÷28=7,可以得知CNT=7。
而且其实从这里也很好看出既然取的是(bp+4+28i),说明这应该就是idx的地址,也就是说在a_struct内部,idx先于x被定义。这一点后面还有验证。
最后我们重新分析第13行,这次我们分析mov的终点。是0x8(%ecx,%edx,4),也就是(4%edx+bp+8),由上面我们知道%dex内是什么,代入后,实际上mov的终点是(bp+4+28i+4+4*【(bp+4+28i)所在内存的值】)。
现在一切都清晰了!!“bp+4+28i”定位到a[i];“+4”是因为有idx是4字节,所以从这里可以验证idx应该在结构中定义在前面;“+4*【(bp+4+28i)所在内存的值】”定位到bp->a[i]->x[bp->a[i]->idx],也就是ap->x[ap->idx]。
再回到解题上,一个a_struct共28字节,int型的x占4字节,剩下24字节应该就是一个6位int数组,故int x[6]。
至此这道题应该就完全理顺了。
第5章
5.19
#include <bits/stdc++.h>void *my_memset(void *s, int c, size_t n){ size_t K = sizeof(unsigned long); size_t cnt = 0; // 开始部分进行字节级的写直到对齐 unsigned char *schar = (unsigned char *)s; while ((size_t)schar % K != 0 && cnt < n) { *schar++ = (unsigned char)c; cnt++; } // 合成longc满足K个字节,每个字节都是c的低位 unsigned long longc; unsigned char *temp = (unsigned char *)&longc; size_t i = 0; while (i < K) { *temp++ = (unsigned char)c; i++; } // 对齐后进行字级的写 unsigned long *slong = (unsigned long *)schar; while (cnt + K < n) { *slong++ = longc; cnt += K; } // 结尾部分可能的未成字部分进行字节级的写 schar = (unsigned char *)slong; while (cnt < n) { *schar++ = c; cnt++; } return s;}int main(){ char m[10]; my_memset((void *)m, 0x11223344, sizeof(m)); std::cout << (size_t)m % sizeof(unsigned long) << "\n"; for (size_t i = 0; i < sizeof(m) / sizeof(char); i++) { std::cout << "count " << i << ": " << std ::hex << (int)m[i] << "\n"; } return 0;}
测试结果:
5.20
double Polynomial_summation(double a[], double x, long degree){ long i; double sum0 = 0, sum1 = 0; double xpwr0 = 1, xpwr1 = x; double x_step = x * x; // 循环展开 for (i = 0; i < degree - 2; i += 2) { // 二路并行 sum0 = sum0 + a[i] * xpwr0; sum1 = sum1 + a[i + 1] * xpwr1; xpwr0 *= x_step; xpwr1 *= x_step; } // 补充完整剩余的部分 double sum_rest = 0; for (; i < degree; i++) { sum_rest = sum_rest + a[i] * xpwr0; } return sum0 + sum1 + sum_rest;}
第6章
6.1
25%
6.2
25%
6.3
25%
6.4
假设数据块的宽度为B,其生成由cache的容量C绝决定且有2*B^2<C,在此情况下B取最大值。
假设数据块的宽度为B,即每次读入B个数据,其生成由cache的容量C决定且有2*B^2<=C,在此情况下B取最大值。
以下为尝试的代码实现
#define B width_of_Block //2*B^2<=Cvoid transpose_AsSoonAsPossible(int *dst, int *src, int dim){ long border=dim*dim; for ( int i=0; i<dim; i+=B; ) { for ( int j=0; j<dim; j+=B; ) { //确定一次作用的数据块的起始位置(即块的左上角的那个位置) for ( int m=i; m<i+B; m++) { for ( int n=j; n<j+B; n++) { //对数据块的每一个位置进行操作 int dst_pos = n*dim + m; int src_pos = m*dim + n; if ( dst_pos<border && src_pos<border) //必须保证不能超出边界(矩阵不一定是阵) { dst[dst_pos]=src[src_pos]; } } } } }}
验证程序:
// 假设数据块的宽度为B,即每次读入B个数据,其生成由cache的容量C决定且有2 *B ^ 2 <= C, 在此情况下B取最大值。// 下为尝试的代码实现#define width_of_Block 4#define B width_of_Block // 2*B^2<=Cvoid transpose_AsSoonAsPossible(int *dst, int *src, int dim){ long border = dim * dim; for (int i = 0; i < dim; i += B) { for (int j = 0; j < dim; j += B) { //确定一次作用的数据块的起始位置(即块的左上角的那个位置) for (int m = i; m < i + B; m++) { for (int n = j; n < j + B; n++) { //对数据块的每一个位置进行操作 int dst_pos = n * dim + m; int src_pos = m * dim + n; if (dst_pos < border && src_pos < border) //必须保证不能超出边界 { dst[dst_pos] = src[src_pos]; } } } } }}int main(){int N=15; int *src=malloc(sizeof(int)*N*N); //initializationfor (int i=0;i<N;i++)for (int j=0;j<N;j++)src[i*N+j]=i;printf("original:\n");for (int i=0;i<N;i++){for (int j=0;j<N;j++)printf("%d ",src[i*N+j]);printf("\n");}int *dst=malloc(sizeof(int)*N*N); transpose_AsSoonAsPossible(dst, src, N); printf("\nanswer:\n"); for (int i=0;i<N;i++){for (int j=0;j<N;j++)printf("%d ",dst[i*N+j]);printf("\n");}free(dst);free(src);}
第7章
7.12
图7-10中的行号 | 地址 | 值 |
---|---|---|
15 | 0x80483cb | 0x804945c |
16 | 0x80483d0 | 0x8049458 |
18 | 0x80483d8 | 0x8049548 |
18 | 0x80483dc | 0x8049458 |
23 | 0x80483e7 | 0x8049548 |
7.13
代码如下
extern int ps(void);int x=1;int *xp=&x;void p2(int y){}void p1(){ p2(*xp+p3());}
将其保存为t2.c文件
使用gcc -c t2.c
生成可重定位的目标文件
使用objdump -D t2.o > m.txt
反汇编并保存在m.txt内。在m.txt查看.text和.data
Disassembly of section .text:00000000 <p2>: 0:55 push %ebp 1:89 e5 mov %esp,%ebp 3:5d pop %ebp 4:c3 ret 00000005 <p1>: 5:55 push %ebp 6:89 e5 mov %esp,%ebp 8:53 push %ebx 9:83 ec 14 sub $0x14,%esp c:a1 00 00 00 00 mov 0x0,%eax 11:8b 18 mov (%eax),%ebx 13:e8 fc ff ff ff call 14 <p1+0xf> 18:01 d8 add %ebx,%eax 1a:89 04 24 mov %eax,(%esp) 1d:e8 fc ff ff ff call 1e <p1+0x19> 22:83 c4 14 add $0x14,%esp 25:5b pop %ebx 26:5d pop %ebp 27:c3 ret
Disassembly of section .data:00000000 <x>: 0:01 00 add %eax,(%eax)...00000004 <xp>: 4:00 00 add %al,(%eax)...
使用readelf -a t2.o
可查看
Relocation section '.rel.text' at offset 0x434 contains 3 entries: Offset Info Type Sym.Value Sym. Name0000000d 00000901 R_386_32 00000004 xp00000014 00000c02 R_386_PC32 00000000 p30000001e 00000a02 R_386_PC32 00000000 p2Relocation section '.rel.data' at offset 0x44c contains 1 entries: Offset Info Type Sym.Value Sym. Name00000004 00000801 R_386_32 00000000 x
根据上述elf信息可推断
A.
节偏移 | 重定位类型 | 符号名字 |
---|---|---|
0xd | 绝对引用 | xp |
0x14 | 相对引用 | p3 |
0x1e | 相对引用 | p2 |
B.
节偏移 | 重定位类型 | 符号名字 |
---|---|---|
0x4 | 绝对引用 | x |
7.14
题目代码如下:
int relo3(int val){switch(val){case 100:return (val);case 101:return (val+1);case 103: case 104:return (val+3);case 105:return (val+5);default:return (val+6);}}
保存为t3.c文件
使用gcc -c t3.c
生成可重定位的目标文件
使用objdump -D t3.o > m.txt
反汇编并保存在m.txt内。在m.txt查看.text和.data
截图如下
查看m.txt中.text与.rodata段
Disassembly of section .text:00000000 <relo3>: 0:55 push %ebp 1:89 e5 mov %esp,%ebp 3:8b 45 08 mov 0x8(%ebp),%eax 6:83 e8 64 sub $0x64,%eax 9:83 f8 05 cmp $0x5,%eax c:77 26 ja 34 <relo3+0x34> e:8b 04 85 00 00 00 00 mov 0x0(,%eax,4),%eax 15:ff e0 jmp *%eax 17:8b 45 08 mov 0x8(%ebp),%eax 1a:eb 1e jmp 3a <relo3+0x3a> 1c:8b 45 08 mov 0x8(%ebp),%eax 1f:83 c0 01 add $0x1,%eax 22:eb 16 jmp 3a <relo3+0x3a> 24:8b 45 08 mov 0x8(%ebp),%eax 27:83 c0 03 add $0x3,%eax 2a:eb 0e jmp 3a <relo3+0x3a> 2c:8b 45 08 mov 0x8(%ebp),%eax 2f:83 c0 05 add $0x5,%eax 32:eb 06 jmp 3a <relo3+0x3a> 34:8b 45 08 mov 0x8(%ebp),%eax 37:83 c0 06 add $0x6,%eax 3a:5d pop %ebp 3b:c3 ret
00000000 <.rodata>: 0:17 pop %ss 1:00 00 add %al,(%eax) 3:00 1c 00 add %bl,(%eax,%eax,1) 6:00 00 add %al,(%eax) 8:34 00 xor $0x0,%al a:00 00 add %al,(%eax) c:24 00 and $0x0,%al e:00 00 add %al,(%eax) 10:24 00 and $0x0,%al 12:00 00 add %al,(%eax) 14:2c 00 sub $0x0,%al...
使用readelf -a t3.o
可查看
Relocation section '.rel.text' at offset 0x42c contains 1 entries: Offset Info Type Sym.Value Sym. Name00000011 00000501 R_386_32 00000000 .rodataRelocation section '.rel.rodata' at offset 0x434 contains 6 entries: Offset Info Type Sym.Value Sym. Name00000000 00000201 R_386_32 00000000 .text00000004 00000201 R_386_32 00000000 .text00000008 00000201 R_386_32 00000000 .text0000000c 00000201 R_386_32 00000000 .text00000010 00000201 R_386_32 00000000 .text00000014 00000201 R_386_32 00000000 .text
根据上述elf信息可推断
A.
节偏移 | 重定位类型 | 符号名字 |
---|---|---|
0x11 | 绝对引用 | .rodata |
B.
节偏移 | 重定位类型 | 符号名字 |
---|---|---|
0x0 | 绝对引用 | .text |
0x4 | 绝对引用 | .text |
0x8 | 绝对引用 | .text |
0xc | 绝对引用 | .text |
0x10 | 绝对引用 | .text |
0x14 | 绝对引用 | .text |
第8章
8.23
当父进程接收到第一个SIGUSR2信号之后,父进程进入信号处理例程。与此同时,由于隐式阻塞机制,剩余4个该类信号SIGUSR2被阻塞,并没有被接收。其中只有1个被视为待处理信号而被保存,其余的3个被丢弃。因此最终只有2个被处理,counter最终结果为2。
这是书上对于信号的基础概念。简单来说就是同一时间同一信号最多处理一个,挂起一个,丢弃剩余全部。
8.20
代码如下:
#include <unistd.h>#include <stdio.h>#include <stdlib.h> int main(int argc, const char *argv[], const char *envp[]){ if (execve("/bin/ls", argv, envp) < 0) { printf("/lib/ls not found.\n"); return -1; } return 0;}
测试结果:
第9章
第一题
请完成《深入理解计算机系统》(第2版)P586588,9.119.13,请体现你的完成过程。
示例内存系统如下:
9.11
答案:
9.12
答案:
9.13
答案:
第二题
设若有一个输入文件hello.txt,由字符串“Hello,World!\n”组成,编写一个C程序,使用mmap将该txt文件的内容修改为“Hello, HNU!\n”。
代码如下:
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <sys/mman.h> int main(){ int fd = open("hello.txt",O_RDWR,0); char *start = mmap(NULL,1,PROT_WRITE,MAP_SHARED,fd,0); ftruncate(fd,11); close(fd); if(start == MAP_FAILED)printf("error!\n");else{char* newstr="Hello,HNU!\n";char *temp = start;int i=0;for (; i<11;temp++,i++)*temp=newstr[i];munmap(start,1);return 0;}}
使用程序前后:
本文链接:https://www.kjpai.cn/gushi/2024-04-24/161887.html,文章来源:网络cs,作者:付梓,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!
下一篇:返回列表