湖南大学CS-2024期末考试解析

这是HNU 2024年计算机系统的期末试卷的整理,答案是我和同学还有AI核对了的,但可能仍有不足之处,欢迎大家在评论区讨论。

本文章由于附带了一些解析过程,所以内容可能有些冗长。我将答案放在了每道题的开头部分,需要对答案的同学可以根据目录快速跳转。
相应的,解析部分在答案的后面。

原卷在这里

试卷的 PDF 版本

个人感觉:题型总体上比较符合考点,不算太难,可以在两小时之内完成

注:答案的解析部分绝大部分由AI完成

以下是答案及解析部分

一、简答题(10 分)

请用 32 位补码整数和 IEEE 754 单精度浮点数格式分别表示 2049 这个值(结果请用16 进制表示)。(7 分)
请问两种表示方法二进制序列有相同的部分吗?为什么?(3 分)

答案

(1)

  • 2049 的 32 位补码: 0x00000801

  • 2049 的 IEEE 754 单精度: 0x45001000

(2)

回答:
有相同的部分:2049 可以精确表示为浮点数,其浮点尾数部分中的 00000000001(表示 2⁻¹¹)恰好与补码中低 11 位 00000000001 相同;

解释:
任何正整数 N N N 的二进制补码可写成     1   b k − 1   b k − 2 … b 0 \,1\,b_{k-1}\,b_{k-2}\dots b_{0} 1bk1bk2b0(最高位为 1,后面跟 k k k 位)。

而浮点数的归一化格式为:

N    =    1. ( b k − 1 b k − 2 … b 0 ) 2    ×    2   k . N \;=\; 1.\bigl(b_{k-1}b_{k-2}\dots b_{0}\bigr)_2 \;\times\;2^{\,k}. N=1.(bk1bk2b0)2×2k.

  • 当整数的有效二进制位数 ≤ 24(包括最高位 1),其最高位之后的位会完整出现在浮点尾数的高位部分。
  • 由于浮点数中小数点前的 1 是隐含的,不会存储在尾数中,所以补码的最高有效位不会出现在尾数里。

解析

一、32位补码整数表示

  • 2049 的二进制(原码)是:
0000 0000 0000 1000 0000 0001

其中,2049 = 2¹¹ + 1,对应一共 12 位有效二进制位,高位全部补零。

  • 作为 32 位补码,符号位为 0,剩余高位用 0 填充,最终的 32 位二进制序列为:
0000 0000 0000 0000 0000 1000 0000 0001

将其每 4 位分组换算成十六进制,即

0x00000801

二、IEEE 754 单精度浮点数表示

  1. 确定符号位(S)
    2049 为正数,故 S = 0。
  2. 将 2049 转为二进制并归一化(求指数 E 和尾数 M)
    • 2049 在二进制下 =
      1000 0000 0001₂
      = 1·2¹¹ + 1·2⁰
      
    • 归一化写法:
      2049.0 = 1.00000000001₂ × 2¹¹
      
      此时“1.”之后的小数部分共有 11 位:前 10 位都是 0,第 11 位是 1,接下来全部补 0。
  3. 计算指数域(E′)
    • 归一化指数 e = 11
    • 单精度指数偏移量为 127 → 存储的指数字段 E′ = e + 127 = 11 + 127 = 138
    • 138₁₀ 转为二进制 8 位:
      138₁₀ = 1000 1010₂
      
  4. 求尾数(M)
    • “.00000000001” 共 23 位:
      • 前 10 位都是 0
      • 第 11 位为 1
      • 后面其余共 23 – 11 = 12 位都补 0
    • 写成一串 23 位:
      00000000001 00000000000
      
  5. 拼接得 32 位浮点表示
    S (1 位)   |   E (8 位)      |           M (23 位)
    --------------------------------------------------
    0          | 10001010        | 00000000001000000000000
    
    按 4 位一组分段:
    0 100 0101 0 000 0000 0001 0000 0000 0000₂
    
    因此,IEEE 754 单精度表示(十六进制)为:
    0x45001000
    

二、程序填空题(15 分,每空 3 分)

答案

