深入理解任务调度

一个有趣的问题

系统运行后,下面的任务将如何执行?

 我们期待的执行结果是 TaskA,TaskB,TaskC 在并行执行

我们可以看到 TaskB 并没有被执行

思考

任务B执行了吗?如果执行了,那么为什么看不到输出?

问题分析

app.c

void AppMain()
{
	RegApp("TaskA", TaskA, 255);
	RegApp("TaskB", TaskB, 255);
	RegApp("TaskC", TaskC, 255);
	//RegApp("TaskD", TaskD, 255);
}

task.c

#include "utility.h"
#include "task.h"
#include "app.h"

#define MAX_TASK_NUM       4
#define MAX_RUNNING_TASK   2
#define MAX_READY_TASK     (MAX_TASK_NUM - MAX_RUNNING_TASK)
#define MAX_TASK_BUFF_NUM  (MAX_TASK_NUM + 1)
#define PID_BASE           0x10

static AppInfo* (*GetAppToRun)(uint index) = NULL;
static uint (*GetAppNum)() = NULL;

void (* const RunTask)(volatile Task* pt) = NULL;
void (* const LoadTask)(volatile Task* pt) = NULL;

volatile Task* gCTaskAddr = NULL;
static TaskNode gTaskBuff[MAX_TASK_BUFF_NUM] = {0};
static TSS gTSS = {0};
static Queue gFreeTaskNode = {0};
static Queue gReadyTask = {0};
static Queue gRunningTask = {0};
static Queue gWaitingTask = {0};
static TaskNode* gIdleTask = NULL;
static uint gAppToRunIndex = 0;
static uint gPid = PID_BASE;

void TaskEntry()
{
	if(gCTaskAddr != NULL)
	{
		gCTaskAddr->tmain();
	}
	
	// to destory current task here
	asm volatile(
	    "movw $0, %ax \n"
	    "int $0x80 \n"
	);
	
	while(1);    // TODO: schedule next task to run
}

void IdleTask()
{  
    while(1);
}

static void InitTask(Task* pt, uint id, const char* name, void(*entry)(), ushort pri)
{
    pt->rv.cs = LDT_CODE32_SELECTOR;
    pt->rv.gs = LDT_VIDEO_SELECTOR;
    pt->rv.ds = LDT_DATA32_SELECTOR;
    pt->rv.es = LDT_DATA32_SELECTOR;
    pt->rv.fs = LDT_DATA32_SELECTOR;
    pt->rv.ss = LDT_DATA32_SELECTOR;
    
    pt->rv.esp = (uint)pt->stack + AppStackSize;
    pt->rv.eip = (uint)TaskEntry;
    pt->rv.eflags = 0x3202;
    
    pt->id = id;
    
    StrCpy(pt->name, name, sizeof(pt->name) - 1);
    
    pt->tmain = entry;
    
    pt->current = 0;
    pt->total = 256 - pri;
    
    SetDescValue(AddrOff(pt->ldt, LDT_VIDEO_INDEX),  0xB8000, 0x07FFF,         DA_DRWA + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_CODE32_INDEX), 0x00,    PageDirBase - 1, DA_C + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_DATA32_INDEX), 0x00,    PageDirBase - 1, DA_DRW + DA_32 + DA_DPL3);
    
    pt->ldtSelector = GDT_TASK_LDT_SELECTOR;
    pt->tssSelector = GDT_TASK_TSS_SELECTOR;
}

static void PrepareForRun(volatile Task* pt)
{
	pt->current++;

	gTSS.ss0 = GDT_DATA32_FLAT_SELECTOR;
    gTSS.esp0 = (uint)&pt->rv + sizeof(pt->rv);
    gTSS.iomb = sizeof(TSS);
    
    SetDescValue(AddrOff(gGdtInfo.entry, GDT_TASK_LDT_INDEX), (uint)&pt->ldt, sizeof(pt->ldt)-1, DA_LDT + DA_DPL0);
}

static void CreateTask()
{
	int num = GetAppNum();
	
	while((gAppToRunIndex < num) && (Queue_Length(&gReadyTask) < MAX_READY_TASK))
	{
		TaskNode* tn = (TaskNode*)Queue_Remove(&gFreeTaskNode);
		
		if(tn != NULL)
		{
			AppInfo* app = GetAppToRun(gAppToRunIndex);
			
			InitTask(&tn->task, gPid++, app->name, app->tmain, app->priority);
			
			Queue_Add(&gReadyTask, &tn->head);	
		}
		else
		{
			break;
		}
		
		gAppToRunIndex++;
	}
}

