创建并执行内核线程

实验目标

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

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

proj10概述

实现描述

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

项目组成

proj10
├──  ……
│   ├── process
│   │   ├── entry.S
│   │   ├── proc.c
│   │   ├── proc.h
│   │   └── switch.S
│   ├── schedule
│   │   ├── sched.c
│   │   └── sched.h
│   ├── sync
│   │   └── sync.h
│   └── trap
│       ├── trap.c
│       ├── ……
│       └── trapentry.S
├── libs
│   ├── ……
│   └── unistd.h
└── ……
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的命令如下:

make
make qemu

则可以得到如下显示界面

thuos:~/oscourse/ucore/i386/lab3_process/proj10$ make qemu
(THU.CST) os is loading ...

Special kernel symbols:
entry  0xc010002c (phys)
etext  0xc0113bd7 (phys)
edata  0xc013fab0 (phys)
end    0xc0144e74 (phys)
Kernel executable memory footprint: 276KB
……
check_mm_shm_swap: step2, dup_mmap ok.
check_mm_shm_swap() succeeded.
++ setup timer interrupts
this initproc, pid = 1, name = "init"
To U: "Hello world!!".
To U: "en.., Bye, Bye. :)"
kernel panic at kern/process/proc.c:317:
process exit!!.

Welcome to the kernel debug monitor!!
Type 'help' for a list of commands.
K>
从上图可以看到,proj10创建了一个内核线程init_main,然后启动内核线程initproc运行,此线程调用init_main函数完成了其主要的工作,即输出了一些信息:
this initproc, pid = 1, name = "init"
To U: "Hello world!!".
To U: "en.., Bye, Bye. :)"
然后就“死亡”了,所占用的资源被回收。由于现在没有其他值得执行的线程,所以ucore就进入到kernel debug monitor了。到底是在哪里判断并进入monitor的呢?我们只需在“K>”提示符后面输入backtrace,就可得到如下输出:
K> backtrace
ebp:0xc7eb0ee8 eip:0xc0101c86 args:0x00000000 0x00000000 0xc7eb0f68 0xc0102489 
    kern/debug/kdebug.c:298: print_stackframe+21
ebp:0xc7eb0ef8 eip:0xc010258b args:0x00000000 0xc7eb0f1c 0x00000000 0x00000000 
    kern/debug/monitor.c:147: mon_backtrace+10
ebp:0xc7eb0f68 eip:0xc0102489 args:0xc013fac0 0x00000000 0xc011784a 0xc7eb0fdc 
    kern/debug/monitor.c:93: runcmd+134
ebp:0xc7eb0f98 eip:0xc010250d args:0x00000000 0xc7eb0fdc 0x0000013d 0xc7eb0fcc 
    kern/debug/monitor.c:114: monitor+91
ebp:0xc7eb0fc8 eip:0xc0102b2f args:0xc0117825 0x0000013d 0xc0117839 0xc0144e44 
    kern/debug/panic.c:30: __panic+106
ebp:0xc7eb0fe8 eip:0xc0112bc7 args:0x00000000 0xc01178b8 0x00000000 0x00000010 
    kern/process/proc.c:317: do_exit+33
K>

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

results matching ""

    No results matching ""