int main()
{
    int i = 0, j = 1, k = 2, loop = 3;
    int *p = &i;
    int arr[3] = { 9,  /*(1)*/ 5 , 31 };
    scanf("%d,%d", &i, &j);
    p = /*(2)*/ &arr[1];
    k = /*(3)*/ (*p > j + 1) ? (i + 3) : (2 * i);
    for (loop = 0; loop < 2; /*(4)*/ p++, loop++)
    {
        /*(5)*/ k = *p + 2 * k;
    }
    return 0;
}
  • (1) 5
  • (2) &arr[1]
  • (3) (*p > j + 1) ? (i + 3) : (2 * i)*p <= j + 1 ? 2 * i : i + 3
  • (4) p++, loop++
  • (5) k = *p + 2 * k

注:第三小问有无括号均可以

解析

下面结合汇编逐步还原填空(假设栈帧中各变量的偏移已经对应好)。

(1) arr[1] 的初值

从汇编中看到:

movl   $9,  16(%esp)    # arr[0] = 9  
movl   $5,  20(%esp)    # arr[1] = 5  
movl   $31, 24(%esp)    # arr[2] = 31

因此

arr[3] = { 9, 5, 31 };

/*(1)*/ 5

(2) 在 scanf 之后对 p 的赋值

汇编里是:

leal   16(%esp), %eax    # %eax = &arr[0]  
addl   $4,      %eax     # %eax = &arr[0] + 4 = &arr[1]  
movl   %eax,   44(%esp)  # p = %eax

所以

p = &arr[1];

(3) 计算 k 的那一段

对应汇编:

	movl   44(%esp), %eax    # %eax = p  
    movl  (%eax),   %eax     # %eax = *p  
    movl   32(%esp), %edx    # %edx = j  
    addl   $1,      %edx     # %edx = j + 1  
    cmpl   %edx,    %eax     # 比较:*p 与 (j+1)  
    jle    .L2              # 如果 *p <= j+1 跳到 L2  
    movl   28(%esp), %eax  # eax = i  
    addl   $3,      %eax   # eax = i + 3  
    jmp    .L3  
  .L2:
    movl   28(%esp), %eax  # eax = i  
    addl   %eax,    %eax   # eax = i + i  
  .L3:
    movl   %eax,   36(%esp)  # k = eax

翻译成 C 逻辑就是:

if (*p > j + 1) {
    k = i + 3;
} else {
    k = 2 * i;
}

为了把它写在一行赋值里,我们可以用三目运算符。比如:

k = (*p > j + 1) ? (i + 3) : (2 * i);

(4) 和 (5) 对应循环体

PS :这里我写成了 loop += 4 ,但是在 C 语言里对指针做 p++ 操作时,编译器会自动将它加上“一个元素大小”,对于 int * 来说,就是加 sizeof(int)(通常是 4 字节)。因此答案是 loop ++

从汇编看,先在调用 f1 之后(此处与本题无关),会做:

	movl   $0,   40(%esp)   # loop = 0
    jmp    .L4
.L5:
    movl  36(%esp), %eax    # %eax = k
    leal  (%eax,%eax), %edx # %edx = 2*k
    movl  44(%esp), %eax    # %eax = p
    movl  (%eax),   %eax    # %eax = *p
    addl  %edx,    %eax      # %eax = *p + 2*k
    movl  %eax, 36(%esp)    # k = *p + 2*k

    addl  $1,   40(%esp)    # loop++
    addl  $4,   44(%esp)    # p = p + 1  (因为每次移动 4 字节,也就是下一个整型)

.L4:
    cmpl  $1,   40(%esp)
    jle   .L5

可以看出:

  1. 循环初始化 loop = 0;
  2. 条件是 loop <= 1,等价于 C 里 loop < 2
  3. 进入循环体后,本质上做了两件事:
    • k = *p + 2 * k;
    • loop++; p++;
  • 循环代码
    for (loop = 0; loop < 2; p++, loop++) {
        k = *p + 2 * k;
    }
    

三、程序填空题(第三空 3 分,其余三空每空 4 分,共 15 分)

答案

struct s {
    int i;
    int j;
    struct s *pnext;
} *ps1, *ps2;

int f(int k, struct s *ps) {
    if (ps->pnext != NULL)     /* (1) */
        return ps->i * k * ps->j- ps->pnext->i * ps->pnext->j; /* (2) */
    else
        return k + ps->i + ps->j;
}

