计算机系统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,即所能表示最大的数,此时右边溢出,等式不成立
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 = ((unsigned)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<=C
void 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<=C
void 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);
//initialization
for (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. Name
0000000d 00000901 R_386_32 00000004 xp
00000014 00000c02 R_386_PC32 00000000 p3
0000001e 00000a02 R_386_PC32 00000000 p2
Relocation section '.rel.data' at offset 0x44c contains 1 entries:
Offset Info Type Sym.Value Sym. Name
00000004 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. Name
00000011 00000501 R_386_32 00000000 .rodata
Relocation section '.rel.rodata' at offset 0x434 contains 6 entries:
Offset Info Type Sym.Value Sym. Name
00000000 00000201 R_386_32 00000000 .text
00000004 00000201 R_386_32 00000000 .text
00000008 00000201 R_386_32 00000000 .text
0000000c 00000201 R_386_32 00000000 .text
00000010 00000201 R_386_32 00000000 .text
00000014 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
代码如下:文章来源:https://www.toymoban.com/news/detail-474578.html
#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.toymoban.com/news/detail-474578.html
到了这里,关于HNU-计算机系统-CSAPP作业答案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!