static void ReadyToRunning()
{
	QueueNode* node = NULL;

	if(Queue_Length(&gReadyTask) < MAX_READY_TASK)
	{
		CreateTask();
	}
	
	while((Queue_Length(&gReadyTask) > 0) && (Queue_Length(&gRunningTask) < MAX_RUNNING_TASK))
	{
		node = Queue_Remove(&gReadyTask);
		
		((TaskNode*)node)->task.current = 0;
		
		Queue_Add(&gRunningTask, node);
	}
}

static void RunningToReady()
{
	if(Queue_Length(&gRunningTask) > 0)
	{
		TaskNode* tn = (TaskNode*)Queue_Front(&gRunningTask);
	
		if(!IsEqual(tn, gIdleTask))
		{
			if(tn->task.current == tn->task.total)
			{
				Queue_Remove(&gRunningTask);
				Queue_Add(&gReadyTask, (QueueNode*)tn);
			}
		}
	}
}

static void CheckRunningTask()
{
	if(Queue_Length(&gRunningTask) == 0)
	{
		Queue_Add(&gRunningTask, &gIdleTask->head);
	}
	else if(Queue_Length(&gRunningTask) > 1)
	{
		if(Queue_Front(&gRunningTask) == &gIdleTask->head)
		{
			Queue_Remove(&gRunningTask);
		}
	}
}

void TaskModInit()
{
	int i = 0;
    byte* pStack = (byte*)(PageDirBase -  MAX_TASK_BUFF_NUM * AppStackSize);
    
    for(i = 0; i < MAX_TASK_BUFF_NUM; i++)
    {
    	TaskNode* tn = (void*)AddrOff(gTaskBuff, i);
    	
    	tn->task.stack = (void*)AddrOff(pStack, i * AppStackSize);
    }
    
    gIdleTask = (void*)AddrOff(gTaskBuff, MAX_TASK_NUM);
    
    GetAppToRun = (void*)(*((uint*)GetAppToRunEntry));
    GetAppNum = (void*)(*((uint*)GetAppNumEntry));
    
    Queue_Init(&gFreeTaskNode);
    Queue_Init(&gReadyTask);
    Queue_Init(&gRunningTask);
    Queue_Init(&gWaitingTask);
    
    for(i = 0; i < MAX_TASK_NUM; i++)
    {
    	Queue_Add(&gFreeTaskNode, (QueueNode*)AddrOff(gTaskBuff, i));
    }
    
    SetDescValue(AddrOff(gGdtInfo.entry, GDT_TASK_TSS_INDEX), (uint)&gTSS, sizeof(gTSS)-1, DA_386TSS + DA_DPL0);
    
    InitTask(&(gIdleTask->task), 0, "IdleTask", IdleTask, 255);
    
    ReadyToRunning();
    CheckRunningTask();
    
}

void LaunchTask()
{
    gCTaskAddr = &((TaskNode*)Queue_Front(&gRunningTask))->task;
    
    PrepareForRun(gCTaskAddr);
    
    RunTask(gCTaskAddr);
}

void Schedule()
{
	RunningToReady();
	
	ReadyToRunning();
	
	CheckRunningTask();

	Queue_Rotate(&gRunningTask);
	
    gCTaskAddr = &((TaskNode*)Queue_Front(&gRunningTask))->task;
    
    PrepareForRun(gCTaskAddr);
    
    LoadTask(gCTaskAddr);
}

void KillTask()
{
	QueueNode* node = Queue_Remove(&gRunningTask);
	
	Queue_Add(&gFreeTaskNode, node);
	
	Schedule();
}

TaskA,TaskB,TaskC 的 total 值都为1,任务开始被执行时,TaskA 和 TaskB 被放入执行队列,TaskA 的1次执行时间到了后,就把TaskA 移入到就绪队列,TaskC 加入到执行队列, 然后执行 Rotate 操作,将 TaskC 变换到执行队列的队首,去执行 TaskC,TaskC 的一次执行时间到了后,把 TaskC 移入到就绪队列, TaskA加入到执行队列,然后又执行 Rotate 操作,把 TaskA 变换到执行队列的队首,去执行 TaskA;TaskA 任务完成时,又把 TaskC 加入执行队列,执行 Rotate 操作,将 TaskC 变换到队首,去执行 TaskC,这样 TaskB 就永远无法得到执行了。

问题解决

任务B 得不到执行的关键是 要执行任务的 total 值不能都为1,所以我们需要将一个任务的最小 total 值设置为至少大于1。

task.c

#include "utility.h"
#include "task.h"
#include "app.h"

#define MAX_TASK_NUM       4
#define MAX_RUNNING_TASK   2
#define MAX_READY_TASK     (MAX_TASK_NUM - MAX_RUNNING_TASK)
#define MAX_TASK_BUFF_NUM  (MAX_TASK_NUM + 1)
#define PID_BASE           0x10
#define MAX_TIME_SLICE     260