int main() {
    ps1 = (struct s*)malloc(sizeof(struct s));
    ps2 = (struct s*)malloc(sizeof(struct s));
    ps1->i = 1;
    ps1->j = 2;
    ps2->i = 3;
    ps2->j = 4;
    ps1->pnext = NULL;
    ps2->pnext = NULL;
    ps2->pnext = ps1;                                /* (3) */
    ps1->j = f( f(ps2->i, ps2), ps1 );               /* (4) */
    printf("%d\n", ps1->j);
    return 0;
}
  • (1) ps->pnext != NULL
  • (2) ps->i * k * ps->j - ps->pnext->i * ps->pnext->j
  • (3) ps2->pnext = ps1;
  • (4) f( f(ps2->i, ps2), ps1 )

函数 f 的分析

汇编代码(简化后片段):

f:
    pushl   %ebp
    movl    %esp, %ebp
    movl    12(%ebp), %eax      # eax ← ps
    movl    8(%eax),  %eax      # eax ← ps->pnext
    testl   %eax,  %eax
    je      .L2                 # 如果 ps->pnext == NULL,跳到 .L2

    # —— ps->pnext != NULL 时,计算:ps->i * k * ps->j - pnext->i * pnext->j ——  
    movl    12(%ebp), %eax      # eax ← ps
    movl    (%eax),  %eax       # eax ← ps->i
    movl    %eax,    %edx       # edx ← ps->i
    imull   8(%ebp),  %edx      # edx ← ps->i * k
    movl    12(%ebp), %eax      # eax ← ps
    movl    4(%eax),  %eax      # eax ← ps->j
    imull   %eax,    %edx       # edx ← ps->i * k * ps->j

    movl    12(%ebp), %eax      # eax ← ps
    movl    8(%eax),  %eax      # eax ← ps->pnext
    movl    (%eax), %ecx        # ecx ← pnext->i
    movl    12(%ebp), %eax      # eax ← ps
    movl    8(%eax),  %eax      # eax ← ps->pnext
    movl    4(%eax),  %eax      # eax ← pnext->j
    imull   %ecx,   %eax        # eax ← pnext->i * pnext->j

    movl    %edx,   %ecx        # ecx ← 第一部分 ps->i * k * ps->j
    subl    %eax,   %ecx        # ecx ← (ps->i*k*ps->j) - (pnext->i*pnext->j)
    movl    %ecx,   %eax        # eax ← 结果
    jmp     .L3

.L2:
    # —— ps->pnext == NULL 时,返回 k + ps->i + ps->j ——  
    movl    12(%ebp), %eax      # eax ← ps
    movl    (%eax),  %eax       # eax ← ps->i
    movl    %eax,   %edx        # edx ← ps->i
    addl    8(%ebp),  %edx      # edx ← ps->i + k
    movl    12(%ebp), %eax      # eax ← ps
    movl    4(%eax),  %eax      # eax ← ps->j
    addl    %edx,   %eax        # eax ← ps->j + (ps->i + k)

.L3:
    popl    %ebp
    ret

由此可知,C 代码中应是:

int f(int k, struct s *ps) {
    if (ps->pnext != NULL)
        return ps->i * k * ps->j
             - ps->pnext->i * ps->pnext->j;
    else
        return k + ps->i + ps->j;
}

对应填空:

  1. (1) 应当是 ps->pnext != NULL
  2. (2) 应当是 ps->i * k * ps->j - ps->pnext->i * ps->pnext->j

main 函数的分析

从汇编看主要关键段:

	movl    ps2,   %eax
    movl    ps1,   %edx
    movl    %edx,  8(%eax)   # 也就是 ps2->pnext = ps1;

    movl    ps1,   %ebx
    movl    ps1,   %esi

    movl    ps2,   %edx
    movl    ps2,   %eax
    movl    (%eax), %eax     # eax ← ps2->i
    movl    %edx,   4(%esp)  # 准备调用 f 的第二个参数 = ps2
    movl    %eax,   (%esp)   # 准备调用 f 的第一个参数 = ps2->i
    call    f                 # 第一次调用:f(ps2->i, ps2)

    movl    %esi,  4(%esp)    # 准备第二次调用 f 的第二个参数 = ps1
    movl    %eax,  (%esp)     # 第一次 f 的返回值做第二次 f 的第一个参数
    call    f                 # 第二次调用:f( 上一次返回值 , ps1)

    movl    %eax,  4(%ebx)    # ps1->j = 第二次 f 的返回值

