Nachos实习——Lab1线程机制实习报告

本文是关于Nachos操作系统实习的第一部分,重点在于实现线程的基础数据结构——进程控制块(PCB)和调度机制。作者详细调研了Linux的PCB并与Nachos进行对比,分析了Linux的调度算法。在Nachos中,作者扩展了线程数据结构,增加了线程ID和用户ID,并限制了线程数量。此外,还实现了一个全局线程管理机制,允许查看系统中线程的状态。最后,作者扩展了线程调度算法,实现了优先级抢占式调度。
摘要由CSDN通过智能技术生成

Nachos实习——Lab1线程机制实习报告

文章目录

内容一:总体概述

本次Lab针对的内容是实现线程最基本的数据结构——进程控制块(PCB)和线程的调度机制,同时扩展实现时间片轮转调度算法。

内容二:任务完成情况

Exercise 1 Exercise 2 Exercise 3 Exercise 4 Exercise 5 Exercise 6 Challenge
完成情况 Y Y Y Y Y Y Y

内容三:具体完成Exercise情况

Exercise 1 调研

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

我的调研选择了Linux-2.6.0版本阅读

1、Linux进程控制块(PCB)

通过百度了解到Linux的进程控制块由一个task_struct的结构体定义。存放在文件/linux-2.6.0/include/linux/sched.h中,里面包含了众多信息,其中比较重要的信息:

  • 标识符:主要包括进程标识符、用户标识符、组标识符、备份用户标识符、文件系统用户标识符等。

  • 进程状态:进程状态是进城调度和交换的依据。

  • 进程调度信息:这些包含进程优先级以及进程的类别等

  • 进程通信相关的信息:Linux支持多种不同形式的通信机制。它支持典型的Unix通信机制:信号、管道,也支持System V通信机制:共享内存、信号量和消息队列

  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。

  • 上下文环境:进程执行时处理器的寄存器中的数据

  • 其他:还有其他一些必要信息

2、Linux进程控制块与Nachos的异同

Linux的进程控制块包含了比较详细的进程信息,而Nachos相对于Linux系统的PCB实现就要简单很多。

Nachos在Thread 类中实现了操作系统的线程控制块。Thread 线程控制块较Linux PCB 为简单的多,它没有线程标识 (pid)、实际用户标识 (uid)等和线程操作不是非常有联系的部分。Nachos仅仅定义了四个线程控制块的变量stackTop and stack(表示当前进程所占的栈顶和栈底)、machineState( 保留未在CPU上运行的进程的寄存器状态)、status(表示当前进程的状态)以及一些最基本的对线程操作的函数,如Fork()、Sleep()等。

除此之外,Nachos线程的总数目没有限制,线程的调度比较简单,而且没有实现线程的父子关系等。

3、Linux采用的进程/线程调度算法

Linux内核的三种调度策略 :

  • SCHED_OTHER 分时调度策略,(默认的)

  • SCHED_FIFO实时调度策略,先到先服务

  • SCHED_RR实时调度策略,时间片轮转

Linux内核将进程分成两个级别:普通进程和实时进程。实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。

SHCED_RR和SCHED_FIFO的不同:

当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。

SCHED_FIFO一旦占用cpu则一直运行。一直运行直到有 更高优先级任务到达或自己放弃 。

Exercise 2 源代码阅读

仔细阅读下列源代码,理解Nachos现有的线程机制。

  • code/threads/main.cc和code/threads/threadtest.cc

  • code/threads/thread.h和code/threads/thread.cc

Nachos现有的线程机制与Linux的进程机制比较相似,只是相对比较简单。

Nachos的线程有四种状态:

  • JUST_CREATED
  • RUNNING
  • READY
  • BLOCKED

和四个主要的方法Thread()Fork()Finish()Yield()Sleep()

  • Thread():构造函数,初始化一个新的Thread。
  • Fork(VoidFunctionPtr func,int arg):func,新线程运行的函数;分配一块固定大小的内存作为线程的堆栈,在栈顶放入 ThreadRoot 的地址。
  • Finish():并不是直接收回线程的数据结构和堆栈,因为我们仍在这个堆栈上运行这个线程。做法是将threadToBeDestroyed的值设为当前线程,使得Scheduler的Run()可以调用销毁程序,当我们这个程序退出上下文时,将其销毁。
  • Yield():用于本线程放弃处理机。
  • Sleep():可以使当前线程转入阻塞态,并放弃 CPU, 直到被另一个线程唤醒,把它放回就绪线程队列

下面对每个文件进行了简单的说明:

1、code/threads/main.cc

该模块是整个 Nachos 系统的入口,它分析了 Nachos 的命令行参数,根据不同的选项进行不同功能的初始化设置。

2、code/threads/threadtest.cc

这是一个简单的线程实验的测试用例。用于指导我们如何对线程的修改进行测试的。

  • testnum:测试号,对应相应的测试函数。
  • SimpleThread():一个5次循环的程序,每次循环中都让出CPU,让其他就绪的线程执行。
  • ThreadTest1():一个测试方法,创建两个线程,让他们都执行SimpleThread()方法,使这两个线程可以交替执行。
  • ThreadTest():可以看做一个总控程序,根据main函数传过来testnum参数值来执行不同的测试程序。例如,当testnum==1时,就执行ThreadTest1()。
3、code/threads/thread.h

用于管理线程的数据结构。如线程控制块、线程的基本方法都在这个文件中被定义。

4、code/threads/thread.cc

实现了用于管理线程事务的具体方法。主要有四种操作:Fork 、Finish、Yield、Seelp。

Exercise 3 扩展线程的数据结构

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

设计思路:

  • 对于用户ID:现有的Nachos代码不支持多用户,所以我直接将用户ID设置为固定值1000。
  • 对于线程ID:结合Exercise 4,我声明了一个容量为128的数组,初始值全0。每个元素的取值为0或1。0表示该数组下标没有作为线程的ID分配出去,1表示已分配。每次创建线程的时候都遍历一次数组,把第一个不为0的下标分配给线程作为ID。如果全为1,则返回线程ID为-1,表示不创建线程
1、threads/threads.h

在threads/threads.h中添加变量和方法

class Thread {
    private:
    		int tid;    //添加线程ID
   		 	int uid;    //添加用户ID
    public:
    		int getTid();    //获取线程ID
    		int getUid();    //获取用户ID
    		void setUid();   //设置用户ID
}
2、threads/system.h

由于系统的全局变量都声明和定义在system.h或system.cc中,我们也将数组定义在这里

 #define MAX_THREAD_NUM 128						//根据Exercise 4将系统可容纳的线程数量设置为128
extern int tid_flag[MAX_THREAD_NUM];	//用来标识该ID是否被分配

注意:上面使用了extern定义tid_flag数组,是因为我们事先需要在system.cc文件中定义和初始化

3、threads/system.cc

在Initialize()函数中初始化数组

int tid_flag[MAX_THREAD_NUM];
void
Initialize(int argc, char **argv)
{
    for (int i 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值