Skip to content

Latest commit

 

History

History
93 lines (53 loc) · 6.26 KB

Lab3.md

File metadata and controls

93 lines (53 loc) · 6.26 KB

Lab3

思考题 1: 内核从完成必要的初始化到用户态程序的过程是怎么样的?尝试描述一下调用关系。

  1. 内核在完成必要的初始化之后,先调用arch_interrupt_init函数进行异常向量表的初始化
  2. 然后调用create_root_thread创建第一个用户态程序。在create_root_thread中:
    1. 内核先调用create_root_cap_group创建内核对象(kernel object)——进程的抽象cap_group
    2. 之后再调用__create_root_threadcap_group中创建root thread
    3. 最后通过obj_putswitch_to_thread函数设置新创建的root thread,将current_thread设置为root thread
  3. 最后回到main函数中,调用eret_to_thread(switch_context())进行context switch,真正切换到刚刚创建的root上运行

练习题 2: 在 kernel/object/cap_group.c 中完善 cap_group_initsys_create_cap_groupcreate_root_cap_group 函数。在完成填写之后,你可以通过 Cap create pretest 测试点。

  • cap_group_init函数是初始化cap_group的函数,需要依次初始化slot_table, thread_list, thread_cnt以及pid(根据传入的参数决定)
  • sys_create_cap_group是用户创建cap_group的函数,在需要我们填写的部分中:
    1. 调用obj_alloc创建new_cap_group对象,同时调用cap_group_init来初始化cap_group
    2. 调用obj_alloc创建vmspace对象
  • create_root_cap_group是用户创建第一个进程所使用的函数,在我们需要填写的部分中:
    1. 调用obj_alloc创建new_cap_group对象
    2. 调用cap_group_init来初始化cap_group,同时调用cap_alloc来更新cap_groupslot_table,将cap_group自身加入到其中
    3. 调用obj_alloc创建vmspace对象
    4. 设置vmspace->pcidROOT_PCID,同时调用vmspace_init来初始化vmspace,再调用cap_alloc来更新cap_groupslot_table,将vmspace加入到其中

练习题 3: 在 kernel/object/thread.c 中完成 load_binary 函数,将用户程序 ELF 加载到刚刚创建的进程地址空间中。

由于Chcore系统中尚无文件系统,所有用户程序镜像以ELF二进制的形式直接嵌入到内核镜像中,所以相当于我们可以直接从内存中访问ELF文件,观察load_binary函数的参数,我们不难发现bin就是ELF文件开头的位置,我们进行如下操作:

  1. 从program headers中获取flags(调用PFLAGS2VMRFLAGS将其转化为vmr_flags_t类型)

  2. 算出seg_map_sz,需要以页为粒度去映射,因此需要调用ROUND_UPROUND_DOWN

    seg_map_sz = ROUND_UP(p_vaddr + seg_sz, PAGE_SIZE) - ROUND_DOWN(p_vaddr, PAGE_SIZE);
  3. 为每个ELF section创建一个对应的pmo,调用create_pmo

  4. 将ELF文件中对应的section拷贝到刚刚创建的pmo的内存中(调用memcpy)

  5. 调用vmspace_map_range添加内存映射,以页为粒度

练习题 4: 按照前文所述的表格填写 kernel/arch/aarch64/irq/irq_entry.S 中的异常向量表,并且增加对应的函数跳转操作。

  1. 异常向量表调用宏定义函数exception_entry+label来实现,对照前文的表格填入
  2. 函数跳转即为bl <handler>irq_el1h, irq_el1t, fiq_el1t, fiq_el1h, error_el1t, error_el1h, sync_el1t, sync_el1h调用unexpected_handlersync_el1h调用handle_entry_c

练习题 5: 填写 kernel/arch/aarch64/irq/pgfault.c 中的 do_page_fault,需要将缺页异常转发给 handle_trans_fault 函数。

  • 直接调用ret = handle_trans_fault(current_thread->vmspace, fault_addr)即可

练习题 6: 填写 kernel/mm/pgfault_handler.c 中的 handle_trans_fault,实现 PMO_SHMPMO_ANONYM 的按需物理页分配。

  1. 首先调用get_page_from_pmo获取缺页异常的page的物理地址
  2. 检查pmo中当前的fault地址对应的物理页是否存在
    1. 若未分配(pa == 0),则通过get_pages分配一个物理页,然后用commit_page_pmo将页记录在pmo中,用map_range_in_pgtbl增加页表映射
    2. 若已分配,则调用map_range_in_pgtbl修改页表映射

练习题 7: 按照前文所述的表格填写 kernel/arch/aarch64/irq/irq_entry.S 中的 exception_enterexception_exit,实现上下文保存的功能。

  • exception_enter中用stpx0~x29寄存器依次保存,然后在获取系统寄存器之后,将这些系统寄存器中的值与x30一起保存,最后更新sp的值
  • exception_exit同理,先将系统寄存器中的值从内存中通过ldp加载出来存入系统寄存器,然后再依次使用stpx0~x29从内存中加载出来恢复,最后更新sp的值

思考题 8: ChCore中的系统调用是通过使用汇编代码直接跳转到syscall_table中的 相应条目来处理的。请阅读kernel/arch/aarch64/irq/irq_entry.S中的代码,并简要描述ChCore是如何将系统调用从异常向量分派到系统调用表中对应条目的。

  1. 在user态进行系统调用时,代码下陷到kernel态触发同步异常,处理器在异常向量表中找到对应的异常处理程序代码sync_el0_64
  2. 进入到sync_el0_64后,首先调用exception_enter进行上下文保存,进行一些比较之后调用el0_syscall
  3. el0_syscall先到syscall_table中查询对应的syscall entry,也即对应的异常handler函数,然后通过blr调用这个函数
  4. 最后调用exception_exit恢复上下文,退出syscall

练习题 9: 填写 kernel/syscall/syscall.c 中的 sys_putcsys_getckernel/object/thread.c 中的 sys_thread_exitlibchcore/include/chcore/internal/raw_syscall.h 中的 __chcore_sys_putc__chcore_sys_getc__chcore_sys_thread_exit,以实现 putcgetcthread_exit 三个系统调用。

  1. sys_thread_exit:将current_threadthread_exit_state设置为TE_EXITED,然后调用thread_deinit来销毁当前的thread
  2. sys_putc:调用uart_send
  3. sys_getc:调用uart_recv
  4. __chcore_sys_putc:调用__chcore_syscall1,传入系统调用编号和ch
  5. __chcore_sys_getc:调用__chcore_sys_call0,传入系统调用编号
  6. __chcore_sys_thread_exit:调用__chcore_syscall0,传入系统调用编号