通过以上可以还原出:

  • (3) 是让 ps2->pnext 指向 ps1,即
    ps2->pnext = ps1;
    
  • (4) 是两次调用 f 的嵌套,把结果赋给 ps1->j,即
    ps1->j = f( f(ps2->i, ps2), ps1 );
    

四、综合分析题(10 分)

答案

  1. 该程序运行后计算结果为:
    15
    
  2. 栈帧图上
    • 0BC 处填 0x0804844A
    • 08C 处填 0x08048483
  3. 栈帧图上
    • 0C0 处填 0x00000003
    • 0C4 处填 0x00000004
  4. 栈帧图上
    • 090 处填 0x00000003
    • 094 处填 0x00000004

解析

1. 该程序运行后计算结果为 ______

答案:

15

解析:

  • f1(3,4) 调用 f2(3,4) 得到 12,再在 f1 中加上 3,结果是 15

2. 在栈帧图上的 “0BC” 和 “08C” 处填上正确的地址值

首先看调用链:

main → f1 → f2
  • main 执行到 call f1(地址 0x08048445)时,返回地址会是下一条指令 0x0804844a
  • f1 执行到 call f2(地址 0x0804847e)时,返回地址会是下一条指令 0x08048483

在栈帧图里:

  • 0x...0BC 存的是 f1 被调用时,保存到栈上的 “返回地址”。此时的返回地址就是从 main 返回的位置,也就是 0x0804844a
  • 0x...08C 存的是 f2 被调用时,保存到栈上的 “返回地址”。此时的返回地址就是从 f1 返回的位置,也就是 0x08048483

因此:

  • 位置 0BC0x0804844A
  • 位置 08C0x08048483

3. 在栈帧图上的 “0C0” 和 “0C4” 处填上正确的传递参数值

继续看 f1 被调用时 main 在栈上给 f1 的两个参数:

  • main 里,
    movl   $0x3,0x14(%esp)     # 准备 f1 的第1个参数 3
    movl   $0x4,0x18(%esp)     # 准备 f1 的第2个参数 4
    ……
    mov    0x18(%esp),%eax     # eax ← 4
    mov    %eax,0x4(%esp)      # 把 4 作为 f1 的第2个参数,写到 [esp+4]
    mov    0x14(%esp),%eax     # eax ← 3
    mov    %eax,(%esp)         # 把 3 作为 f1 的第1个参数,写到 [esp]
    call   f1
    
    当执行 call f1 之后,在 f1 的栈帧里:
    • EBP+8 (=0x…0C0) 存储 “第一个参数”,也就是 3
    • EBP+12(=0x…0C4) 存储 “第二个参数”,也就是 4

所以:

  • 0C0 处(f1 的第一个参数)填:0x00000003
  • 0C4 处(f1 的第二个参数)填:0x00000004

4. 在栈帧图上的 “090” 和 “094” 处填上正确的传递参数值

f2 被调用时 f1 在栈上给 f2 的两个参数:

  • f1 里,
    mov    0x8(%ebp),%eax    # eax ← f1 的第1个参数 a(此处 a=3)
    mov    %eax,(%esp)       # 把 3 放到 [esp],做 f2 的第1个参数
    mov    0xc(%ebp),%eax    # eax ← f1 的第2个参数 b(此处 b=4)
    mov    %eax,0x4(%esp)    # 把 4 放到 [esp+4],做 f2 的第2个参数
    call   f2
    
    当执行 call f2 之后,在 f2 的栈帧里:
    • EBP+8 (=0x…090) 存储 “第一个参数”,也就是 3
    • EBP+12(=0x…094) 存储 “第二个参数”,也就是 4

所以:

  • 090 处(f2 的第一个参数)填:0x00000003
  • 094 处(f2 的第二个参数)填:0x00000004

五、分析题(25 分)

有如下两个 C 语言代码文件 (此处略,详情见试卷)

