问题
在内核中实现互斥锁的功能是必要的吗?
是必要的。
在内核态实现互斥锁功能的考虑
进入内核态之后所有任务均暂停执行
直观的决定当前任务是否能进入临界区(True or False)
- True:返回用户态,当前任务继续执行
- False:当前任务进入阻塞状态,调度下一任务执行
内核拥有最高特权级,能够决定任务的生死
互斥锁内核数据结构及接口实现
设计思想:通过内核链表管理 Mutex 变量,只有链表中的 Mutex 变量才能完成任务间的互斥。
创建互斥锁
销毁互斥锁
进入临界区
退出临界区
互斥锁的内核实现
mutex.h
#ifndef MUTEX_H
#define MUTEX_H
#include "type.h"
#include "list.h"
typedef struct
{
ListNode head;
uint lock;
} Mutex;
void MutexModInit();
void MutexCallHandler(uint cmd, uint param);
Mutex* SysCreateMutex();
void SysEnterCritical(Mutex* mutex);
void SysExitCritical(Mutex* mutex);
void SysDestroyMutex(Mutex* mutex);
#endif
mutex.c
#include "mutex.h"
#include "memory.h"
static List gMList = {0};
void MutexModInit()
{
List_Init(&gMList);
}
void MutexCallHandler(uint cmd, uint param)
{
if(cmd == 0)
{
uint* pRet = (uint*)param;
*pRet = (uint)SysCreateMutex();
}
else if(cmd == 1)
{
SysEnterCritical((Mutex*)param);
}
else if(cmd == 2)
{
SysExitCritical((Mutex*)param);
}
else
{
SysDestroyMutex((Mutex*)param);
}
}
static uint IsMutexValid(Mutex* mutex)
{
uint ret = 0;
if(mutex)
{
ListNode* pos = NULL;
List_ForEach(&gMList, pos)
{
if(IsEqual(pos, mutex))
{
ret = 1;
break;
}
}
}
return ret;
}
Mutex* SysCreateMutex()
{
Mutex* ret = (Mutex*)Malloc(sizeof(Mutex));
if(ret)
{
List_Add(&gMList, (ListNode*)ret);
ret->lock = 0;
}
PrintString("Create Mutex, ID: ");
PrintIntHex((uint)ret);
PrintChar('\n');
return ret;
}
void SysDestroyMutex(Mutex* mutex)
{
if(mutex)
{
ListNode* pos = NULL;
List_ForEach(&gMList, pos)
{
if(IsEqual(pos, mutex))
{
List_DelNode(pos);
Free(pos);
PrintString("Destroy Mutex, ID: ");
PrintIntHex((uint)mutex);
PrintChar('\n');
break;
}
}
}
}
void SysEnterCritical(Mutex* mutex)
{
if(IsMutexValid(mutex))
{
if(mutex->lock)
{
PrintString("Move current to waitting status.\n");
}
else
{
mutex->lock = 1;
PrintString("Enter critical section, access critical resource.\n");
}
}
}
void SysExitCritical(Mutex* mutex)
{
if(IsMutexValid(mutex))
{
mutex->lock = 0;
PrintString("Notify all tasks to run again, critical resource is available.\n");
}
}
app.c
void TaskA()
{
int i = 0;
volatile uint mutex = 0;
SetPrintPos(0, 12);
PrintString(__FUNCTION__);
PrintChar('\n');
mutex = CreateMutex();
PrintString("Mutex ID: ");
PrintIntHex(mutex);
PrintChar('\n');
EnterCritical(mutex);
ExitCritical(mutex);
DestroyMutex(mutex);
}
我们用链表作为实现 Mutex 的数据结构,只有在链表中的 Mutex 变量才能完成任务间的互斥。Mutex 中的 lock 成员作为标志位,用来标识此互斥锁是处于空闲状态还是占用状态。
我们在 TaskA 中创建了一个互斥锁,模拟了进入临界区和退出临界区,最后销毁了互斥锁。我们在3个地方打印了互斥锁的 ID 号,一次在内核创建互斥锁的时候,一次在内核销毁互斥锁的时候,一次在应用程序调用 CreateMutex 函数后,它们的 Mutex ID 值都相等,说明应用程序能够正确的使用内核分配出来的互斥锁。
思考
在当前实现下,阻塞的多个任务重新进入执行态,会发生什么?