创建并执行内核线程

实验目标

ucore在lab2完成了内存管理。一个程序如果要加载到内存中运行,通过ucore的内存管理就可以分配合适的空间了。接下来就需要考虑如何使用CPU来“并发”执行多个程序。

操作系统把一个程序加载到内存中运行,这个运行的程序会经历从“出生”到“死亡”的整个“生命”历程。这个运行程序的整个执行过程就是进程。为了记录、描述和管理程序执行的动态变化过程,需要有一个数据结构,这个就是进程控制块。一个进程与一个进程控制块一一对应。为此,ucore就需要建立合适的进程控制块数据结构,并基于进程控制块来完成对进程的管理。

proj10概述

实现描述

project10是lab3的第一个项目,它基于lab2的最后一个项目proj9.2。主要就是扩展了进程控制块的数据结构,并基于进程控制块实现了对进程的初步管理。并通过创建两个内核线程idleproc和init_main来实现了对CPU的分时使用。

项目组成

  1. proj10
  2. ├── ……
  3. ├── process
  4. ├── entry.S
  5. ├── proc.c
  6. ├── proc.h
  7. └── switch.S
  8. ├── schedule
  9. ├── sched.c
  10. └── sched.h
  11. ├── sync
  12. └── sync.h
  13. └── trap
  14. ├── trap.c
  15. ├── ……
  16. └── trapentry.S
  17. ├── libs
  18. ├── ……
  19. └── unistd.h
  20. └── ……
  21. 14 directories, 77 files

相对与proj9.2,proj10增加了7个文件,修改了相对重要的3个文件。主要修改和增加的文件如下:

  • process/proc.[ch]:实现了进程控制块的定义和基于进程控制块的各种进程管理函数,实现了对进程生命周期管理的绝大部分功能。
  • process/entry.S:内核线程的起始入口处和结束处理(通过调用do_exit完成实际结束工作)。
  • process/switch.S:实现了进程上下文(context)切换的函数switch_to,由于与硬件相关,所以直接采用汇编实现。
  • schedule/sched.[ch]:实现了一个先进先出(First In First Out)策略的进程调度。
  • trap/trap.c,trapentry.S:
  • unistd.h:定义了一系列系统调用号,为后续用户进程访问内核功能做准备,这里暂时用不上。

编译运行

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

  1. make
  2. make qemu

则可以得到如下显示界面

  1. thuos:~/oscourse/ucore/i386/lab3_process/proj10$ make qemu
  2. (THU.CST) os is loading ...
  3. Special kernel symbols:
  4. entry 0xc010002c (phys)
  5. etext 0xc0113bd7 (phys)
  6. edata 0xc013fab0 (phys)
  7. end 0xc0144e74 (phys)
  8. Kernel executable memory footprint: 276KB
  9. ……
  10. check_mm_shm_swap: step2, dup_mmap ok.
  11. check_mm_shm_swap() succeeded.
  12. ++ setup timer interrupts
  13. this initproc, pid = 1, name = "init"
  14. To U: "Hello world!!".
  15. To U: "en.., Bye, Bye. :)"
  16. kernel panic at kern/process/proc.c:317:
  17. process exit!!.
  18. Welcome to the kernel debug monitor!!
  19. Type 'help' for a list of commands.
  20. K>
  21. 从上图可以看到,proj10创建了一个内核线程init_main,然后启动内核线程initproc运行,此线程调用init_main函数完成了其主要的工作,即输出了一些信息:
  22. this initproc, pid = 1, name = "init"
  23. To U: "Hello world!!".
  24. To U: "en.., Bye, Bye. :)"
  25. 然后就“死亡”了,所占用的资源被回收。由于现在没有其他值得执行的线程,所以ucore就进入到kernel debug monitor了。到底是在哪里判断并进入monitor的呢?我们只需在“K>”提示符后面输入backtrace,就可得到如下输出:
  26. K> backtrace
  27. ebp:0xc7eb0ee8 eip:0xc0101c86 args:0x00000000 0x00000000 0xc7eb0f68 0xc0102489
  28. kern/debug/kdebug.c:298: print_stackframe+21
  29. ebp:0xc7eb0ef8 eip:0xc010258b args:0x00000000 0xc7eb0f1c 0x00000000 0x00000000
  30. kern/debug/monitor.c:147: mon_backtrace+10
  31. ebp:0xc7eb0f68 eip:0xc0102489 args:0xc013fac0 0x00000000 0xc011784a 0xc7eb0fdc
  32. kern/debug/monitor.c:93: runcmd+134
  33. ebp:0xc7eb0f98 eip:0xc010250d args:0x00000000 0xc7eb0fdc 0x0000013d 0xc7eb0fcc
  34. kern/debug/monitor.c:114: monitor+91
  35. ebp:0xc7eb0fc8 eip:0xc0102b2f args:0xc0117825 0x0000013d 0xc0117839 0xc0144e44
  36. kern/debug/panic.c:30: __panic+106
  37. ebp:0xc7eb0fe8 eip:0xc0112bc7 args:0x00000000 0xc01178b8 0x00000000 0x00000010
  38. kern/process/proc.c:317: do_exit+33
  39. K>

这里可以清楚的看到,在proc.c的317行(do_exit函数内)调用了panic函数,导致进入了monitor。idleproc 和 initproc 是 ucore 里面两个特殊的内核线程,它们是不允许退出的,所以 do_exit 里面直接调用 panic 了,对于其它普通的进程而言,do_exit 实际上还有很多工作要做,后续章节会进一步讲到。上述执行过程其实包含了对进程整个生命周期的管理。下面我们将从原理和实现两个方面对此进行进一步阐述。