进程状态设计(上)

问题

如何设计 KillTask() 函数?

任务生命周期

任务从开始执行到结束执行经过的时间

任务的状态

任务在生命周期中会经历不同状态 (创建,销毁)

任务的生命周期与状态转换

 TaskNodeBuffer 数组里存有多个任务,此时处于创建的状态,还没有将任务初始化;当将任务初始化后,就处于就绪状态,随时可能被处理器调度;当有任务被处理器调度时,这个任务处于执行状态;当任务处于执行状态,又需要与外设交互,等待外设处理结果返回时,这个任务处于等待状态;当这个任务执行完,操作系统需要销毁这个任务并释放这个任务的系统资源,此时这个任务处于销毁状态。

状态切换概要设计

为每种状态准备内核队列 (就绪队列,执行队列,等待队列)

任务创建后立即加入就绪队列

调度器根据当前执行的任务数量决定调度策略

  • 执行时间结束的任务进入就绪队列
  • 等待外部事件的任务进入等待队列
  • 等待队列中的任务必须先进入就绪队列才能继续执行

核心数据结构 => 内核队列

KillTask() 实现思路

当 KillTask() 被调用时,意味着当前执行的任务结束

因此:

1. 将当前任务从执行队列中移除,并移入空闲 TaskNode 队列

2. 调度就绪队列中的任务进入执行队列 (如果就绪队列存在任务)

3. 执行队列中的队首任务被调度执行 (调度下一个任务执行)

整体实现规划

1. 实现空闲 TaskNode 队列 (填充预定义数量的 TaskNode)

2. 初始化预定义任务 (调度进入就绪队列)

3. 启动第一个任务 (调度进入执行队列)

4. 实现 KillTask() 。。。

任务生命期状态实现

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)

extern AppInfo* GetAppToRun(uint index);
extern uint GetAppNum();

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

volatile Task* gCTaskAddr = NULL;
static TaskNode gTaskBuff[MAX_TASK_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 = {0};
static uint gAppToRunIndex = 0;

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()
{  
	int i = 0;

    SetPrintPos(0, 10);

    PrintString(__FUNCTION__);
    
    while(1)
    {
        SetPrintPos(10, 10);
        PrintChar('A' + i);
        i = (i + 1) % 26;
        Delay(1);
    }
}

static void InitTask(Task* pt, const char* name, void(*entry)())
{
    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 + sizeof(pt->stack);
    pt->rv.eip = (uint)TaskEntry;
    pt->rv.eflags = 0x3202;
    
    StrCpy(pt->name, name, sizeof(pt->name) - 1);
    
    pt->tmain = entry;
    
    SetDescValue(AddrOff(pt->ldt, LDT_VIDEO_INDEX),  0xB8000, 0x07FFF, DA_DRWA + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_CODE32_INDEX), 0x00,    0xFFFFF, DA_C + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_DATA32_INDEX), 0x00,    0xFFFFF, DA_DRW + DA_32 + DA_DPL3);
    
    pt->ldtSelector = GDT_TASK_LDT_SELECTOR;
    pt->tssSelector = GDT_TASK_TSS_SELECTOR;
}

static void PrepareForRun(volatile Task* pt)
{
	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, app->name, app->tmain);
			
			Queue_Add(&gReadyTask, &tn->head);	
		}
		else
		{
			break;
		}
		
		gAppToRunIndex++;
	}
}

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

	if(Queue_Length(&gReadyTask) == 0)
	{
		CreateTask();
	}
	
	while((Queue_Length(&gReadyTask) > 0) && (Queue_Length(&gRunningTask) < MAX_RUNNING_TASK))
	{
		node = Queue_Remove(&gReadyTask);
		
		Queue_Add(&gRunningTask, node);
	}
}

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;
    
    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), "IdleTask", IdleTask);
    
    ReadyToRunning();
    CheckRunningTask();
    
}

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

void Schedule()
{
	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();
}

app.h


#ifndef APP_H
#define APP_H

#include "type.h"

typedef struct
{
	const char* name;
	void (*tmain)();
} AppInfo;

void AppModInit();
AppInfo* GetAppToRun(uint index);
uint GetAppNum();

#endif

app.c


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

#define MAX_APP_NUM  16

