进程等待机制的实现(下)

问题

完整实现等待机制还有哪些工作要做?

关键实现步骤

新增 Wait(const char* name) 系统调用接口

拓展 RunningToWaiting() 内核函数:参数化等待队列

拓展 WaitingToReady() 内核函数:参数化等待队列

实现 FindTaskByName():通过名字查找任务数据结构

关键问题

任务分散于各个队列中,如何快速通过名字查找目标任务?

边界情况处理

当目标任务不存在时,立即返回,无需阻塞

任务不能等待"IdleTask",Wait("IdleTask") 将立即返回

进程等待机制的实现

task.h

typedef struct
{
    RegValue   rv;
    Descriptor ldt[3];
    ushort     ldtSelector;
    ushort     tssSelector;
    void (*tmain)();
    uint       id;
    ushort     current;
    ushort     total;
    char       name[16];
    Queue      wait; 
    byte*      stack;
} Task;

syscall.c

void Wait(const char* name)
{
	SysCall(0, 1, name, 0);
}

ihandler.c

void SysCallHandler(uint type, uint cmd, uint param1, uint param2)    // __cdcel__
{
	switch(type)
	{
		case 0:
			TaskCallHandler(cmd, param1, param2);
			break;
		case 1:
			MutexCallHandler(cmd, param1, param2);
			break;
		default:
			break;
	}
}

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(
	    "movl $0, %eax \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);
    
    Queue_Init(&pt->wait);
    
    pt->tmain = entry;
    
    pt->current = 0;
    pt->total = MAX_TIME_SLICE - pri;
    
    SetDescValue(AddrOff(pt->ldt, LDT_VIDEO_INDEX),  0xB8000, 0x07FFF,            DA_DRWA + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_CODE32_INDEX), 0x00,    KernelHeapBase - 1, DA_C + DA_32 + DA_DPL3);
    SetDescValue(AddrOff(pt->ldt, LDT_DATA32_INDEX), 0x00,    KernelHeapBase - 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 WaitingToReady(Queue* wq)
{
	while(Queue_Length(wq) > 0)
	{
		TaskNode* tn = (TaskNode*)Queue_Front(wq);
	
		PrintString("W2R: ");
		PrintString(gCTaskAddr->name);
		PrintString(" => ");
		PrintString(tn->task.name);
		PrintChar('\n');
	
		Queue_Remove(wq);
		Queue_Add(&gReadyTask, (QueueNode*)tn);
	}
}

static void RunningToWaiting(Queue* wq)
{
	if(Queue_Length(&gRunningTask) > 0)
	{
		TaskNode* tn = (TaskNode*)Queue_Front(&gRunningTask);
	
		if(!IsEqual(tn, gIdleTask))
		{
			Queue_Remove(&gRunningTask);
			Queue_Add(wq, (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*)(AppHeapBase - 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);
}

static void ScheduleNext()
{
	ReadyToRunning();
	
	CheckRunningTask();

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

void Schedule()
{
	RunningToReady();
	ScheduleNext();
}

void MtxSchedule(uint action)
{
	if(IsEqual(action, NOTIFY))
	{
		WaitingToReady(&gWaitingTask);
	}
	else if(IsEqual(action, WAIT))
	{
		RunningToWaiting(&gWaitingTask);
		ScheduleNext();
	}
}

void KillTask()
{
	QueueNode* node = Queue_Remove(&gRunningTask);
	Task* pt = &((TaskNode*)node)->task;
	
	pt->id = 0;
	
	WaitingToReady(&pt->wait);
	
	Queue_Add(&gFreeTaskNode, node);
	
	ScheduleNext();
}

static Task* FindTaskByName(const char* name)
{
	Task* ret = NULL;

	if(!StrCmp(name, "IdleTask", -1))
	{
		int i = 0;
		
		for(i = 0; i < MAX_TASK_NUM; i++)
		{
			TaskNode* tn = AddrOff(gTaskBuff, i);
			
			if(tn && tn->task.id && StrCmp(name, tn->task.name, -1))
			{
				ret = &tn->task;
				
				break;
			}
		}
	}
	
	return ret;
}

static void WaitTask(const char* name)
{
	if(name)
	{
		Task* pt = FindTaskByName(name);
		
		if(pt)
		{
			PrintString("WT: ");
			PrintString(gCTaskAddr->name);
			PrintString(" wait ");
			PrintString(pt->name);
			PrintChar('\n');
			
			RunningToWaiting(&pt->wait);
			ScheduleNext();
		}
	}
}

void TaskCallHandler(uint cmd, uint param1, uint param2)
{
	switch(cmd)
	{
		case 0:
			KillTask();
			break;
		case 1:
			WaitTask((char*)param1);
			break;
		default:
			break;
	}
}

app.c

void AppMain()
{	
	RegApp("CookRice", CookRice, 255);
	RegApp("CookDish", CookDish, 255);
	RegApp("HaveDinner", HaveDinner, 255);
}

void CookRice()
{
	SetPrintPos(0, 12);
    
    PrintString(__FUNCTION__);
    
    PrintChar('\n');
    
    for(i = 0; i < 50; i++)
    {
    	SetPrintPos(12, 12);
    	PrintChar('A' + i % 26);
    	Delay(1);
    }
    
}

void CookDish()
{
	SetPrintPos(0, 14);
    
    PrintString(__FUNCTION__);
    
    PrintChar('\n');
    
    for(i = 0; i < 30; i++)
    {
    	SetPrintPos(12, 14);
    	PrintChar('0' + i % 10);
    	Delay(1);
    }
}

void HaveDinner()
{	
	Wait("CookDish");
	Wait("CookRice");

	SetPrintPos(10, 16);
	PrintString("Having dinner...");
}

Task 任务结构体中新增了 wait 队列,这个队列为任务等待队列。新增了函数 wait,这是一个系统调用接口,用来等待一个任务执行结束。当调用这个函数时,将当前任务放到要等待的任务的等待队列中,当要等待的任务运行结束时,再把这个执行结束的等待队列中的任务放回就绪队列,等待调度执行。

我们在一个任务运行结束时,把该任务的 id 号标记为0,代表任务已运行结束。通过任务名查找任务,只查找还在运行并且不是 IdleTask 的任务(id 不为0)。

在等待 CookRice 和 CookDish 执行结束时,HaveDinner 才开始执行。 

经典问题一

 TaskA 想要占用相同的锁,从而造成死锁。

经典问题二

当 TaskA 获取了互斥锁A,想要获取互斥锁B,而 TaskB 获取了互斥锁B,想要获取互斥锁A时,就会产生死锁。

经典问题三

 TaskA 等待 TaskB 运行结束时,再向下执行;而 TaskB 等待 TaskA 运行结束时,再向下执行,从而造成了死锁。

死锁问题

现象描述

  • 任务因所需的资源无法获得而进入无限阻塞的状态

发生的条件

  • 互斥:任务需要对资源进行互斥访问 (临界资源)
  • 持有并等待:任务获得了资源,同时又在等待其他资源
  • 循环等待:任务之间在相互等待 (等待路径形成环路)
  • 非抢占:任务已获得的资源不能被抢夺

小结

任务之间除了互斥关系还有等待关系

任务之间的等待可以看作对执行时间的互斥

任务之间可能会因为对多个临界资源的竞争而产生死锁

可根据死锁发生的条件精心设计多任务,避免死锁的发生

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值