对这两个 C 代码文件进行分离编译,分别形成 main_g.o 和 globals.o 文件,再使用 gcc 命令可形成一个可执行文件 test。请问:

1.test 执行的结果是什么?请按照其在屏幕的输出格式书写作答,并请解释为什么有这样的输出。

Parent process waiting for signal.
Received SIGUSR1 signal. Global variable before change: 42
Global variable after change: 100
Global variable value: 100

解释:

  • 程序首先在父进程里打印“Parent process waiting for signal.”,然后进入 pause() 等待信号。
  • 子进程 fork() 出来后会睡眠 1 秒,接着向父进程发送 SIGUSR1
  • 父进程收到 SIGUSR1,进入 handle_signal,此时全局变量 global_var 原本是 42,所以先打印 “Global variable before change: 42”,然后把 global_var 改为 100,再打印 “Global variable after change: 100”。
  • 信号处理结束后,父进程从 pause() 返回,主函数继续执行 print_global_var(),此时打印的是修改后的值 100:“Global variable value: 100”。

由于信号处理函数是在父进程的上下文中执行的,所以全局变量确实被改为了 100,进而最后打印出来也是 100。

2.在命令行里面用 gcc 形成可执行文 test 文件时,main_g.o 和 globals.o 在命令行中出现的顺序必须确定吗?为什么?

不必须,这两个文件都是可重定位目标文件,里面的所有符号都会被 gcc 解析并记录到符号表中,所以顺序不会影响到链接的过程。

3.如下图所示,这是哪个文件的哪一部分内容,你怎么看出来的?

这是可执行文件 test 里面的 main 函数代码段的内容。因为指令的地址都是绝对地址,说明代码段已经被重定位了。 call 的立即数也被修改成为了相对偏移,说明函数调用也被重定位了。

4.下图中 call 指令出现的地方很多都有@PLT,这是什么?其作用是什么?

@PLT 表示的是某个函数在 PLT(Procedure Linkage Table)中的跳转入口,程序通过它来实现动态链接时的函数调用。

作用:

  • 为程序里对共享库中函数的调用提供一个“统一入口”,
  • 支持程序启动时先不解析全部外部函数地址、而是“调用时再解析”,
  • 与 GOT 配合,实现了位置无关的动态链接。

5.为什么在地址 80486af 那一行出现的 call 指令(画横线的部分)没有 PLT 出现?它与其他的 call 相比有什么特点?

它没有 @plt,因为它不是调用外部库函数,而是直接调用同一个可执行文件内部的函数 print_global_var()

特点:
因为 print_global_var() 是在本文件定义的函数,所以当链接器在生成最终的 .text 段时就已经知道了这个函数的绝对地址,可以直接把目标函数的地址编码成一个 PC‐relative 的偏移量,写在指令里即可,不需要经过 PLT中转和动态链接。

补充知识:
与 PLT 调用的区别:凡是 @PLT 的,都是动态库里的函数,需要通过 PLT 表的 “中转” 才能跳转到函数真正的地址;而 call 804861b <print_global_var> 表示的是一个静态地、在本程序里就能找到定义的函数,就不需要 PLT,直接走 “call 0x…(PC 相对偏移)”。

6.根据此图,你能大概推算出(画横线部分)07 ff ff ff 是怎么得来的吗?

首先链接器为 print_global_var() 函数确定它的运行时地址,然后根据 print_global_var() 对应的的重定位表项中的 offsetmain() 函数的起始地址,相加得到需要重定位的地址。接着链接器计算这个式子:

目标函数(print_global_var)的地址- call 指令的下一条指令的地址

得到 07 ff ff ff ,其中下一条指令的地址=
需要重定位的地址 - 修正量(-4)
,最后将这个数填入到需要重定位的地址里面。

注,需要重定位的(起始)地址指的是 call 指令的操作数部分,对应的机器码上就是, call 这条指令的机器码从左往右数的第二个字节的地方,一共4个字节。

六、分析题(25 分)

答案

虚拟地址(VA)划分

VPN  = VA[15:8]
VPO  = VA[7:0]
TLBI = VA[9:8]
TLBT = VA[15:10]

物理地址(PA)划分

PPN = PA[11:8]
PPO = PA[7:0]

