• 基于内核线程实现全局内存页替换机制
    • 实验目标
    • proj11概述
      • 实现描述
      • 项目组成
      • 编译运行

    基于内核线程实现全局内存页替换机制

    实验目标

    到proj11为止,还没有能够在ucore中实现一个完整的内存页替换机制。但其实在lab2的proj8中,已经为ucore实现内存页替换机制提供了大量的支持,并在相关测试函数kern/mm/swap.c::check_swap中进行了检查。但这个检查只是说明了proj8提供了能够完成内存页替换机制的数据结构和函数支持,即已经一砖一瓦地完成了门窗、墙壁等建筑工作,还差把相关部件完整组织起来实现成一个完整的房子。proj11就是完成这最后一步,采用内核线程来实现内存页替换机制,使得用户进程在快用完内存后,可以通过内存页替换机制把不常用的页换出到硬盘swap分区中,常用的页保存在内存中,保持系统中有足够的内存给用户进程使用。

    proj11概述

    实现描述

    proj11是lab3的第六个project。它在proj10.4的基础上实现了基于内核线程的内存页替换机制,主要扩展设计了专门用于执行内存页替换的内核线程kswapd,并增加了等待队列、扩展了进程控制块的成员变量mm的等,使得在用户进程申请存不足或系统空闲内存不足的情况下,通过执行kswapd内存线程,实现内存页替换,把不常用的页放到硬盘swap分区上,给系统提供足够的空闲空间。

    项目组成

    1. proj11
    2. ├── ……
    3. ├── mm
    4. ├── ……
    5. ├── pmm.c
    6. ├── swap.c
    7. ├── swap.h
    8. ├── vmm.c
    9. └── vmm.h
    10. ├── process
    11. ├──……
    12. ├── proc.c
    13. └── proc.h
    14. └── sync
    15. ├── ……
    16. ├── wait.c
    17. └── wait.h
    18. └── user
    19. ├── cowtest.c
    20. ├── swaptest.c
    21. └── ……
    22. 17 directories, 114 files

    相对于proj10.4,proj11在内核方面主要增加了有关kswapd内核线程和相关函数以及等待队列实现,在用户程序方面,增加测试ucore的COW实现的用户程序cowtest.c和测试内存页置换实现的swaptest.c。主要修改和增加的文件如下:

    • kern/mm/pmm.c:扩展了alloc_pages函数,使得它能够在没有获得所需空闲内存页后,进一步调用tre_free_pages来要求ucore释放足够的空闲页,从而再次要求所需空闲页,直到要求得到满足为止。
    • kern/mm/swap.[ch]:更新try_free_pages的实现,完成让当前进程睡眠,并唤醒kswapd内核线程,让它完成对空闲页的回收。同时实现了内核线程kswapd的执行主体kswapd_main函数,此函数完成具体的内存页置换机制。
    • kern/sync/wait.[ch]:实现等待队列机制,使得内存页等资源无法得到满足的进程能够处于等待状态,并在资源得到满足后让进程继续执行。
    • kern/mm/vmm.[ch]:扩展了mm_struct结构,并修改相关函数,使得所有进程的成员变量mm能够链入到全局mm_struct结构的链表proc_mm_list中。

    编译运行

    编译并运行proj11的命令如下:

    1. make
    2. make qemu

    则可以得到如下显示界面

    1. (THU.CST) os is loading ...
    2. Special kernel symbols:
    3. ……
    4. check_vmm() succeeded.
    5. ide 0: 10000(sectors), 'QEMU HARDDISK'.
    6. ide 1: 262144(sectors), 'QEMU HARDDISK'.
    7. check_swap() succeeded.
    8. ……
    9. ++ setup timer interrupts
    10. kernel_execve: pid = 3, name = "swaptest".
    11. buffer size = 00500000
    12. parent init ok.
    13. child 9 fork ok, pid = 13.
    14. child 8 fork ok, pid = 12.
    15. child 7 fork ok, pid = 11.
    16. child 6 fork ok, pid = 10.
    17. child 5 fork ok, pid = 9.
    18. child 4 fork ok, pid = 8.
    19. child 3 fork ok, pid = 7.
    20. child 2 fork ok, pid = 6.
    21. child 1 fork ok, pid = 5.
    22. child 0 fork ok, pid = 4.
    23. check cow ok.
    24. round 0
    25. round 1
    26. round 2
    27. round 3
    28. round 4
    29. child check ok.
    30. wait ok.
    31. check buffer ok.
    32. swaptest pass.
    33. all user-mode processes have quit.
    34. init check memory pass.
    35. kernel panic at kern/process/proc.c:430:
    36. initproc exit.
    37. Welcome to the kernel debug monitor!!
    38. Type 'help' for a list of commands.
    39. K>

    表面上看不出上述输出对内存页置换算法实现的具体体现。不过通过Makefile和对swaptest.c程序的分析,还是能够看出proj11的执行与其他进程的执行不同:

    1. Makefile:
    2. ……
    3. QEMUOPTS = -m 48m -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
    4. swaptest.c
    5. ……
    6. const int size = 5 * 1024 * 1024;
    7. char *buffer;
    8. ……
    9. main(void){
    10. ……
    11. (buffer = malloc(size))
    12. ……
    13. for (i = 0; i < pids; i ++) {
    14. if ((pid[i] = fork()) == 0) {
    15. ……
    16. }

    通过Makefile,可以看到qemu只模拟出了48MB的物理内存空间,但swaptest.c创建了10个子进程,且每个子进程都会复制全局变量buffer,且会对buffer中的所有元素进行写操作。由于每个buffer的空间大小为5MB,所以10个子进程和1个父进程的buffer所占虚拟空间总和为55MB,大于实际的48MB物理内存空间。而在操作系统设计上,用户进程的用户空间是没必要都保存在内存中,这使得必须把某些页换出到硬盘swap分区才能确保所有子进程都能正常执行完毕。为此,我们还需进一步分析proj11中ucore具体的内存页置换机制的实现和执行过程。下面将从实现方面对此进行进一步阐述。