南京大学 计算机系统基础 课程实验 2018(PA3)

其他部分:

  1. PA0-1
  2. PA2

这一部分应该算是最简单的一部分了,几乎就是按照文档的做就可以了,但是有几个问题值的关注

一个系统调用的生命周期是什么

使用navy-apps/tests/hello作为例子分析

  1. 从printf会调用klib中间的printf 函数,至于为什么不是调用Nemu中间实现的或者系统的printf以后会讲到。
printf("Hello World from Navy-apps for the %dth time!\n", i ++);
  1. klib中间经过反复调用各种函数,最终会到达nano.c中间。这里大家就把klib当做一个黑盒子,我看一下其中的代码,说实话,有一种读天书的感觉。

  2. nano.c 中间定义了大量的系统调用其中就包括需要使用_write

int _write(int fd, void *buf, size_t count){
    return _syscall_(SYS_write, fd, (intptr_t)buf, count);
}
  1. nano.c 中间封装了syscall的调用原理,也就是使用0x80号中断
intptr_t _syscall_(int type, intptr_t a0, intptr_t a1, intptr_t a2) {
  int ret = -1;
  asm volatile("int $0x80" : "=a"(ret) : "a"(type), "b"(a0), "c"(a1), "d"(a2));
  return ret;
}
  1. system.c 定义在Nemu模块中间,包含有int的处理函数
make_EHelper(int) {
  uint8_t int_NO = id_dest->val;
  raise_intr(int_NO, *eip);
  print_asm("int %s", id_dest->str);
}
  1. intr.c 中间定义raise_intr函数读取CPU的中断向量表的入口地址,并且开始执行函数,目前raise_intr函数只会被int调用,在PA4中间还可以被时钟中断调用的。
void raise_intr(uint8_t NO, vaddr_t ret_addr) {
  rtl_push(&cpu.eflags);
  cpu.IF = 0; // assume we forbit every all IF in the interrupt
  rtl_push(&cpu.cs);
  rtl_push(&ret_addr); // eip
  uint32_t a = vaddr_read(idtr_addr + sizeof(GateDesc) * NO, 4);
  uint32_t b = vaddr_read(idtr_addr + sizeof(GateDesc) * NO + 4, 4);
  uint32_t entry_addr = (a & 0xffff) | (b & 0xffff0000);
  rtl_j(entry_addr);
}
  1. cte.c中间_cte_init函数实现创建了各种终端号的入口地址,都是使用函数的形式定义的。

  2. trap.S中间定义了函数入口,并且在asm_trap函数中间写下一些列的汇编,其中有一个非常关键的操作。调用函数之前push,根据i386的函数调用规则,push的内容就是参数,也就是esp作为参数传入到一个irq_handle的函数中间了。

  pushl %esp
  call irq_handle
  1. cte.c 中间定义了irq_handle函数,该函数的参数竟然是Context *, 上一步中间说明了esp是参数,也就是调用irq_handle之前的一系列的push的操作就是构成了Context的的内容了。irq_handle 函数会调用一个函数指针,该函数指针指向的内容随架构变化,在x86-neum的架构下会指向do_event函数

  2. irq.c中间定义了do_event函数,进一步调用do_syscall函数

  3. syscall.c 中间定义syscall.c函数

  4. fs.c 中间定义了fs_write函数来处理所有的读写操作,由于虚拟文件的封装,wiret函数会调用实现注册好的serial_write函数。

13.device.c中间包含了seraial_write函数就很简单了,_put()的输出就可以了

清楚了上面过程,整个PA3大概就可以做完了,中间可能会遇到各种bug, 只是需要一层一层的函数调用检查就可以了。

brk 的含义是什么

Nemu和AM是如何协同合作的

printf到底是谁的

随着试验的进行,大家逐渐会模糊一件事情,当你想要调试的时候,printf到底glibc提供的(也就是物理机上安装的操作系统上的运行时库提供的),还是klib提供的(就是哪一个嵌入式库提供的),还是Nemu中间的printf(就是PA2在Nemu中间实现的一系列的io函数)。当然其中使用Log函数是如何实现的。

你可能感兴趣的:(南京大学 计算机系统基础 课程实验 2018(PA3))