static AppInfo* (*GetAppToRun)(uint index) = NULL;
static uint (*GetAppNum)() = NULL;

void (* const RunTask)(volatile Task* pt) = NULL;
void (* const LoadTask)(volatile Task* pt) = NULL;

volatile Task* gCTaskAddr = NULL;
static TaskNode gTaskBuff[MAX_TASK_BUFF_NUM] = {0};
static TSS gTSS = {0};
static Queue gFreeTaskNode = {0};
static Queue gReadyTask = {0};
static Queue gRunningTask = {0};
static Queue gWaitingTask = {0};
static TaskNode* gIdleTask = NULL;
static uint gAppToRunIndex = 0;
static uint gPid = PID_BASE;

void TaskEntry()
{
	if(gCTaskAddr != NULL)
	{
		gCTaskAddr->tmain();
	}
	
	// to destory current task here
	asm volatile(
	    "movw $0, %ax \n"
	    "int $0x80 \n"
	);
	
	while(1);    // TODO: schedule next task to run
}

void IdleTask()
{  
    while(1);
}

static void InitTask(Task* pt, uint id, const char* name, void(*entry)(), ushort pri)
{
    pt->rv.cs = LDT_CODE32_SELECTOR;
    pt->rv.gs = LDT_VIDEO_SELECTOR;
    pt->rv.ds = LDT_DATA32_SELECTOR;
    pt->rv.es = LDT_DATA32_SELECTOR;
    pt->rv.fs = LDT_DATA32_SELECTOR;
    pt->rv.ss = LDT_DATA32_SELECTOR;
    
    pt->rv.esp = (uint)pt->stack + AppStackSize;
    pt->rv.eip = (uint)TaskEntry;
    pt->rv.eflags = 0x3202;
    
    pt->id = id;
    
    StrCpy(pt->name, name, sizeof(pt->name) - 1);
    
    pt->tmain = entry;
    
    pt->current = 0;
    pt->total = MAX_TIME_SLICE - pri;
    
    gTSS.ss0 = GDT_DATA32_FLAT_SELECTOR;
    gTSS.esp0 = (uint)&pt->rv + sizeof(pt->rv);
    gTSS.iomb = sizeof(TSS);
    
    SetDescValue(AddrOff(pt->ldt, LDT_VIDEO_INDEX),  0xB8000, 0x07FFF,         DA_DRWA + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_CODE32_INDEX), 0x00,    PageDirBase - 1, DA_C + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_DATA32_INDEX), 0x00,    PageDirBase - 1, DA_DRW + DA_32 + DA_DPL3);
    
    pt->ldtSelector = GDT_TASK_LDT_SELECTOR;
    pt->tssSelector = GDT_TASK_TSS_SELECTOR;
}

static void PrepareForRun(volatile Task* pt)
{
	pt->current++;

	gTSS.ss0 = GDT_DATA32_FLAT_SELECTOR;
    gTSS.esp0 = (uint)&pt->rv + sizeof(pt->rv);
    gTSS.iomb = sizeof(TSS);
    
    SetDescValue(AddrOff(gGdtInfo.entry, GDT_TASK_LDT_INDEX), (uint)&pt->ldt, sizeof(pt->ldt)-1, DA_LDT + DA_DPL0);
}

static void CreateTask()
{
	int num = GetAppNum();
	
	while((gAppToRunIndex < num) && (Queue_Length(&gReadyTask) < MAX_READY_TASK))
	{
		TaskNode* tn = (TaskNode*)Queue_Remove(&gFreeTaskNode);
		
		if(tn != NULL)
		{
			AppInfo* app = GetAppToRun(gAppToRunIndex);
			
			InitTask(&tn->task, gPid++, app->name, app->tmain, app->priority);
			
			Queue_Add(&gReadyTask, &tn->head);	
		}
		else
		{
			break;
		}
		
		gAppToRunIndex++;
	}
}

static void ReadyToRunning()
{
	QueueNode* node = NULL;

	if(Queue_Length(&gReadyTask) < MAX_READY_TASK)
	{
		CreateTask();
	}
	
	while((Queue_Length(&gReadyTask) > 0) && (Queue_Length(&gRunningTask) < MAX_RUNNING_TASK))
	{
		node = Queue_Remove(&gReadyTask);
		
		((TaskNode*)node)->task.current = 0;
		
		Queue_Add(&gRunningTask, node);
	}
}