Cache Tag (CT) = PA[11:5]
Cache Index (CI) = PA[4:2]
Block Offset (CO) = PA[1:0]

A. 虚拟地址(16 位二进制)

15 14 13 12 11 10  9  8 │  7  6  5  4  3  2  1  0
 1  1  1  0  1  0  1  1    0  1  1  0  1  1  1  0

B. 地址翻译

VPN    = 0xEB       TLB命中?  = 是
TLBI   = 0x3        缺页?     = 否
TLBT   = 0x3A       PPN       = 0x9

C. 物理地址(12 位二进制)

11 10  9  8  7  6  5  4  3  2  1  0
 1  0  0  1  0  1  1  0  1  1  1  0

D. Cache 访问

CO = 0x2       物理地址 = 0x96E
CI = 0x3       Cache命中? = 是
CT = 0x4B      返回的 Cache 字节 = 0x3A

PS:返回的 Cache 只有一个字节,是因为题目最开始说了

内存访问是1字节

解析

第一部分:基本参数

1. 虚拟地址字段划分

虚拟地址共 16 位,记为:

15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
|-------VPN (8 位)--------|---VPO (8 位)----|
  • VPO(Virtual Page Offset):对应虚拟地址的低 8 位,也就是位 [7∶0]。
  • VPN(Virtual Page Number):对应虚拟地址的高 8 位,也就是位 [15∶8]。

对于 TLB 来说:

  • TLBI(TLB 索引):因为 TLB 有 4 个组,需要 2 位索引,这 2 位来自 VPN 的低 2 位,即虚拟地址的位 [9∶8]。
  • TLBT(TLB Tag):对应 VPN 的高 6 位,即虚拟地址的位 [15∶10]。

2. 物理地址字段划分

物理地址共 12 位,记为:

11 10  9  8  7  6  5  4  3  2  1  0
|--PPN (4 位)--|-----PPO (8 位)------|
  • PPO(Physical Page Offset):对应物理地址的低 8 位,即位 [7∶0]。
  • PPN(Physical Page Number):对应物理地址的高 4 位,即位 [11∶8]。

对于 Cache 而言(总行数 24 行,3 路组相联,每行 4 字节):

  • CO(Cache Block Offset):块内偏移需要 2 位,对应物理地址位 [1∶0]。
  • CI(Cache Index):组索引需要 3 位,对应物理地址位 [4∶2]。
  • CT(Cache Tag):其余高位作为 Cache Tag,即物理地址位 [11∶5]。

第二部分:地址翻译(针对虚拟地址 0xEB6E)

我们分四小题回答。

A. 虚拟地址二进制表示

虚拟地址 0xEB6E,对应的 16 位二进制为:

0x E    B    6    E
  1110 1011 0110 1110

将它按位写出(从 bit 15 到 bit 0):

1514131211109876543210
1110101101101110

B. 地址翻译流程

  1. 计算 VPN 与 VPO
    • 虚拟页号 VPN = 高 8 位 = 0xEB
    • 虚拟页偏移 VPO = 低 8 位 = 0x6E
  2. TLB 索引与 Tag
    • 从 VPN 的低 2 位(虚拟地址的 bit [9∶8])取出 TLBI:
      • VPN = 0xEB = 二进制 1110 1011,其中 bit [9∶8] = “11” ⇒ TLBI = 3(十六进制 0x3)。
    • VPN 的高 6 位(虚拟地址 bit [15∶10])作为 TLBT:
      • bit [15∶10] = 111010₂ = 0x3A。
  3. 查 TLB(Index=3组)
    在题图中,TLB 中“Index=3”的四条内容为(“Tag / PPN / Valid”):
    Index=3,Tag=0x28, PPN=0xA, Valid=1
             Tag=0x14, PPN=0x0, Valid=1
             Tag=0x3A, PPN=0x9, Valid=1
             Tag=0x07, PPN=0x2, Valid=1
    
    • 我们要匹配的 TLBT = 0x3A,在 Index=3 组中恰好存在一条 Tag=0x3A,且 Valid=1 ⇒ TLB 命中
    • 因此无需查页表,也不存在缺页。
    • 从该 TLB 条目可得物理页号 PPN = 0x9。
  4. 拼出物理地址
    • 物理页号 PPN = 0x9 (4 位)
    • 页偏移 VPO = 0x6E (8 位)
    • 合成物理地址 = PPN | PPO = 0x9 * 0x100 + 0x6E = 0x900 + 0x6E = 0x96E
    • 换成二进制(12 位)为 1001 0110 1110

