nachos lab1-线程机制

2019-07-14 10:15发布

总体概述

本次lab的主要内容是拓展nachos的线程机制,在nachos的线程模型中添加tid和uid,并添加相应的管理机制。 本次lab的重要知识点在于对nachos中线程具体实现方式的理解,

任务完成情况

  1. Exercise 1 –> Y
  2. Exercise 2 –> Y
  3. Exercise 3 –> Y
  4. Exercise 4 –> Y

具体完成情况如下所示:

Exercise 1 调研

调研Linux或Windows中进程控制块(PCB)的基本实现方式,理解与Nachos的异同。

Linux中的PCB包含以下的内容

  1. PID
  2. 特征信息
  3. 进程状态
  4. 优先级
  5. 通信状态
  6. 现场保护区
  7. 资源需求、分配控制信息
  8. 进程实体信息
  9. 其他(工作单位、工作区、文件信息)

Nachos中的PCB实现

PCB的实现thread.h中,其中仅包含以下信息:
  1. stackTop and stack, 表示当前进程所占的栈顶和栈底
  2. machineState, 保留未在CPU上运行的进程的寄存器状态
  3. status, 表示当前进程的状态

Exercise 2 源代码阅读

仔细阅读下列源代码,理解Nachos现有的线程机制
1. code/threads/main.cc和code/threads/threadtest.cc
2. code/threads/thread.h和code/threads/thread.cc
thread.cc中实现了Thread类的一系列成员函数,包含如下几种: Thread();//Thread的构造函数 ~Thread();//Thread的析构函数 Fork(VoidFunctionPtr func, int arg);//在线程中运行func方法,参数为arg ChechOverflow();//检查线程的栈是否溢出 Finsh();//当Fork运行结束后由ThreadRoot调用,调用sleep,并准备将当前进程销毁 Yield();//如果有其他进程在等待,则让当前进程下CPU Sleep();//当前进程由于被阻塞而下CPU ...... threadtest.cc是一个简单的线程调试程序,在main中调用ThreadTest()函数,并完成一些可自定义的测试方法。

Exercise 3 拓展线程的数据结构

增加“用户ID、线程ID”两个数据成员,并在Nachos现有的线程管理机制中增加对这两个数据成员的维护机制。

思路

维护一个标识数组tid_flag,用以表明当前flag是否被分配,每次创建进程时去表中找到第一个未被分配的tid,将其分配给这个数组。当删除一个进程时,则将进程对应的tid_flag置为0

添加数据

thread.h中,对Thread类添加以下内容: private: int tid; int uid; public: int getTid(){ return this->tid; } int getUid(){ return this->uid; }

数据成员的维护机制

system.h中,声明外部变量: extern int tid_flag[128]; 同时在system.cc中声明变量, 并在Initialize()中对变量进行初始化: //变量声明 int tid_flag[128]; //in Initialize() for(int i = 0;i < 128; ++i){ tid_flag[i] = 0; } 更改Thread::Thread(char* threadName): Thread::Thread(char* threadName) { name = threadName; stackTop = NULL; stack = NULL; status = JUST_CREATED; // allocate thread id for the current thread, wyx for(int i = 0; i < 128; ++i){ if(tid_flag[i] == 0){ this->tid = i; tid_flag[i] = 1; ThreadNum++; tid_pointer[i] = this; break; } } #ifdef USER_PROGRAM space = NULL; #endif printf("Thread %d created! ", this->tid); } 更改Thread::~Thread(): Thread::~Thread() { DEBUG('t', "Deleting thread "%s" ", name); ASSERT(this != currentThread); //Deallocate the thread id tid_flag[this->tid] = 0; if (stack != NULL) DeallocBoundedArray((char *) stack, StackSize * sizeof(int)); printf("Thread %d deleted ", this->tid); }

Exercise 4 增加全局线程管理机制

  1. 在Nachos中增加对线程数量的限制,使得Nachos中最多能够同时存在128个线程;
  2. 仿照Linux中PS命令,增加一个功能TS(Threads Status),能够显示当前系统中所有线程的信息和状态。

思路

在Thread中添加static Thread* getInstance(char* threadName)方法,每次创建新的Thread时首先调用该方法,若当前进程数未超过限制,则正常创建,并返回新创建指针的指针,否则,提示出错,返回NULL 对于TS(),维护tid_flag数组时维护一个Thread* tid_pointer[128]的数组,该数组初始化为NULL,保存tid对应的Thread的指针,在TS中,通过指针得到Thread的对应信息

添加变量ThreadNum

记录当前的线程数,定义类似于tid_flag,故不赘述

更改Thread::Thread()属相

Thread::Thread()由public改为private

添加Thread::getInstance()方法

Thread* Thread::getInstance(char* threadName){ if(ThreadNum < 128){ return new Thread(threadName); }else{ printf("You cannot create more threads! "); return NULL; } }

TS函数

//TS, print the thread status void TS(){ printf("---------------Call TS--------------- "); for(int i = 0;i < 128;++i){ if(tid_flag[i] != 0){ printf("tid: %d, thread name: %s. ", tid_pointer[i]->getTid(), tid_pointer[i]->getName()); } } printf("------------------------------------- "); }

测试结果:

最大线程数

testnum = 130 ThreadTest()中循环调用testnum次ThreadTest1() maxtnum

TS函数

ThreadTest()最后调用 ts

遇到的困难以及解决方式

我遇到了以下两个问题:

1. UID的管理机制

在向Thread类中添加了uid后,我并不清楚应该怎么获得userid,所以并不知道uid的初始化应该如何完成 解决方法: 通过和同学讨论,我们一致认为当前的lab并不涉及用户进程,所有进程都是跑在内核态,故并不需要对userid进程分析,可以在初始化时将uid设置一个缺省值

2. 最大进程数的实现

我一开始并不明确应该用什么方法实现这个功能,所以做了一些尝试。 我最初的实现方法是在Thread::Thread(char* threadName)中判定当前是否有可分配的tid,若有则正常分配,否则将其tid设为缺省值128,在对线程进程回收时根据tid判定,若为128则将其回收 但是后来思考发现,可以通过getInstance的方式,首先判断条件,然后再根据情况返回指向Thread的指针或者NULL。 其实我最终的实现方法有个问题,因为并不能调用Thread::Thread()新建一个进程,给人感觉并不直观,甚至有点奇怪。而且我不确定这种方式对后续的lab是否有影响。

收获及感想

通过这次lab,我觉得我最大的收获在于对nachos的线程机制以及对操作系统内部进程的管理机制的理解更加深刻。

对课程的意见和建议

暂无

参考文献

[1] Andrew S. Tanenbaum著.陈向群 马洪兵 译 .现代操作系统[M].北京:机械工业出版社,2011:47-95. [2] Nachos Doc: http://homes.cs.washington.edu/~tom/nachos/