計算機系統(tǒng)CSAPP課后作業(yè)答案
計科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時有可能發(fā)生float無法精確表示該int而double可以精確表示該int的情況,而從float到double這一步不會發(fā)生問題,可以精確表示。所以可能等式不成立,下面給出反例。
反例:取x=67,108,863
其64位表示為0 10000011000 11111111111111111111111110……
(尾數有25個1,其階碼E=1048=1023+25,尾數M=1.11……(小數點后25個1))
而對其進行32位表示時由于32位的尾數只有23位,無法精確表示該數,所以會發(fā)生舍入,導致(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行,發(fā)現這兩行實際上算的是int n = bp->left + bp->right,對應bp->left地址就是一開始存在%ecx中的(bp),而bp->right是(bp+0xc8),也就是(bp+200),所以可以推斷a_struct[CNT]共占196字節(jié)。(這里bp表示0xc%ebp,為方便簡寫為bp)
然后我們可以分析含信息量最多的第10行,第9行我們可以看到%edx內是7i。來到第10行,%edx內存入的是【(bp+4+28i)所在內存的值+7i】,這里為什么加上7i有點費解,我留在后面探究,但是(bp+4+28i)應該是比較好看懂的,這個應該就是a[i]所在的地址,由此我們知道a_struct一個應該占28字節(jié),用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字節(jié),所以從這里可以驗證idx應該在結構中定義在前面;“+4*【(bp+4+28i)所在內存的值】”定位到bp->a[i]->x[bp->a[i]->idx],也就是ap->x[ap->idx]。
再回到解題上,一個a_struct共28字節(jié),int型的x占4字節(jié),剩下24字節(jié)應該就是一個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;
// 開始部分進行字節(jié)級的寫直到對齊
unsigned char *schar = (unsigned char *)s;
while ((size_t)schar % K != 0 && cnt < n)
{
*schar++ = (unsigned char)c;
cnt++;
}
// 合成longc滿足K個字節(jié),每個字節(jié)都是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;
}
// 結尾部分可能的未成字部分進行字節(jié)級的寫
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;
// 循環(huán)展開
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.
節(jié)偏移 | 重定位類型 | 符號名字 |
---|---|---|
0xd | 絕對引用 | xp |
0x14 | 相對引用 | p3 |
0x1e | 相對引用 | p2 |
B.
節(jié)偏移 | 重定位類型 | 符號名字 |
---|---|---|
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.
節(jié)偏移 | 重定位類型 | 符號名字 |
---|---|---|
0x11 | 絕對引用 | .rodata |
B.
節(jié)偏移 | 重定位類型 | 符號名字 |
---|---|---|
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
代碼如下:文章來源:http://www.zghlxwxcb.cn/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章
第一題
請完成《深入理解計算機系統(tǒng)》(第2版)P586588,9.119.13,請體現你的完成過程。
示例內存系統(tǒng)如下:
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;
}
}
使用程序前后:文章來源地址http://www.zghlxwxcb.cn/news/detail-474578.html
到了這里,關于HNU-計算機系統(tǒng)-CSAPP作業(yè)答案的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!