static AppInfo gAppToRun[MAX_APP_NUM] = {0};
static uint gAppNum = 0;

void TaskA();
void TaskB();
void TaskC();
void TaskD();

static void RegApp(const char* name, void (*tmain)())
{
	if(gAppNum < MAX_APP_NUM)
	{
		AppInfo* app = AddrOff(gAppToRun, gAppNum);
		
		app->name = name;
		app->tmain = tmain;
		
		gAppNum++;
	}
}

void AppModInit()
{
	RegApp("TaskA", TaskA);
	RegApp("TaskB", TaskB);
	RegApp("TaskC", TaskC);
	RegApp("TaskD", TaskD);
}

AppInfo* GetAppToRun(uint index)
{
	AppInfo* ret = NULL;
	
	if(index < MAX_APP_NUM)
	{
		ret = (AppInfo*)AddrOff(gAppToRun, index);
	}
	
	return ret;
}

uint GetAppNum()
{
	return gAppNum;
}

void TaskA()
{
    int i = 0;
    
    SetPrintPos(0, 12);
    
    PrintString(__FUNCTION__);
    
    while(i < 5)
    {
        SetPrintPos(8, 12);
        PrintChar('A' + i);
        i = (i + 1) % 26;
        Delay(1);
    }
    
    SetPrintPos(8, 12);
}

void TaskB()
{
    int i = 0;
    
    SetPrintPos(0, 13);
    
    PrintString(__FUNCTION__);
    
    while(1)
    {
        SetPrintPos(8, 13);
        PrintChar('0' + i);
        i = (i + 1) % 10;
        Delay(1);
    }
}

void TaskC()
{
    int i = 0;
    
    SetPrintPos(0, 14);
    
    PrintString(__FUNCTION__);
    
    while(1)
    {
        SetPrintPos(8, 14);
        PrintChar('a' + i);
        i = (i + 1) % 26;
        Delay(1);
    }
}

void TaskD()
{
    int i = 0;
    
    SetPrintPos(0, 15);
    
    PrintString(__FUNCTION__);
    
    while(1)
    {
        SetPrintPos(8, 15);
        PrintChar('!' + i);
        i = (i + 1) % 10;
        Delay(1);
    }
}

我们在 task.c 中定义了空闲队列、就绪队列、等待队列和运行队列;并定义了最大任务数量为4,运行队列里最多有2个任务,就绪队列里最多有2个任务。

由于 task.c 应该存放内核的代码,但我们却把一些用户的任务也定义在这个源文件里,所以我们新定义了 app.c、app.h 来存放用户的代码。

我们把4个任务搬到了 app.c,在 app.h 中定义了 AppInfo 结构体,里面存放了任务的名字和任务的入口地址;RegApp 函数用于将一个任务名字和入口地址注册到 AppInfo 结构体数组 gAppToRun 中。GetAppToRun 函数通过数组下标拿到一个任务的名字和入口地址。

task.c 中,CreateTask 函数当有任务可以被创建并且就绪队列没满时,取走 gFreeTaskNode 空闲队列的一个空闲任务,拿到一个 app 的名字和入口地址,用来初始化这个任务,最后将这个任务加入就绪队列。

ReadyToRunning 函数:当就绪队列中没有任务时,我们调用 CreateTask 函数来创建任务,并且在就绪队列不为空,运行队列没满时,将就绪队列的队首任务加入到运行队列。

CheckRunningTask 函数:保证一定有一个任务会被执行,当运行队列为空时,就把 IdleTask 任务加入队列,在运行队列中有1个以上的任务时并且队首为 IdleTask 任务时,将 IdleTask 任务移除。

KillTask 函数:将运行完成的任务从运行队列移除,并将这个结点插入到空闲队列中,以便有资源创建新的任务。

实验结果

当前有4个任务,A任务只会运行一段时间,B、C、D 任务会一直运行下去。

当前程序设置的是最多同时运行2个任务,一开始A任务和B任务并行执行,当A任务运行结束时,C任务就开始和B任务并行执行了。

 当运行队列中没有任务时,就会将 IdleTask 添加到运行队列中,保证一个会有一个任务会被执行。

一开始A任务在运行,等A任务运行结束,运行队列没有任务时,IdleTask 任务就被加入运行队列,运行 IdleTask 任务。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值