综上,B 部分填写如下(十六进制均大写):

参数参数
VPN0xEBTLB命中?
TLBI0x3缺页?
TLBT0x3APPN0x9

C. 物理地址二进制表示

物理地址 0x96E,12 位二进制:

0x   9    6    E
    1001 0110 1110

按位写出(bit 11 到 bit 0):

11109876543210
100101101110

D. 物理内存引用 → Cache 访问

  1. 物理地址分字段
    • 物理地址 = 0x96E = 二进制 1001 0110 1110
    • CO(块内偏移) = 位 [1∶0] = 10₂ = 2(十六进制 0x2)。
    • CI(Cache 索引) = 位 [4∶2] = 011₂ = 3(十六进制 0x3)。
    • CT(Cache Tag) = 位 [11∶5] = 1001011₂ = 0x4B。
  2. 查 Cache(Index=3)
    题图给出了 Cache 在索引 3 处 3 路的内容:
    Index=3:
      行0: Tag=0x7F, Valid=1, Line=0x53C9FA45
      行1: Tag=0x4B, Valid=1, Line=0xD36D3A19
      行2: Tag=0x2C, Valid=0, Line=0xF9DEC115  (此路无效)
    
    • 我们的 CT = 0x4B,与“方式1 的 Tag=0x4B”且 Valid=1 完全匹配 ⇒ Cache 命中
    • 取出该路的 Line 字内容:D3 6D 3A 19 (按大端顺序看,4 字节依次是 0xD3,0x6D,0x3A,0x19)。
    • 块内偏移 CO=2 ⇒ 返回第 2 号字节(从 0 开始编号:0→D3,1→6D,2→3A,3→19) ⇒ 返回 0x3A
  3. 结果填写
参数参数
CO0x2物理地址0x96E
CI0x3Cache命中?
CT0x4B返回的 Cache 字节0x3A
### 关于2024年HNU计算机系统期末考试的相关资料 根据已知的信息,可以总结如下: #### 一、考试范围概述 湖南大学(HNU)计算机体系结构课程的2024期末考试内容覆盖广泛,注重细节知识点的记忆与理解。值得注意的是,本次考试并未涉及存储相关内容[^1]。其余部分则紧密结合平时作业中的题目形式,重点在于时间分配不足可能导致考生无法全面作答。 #### 二、文件系统的具体考察方向 基于过往操作系统的期末试题来看,文件系统的设计原理及其实际应用成为重要考点之一。例如,在2022年的操作系统期末试卷中提到的小明案例显示了对文件系统内部机制的理解需求,特别是关于直接指针数量以及数据块大小等参数的实际计算能力测试[^3]。这表明未来可能延续此类命题风格。 以下是针对该类问题的一个简单代码实现例子来帮助理解和解决类似问题: ```python def calculate_max_file_size(block_size, pointer_size, direct_pointers): max_direct_bytes = block_size * direct_pointers indirect_levels = [] single_indirect_blocks = (block_size // pointer_size) double_indirect_blocks = single_indirect_blocks ** 2 triple_indirect_blocks = single_indirect_blocks ** 3 total_blocks = direct_pointers + single_indirect_blocks + \ double_indirect_blocks + triple_indirect_blocks return total_blocks * block_size # 参数设置 block_size_kb = 4 # 数据块大小为4KB pointer_size_b = 4 # 指针大小为4字节 direct_pointers_num = 12 # 使用12个直接指针 max_file_size = calculate_max_file_size( block_size=block_size_kb*1024, pointer_size=pointer_size_b, direct_pointers=direct_pointers_num) print(f"最大可表示文件大小约为 {round(max_file_size / (1024**2), 2)} MB") ``` 此脚本能够辅助学生掌握如何利用给定条件推导出理论上的最大支持文件尺寸。 #### 三、补充说明 尽管上述信息提供了某些方面的指导,但具体的2024年度HNU计算机系统期末考试真题尚未公开发布。建议关注学校官方通知或者向学长学姐寻求更多一手复习资源。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值