static void RunningToReady()
{
	if(Queue_Length(&gRunningTask) > 0)
	{
		TaskNode* tn = (TaskNode*)Queue_Front(&gRunningTask);
	
		if(!IsEqual(tn, gIdleTask))
		{
			if(tn->task.current == tn->task.total)
			{
				Queue_Remove(&gRunningTask);
				Queue_Add(&gReadyTask, (QueueNode*)tn);
			}
		}
	}
}

static void CheckRunningTask()
{
	if(Queue_Length(&gRunningTask) == 0)
	{
		Queue_Add(&gRunningTask, &gIdleTask->head);
	}
	else if(Queue_Length(&gRunningTask) > 1)
	{
		if(Queue_Front(&gRunningTask) == &gIdleTask->head)
		{
			Queue_Remove(&gRunningTask);
		}
	}
}

void TaskModInit()
{
	int i = 0;
    byte* pStack = (byte*)(PageDirBase -  MAX_TASK_BUFF_NUM * AppStackSize);
    
    for(i = 0; i < MAX_TASK_BUFF_NUM; i++)
    {
    	TaskNode* tn = (void*)AddrOff(gTaskBuff, i);
    	
    	tn->task.stack = (void*)AddrOff(pStack, i * AppStackSize);
    }
    
    gIdleTask = (void*)AddrOff(gTaskBuff, MAX_TASK_NUM);
    
    GetAppToRun = (void*)(*((uint*)GetAppToRunEntry));
    GetAppNum = (void*)(*((uint*)GetAppNumEntry));
    
    Queue_Init(&gFreeTaskNode);
    Queue_Init(&gReadyTask);
    Queue_Init(&gRunningTask);
    Queue_Init(&gWaitingTask);
    
    for(i = 0; i < MAX_TASK_NUM; i++)
    {
    	Queue_Add(&gFreeTaskNode, (QueueNode*)AddrOff(gTaskBuff, i));
    }
    
    SetDescValue(AddrOff(gGdtInfo.entry, GDT_TASK_TSS_INDEX), (uint)&gTSS, sizeof(gTSS)-1, DA_386TSS + DA_DPL0);
    
    InitTask(&(gIdleTask->task), 0, "IdleTask", IdleTask, 255);
    
    ReadyToRunning();
    CheckRunningTask();
    
}

void LaunchTask()
{
    gCTaskAddr = &((TaskNode*)Queue_Front(&gRunningTask))->task;
    
    PrepareForRun(gCTaskAddr);
    
    RunTask(gCTaskAddr);
}

void Schedule()
{
	RunningToReady();
	
	ReadyToRunning();
	
	CheckRunningTask();

	Queue_Rotate(&gRunningTask);
	
    gCTaskAddr = &((TaskNode*)Queue_Front(&gRunningTask))->task;
    
    PrepareForRun(gCTaskAddr);
    
    LoadTask(gCTaskAddr);
}

void KillTask()
{
	QueueNode* node = Queue_Remove(&gRunningTask);
	
	Queue_Add(&gFreeTaskNode, node);
	
	Schedule();
}

我们定义了宏 MAX_TIME_SLICE 最大时间分片为260,所以一个任务最少执行的次数为 (260 - 255 = 5)次,就不会出现一个在执行队列里的任务永远都无法被执行的情况了。

深入讨论

任务调度的定义

 当多个任务处于就绪状态,而处理器只有一个时,操作系统就必须决定哪一个任务先执行

操作系统中做出这种决定的部分称为调度器,调度器使用的算法为调度算法

关于调度算法的分类

非抢占式调度算法

  • 任务持续执行直到阻塞 (等待事件),或自愿让出执行权

抢占式调度算法

  • 任务每次执行的时间固定,时间一到被迫让出执行权

什么时候进行任务调度?

当任务退出时 (运行结束)

当新任务创建时

当时钟中断发生时

当任务 I/O 中断发生时

任务的分类

计算密集型任务

  • 任务大部分时间在使用处理器运算 (如:科学运算)

I/O 密集型任务

  • 任务大部分时间用于等待 I/O 操作 (如:文件操作)

关于优先级调度

优先级用于反应任务的重要程度

优先级越高,任务越容易获得执行的机会

  • 越多的执行时间 (计算密集型任务)
  • 越快的响应速度 (I/O 密集型任务)

调度算法的终极目标

公平公正,每个任务都能被执行

最大化处理器利用率,保持处理器始终忙碌

最小化任务从执行到完成的时间

最快响应时间,快速响应用户交互

课后拓展

FCFS 先到先服务调度算法 (First Come First Served)

SJF 最短执行时间调度算法 (Shortest Job First)

RR 轮询调度算法 (Round Robin)

MQ 多级队列调度算法 (Multilevel Queue)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值