进程互斥锁的优化实现

本文探讨了在操作系统中,任务如何通过互斥锁实现临界区的保护。当任务因获取互斥锁失败而阻塞后,重新进入执行态时,必须尝试获取锁,成功则进入临界区,失败则继续等待。同时,提出了优化设计,允许同一任务多次获取同一锁而不阻塞,并防止非法释放占用的锁,确保系统稳定。在实现上,通过系统调用和内核函数协同工作,确保互斥锁的正确使用。
摘要由CSDN通过智能技术生成

问题

在当前实现下,阻塞的多个任务重新进入执行态,会发生什么?

这些任务不会去争夺互斥锁,而是继续向下执行。

当前实现的深入分析

解决方案设计

任务从阻塞中 (因获取互斥锁失败而阻塞) 恢复执行时

  • 必须再次尝试获取锁

内核需要返回获取锁的结果 (成功 or 失败)

  • 成功:任务继续执行,进入临界区
  • 失败:任务等待机会,再次尝试获取锁

解决方案实现步骤

1. 在系统调用 EnterCritical() 中,向内核传入标记变量 wait 的地址

2. 内核根据目标 Mutex 的状态设置标记变量的 wait 的值

3. 任务从系统调用返回后,判断标记变量 wait 的值

  • True - 再次尝试获取锁
  • False - 无需再次尝试,继续向下执行

问题

如果有任务比较聪明,在进入临界区之前,先主动调用 ExitCritical(),会发生什么?

下面的任务执行后会发生什么?

 在 TaskA 进入临界区后,TaskB 获得了互斥锁,也进入了临界区,TaskB 并没有阻塞。

下面任务的行为合法吗?

互斥锁的优化设计

同一个任务中可多次获取同一个互斥锁

  • 同一个任务中多次调用 EnterCritical() 不会阻塞

只有获取锁的任务,才能释放锁

  • 强行释放锁的 (异常行为) 任务将被内核直接杀死

无法销毁处于占用状态的互斥锁

  • 强行调用 DestroyMutex() 销毁占用状态的锁,将无效返回

互斥锁的优化实现方案

任务通过自身标识对锁进行标记

  • mutex->lock = (uint)gCTaskAddr;

释放锁时,通过锁标记与任务自身标识判断合法性

  • IsEqual(mutex->lock, gCTaskAddr)

只有未标识的锁能被销毁

  • IsEqual(mutex->lock, 0) => true

互斥锁的优化实现

syscall.c


#include "syscall.h"

#define SysCall(type, cmd, param1, param2)    asm volatile(                                 \
	                                                        "movl $" #type ", %%eax   \n"   \
	                                                        "movl $" #cmd ", %%ebx    \n"   \
	                                                        "movl %0, %%ecx           \n"   \
	                                                        "movl %1, %%edx           \n"   \
	                                                        "int $0x80                \n"   \
	                                                        :                               \
	                                                        : "r"(param1), "r"(param2)      \
	                                                        : "eax", "ebx", "ecx", "edx"    \
	                                                      )

void Exit()
{	
	SysCall(0, 0, 0, 0);
}

uint CreateMutex()
{
	volatile uint ret = 0;
	
	SysCall(1, 0, &ret, 0);
	
	return ret;
}

void EnterCritical(uint mutex)
{
	volatile uint wait = 0; 
	
	do
	{
		SysCall(1, 1, mutex, &wait);
		
	}while(wait);
}

void ExitCritical(uint mutex)
{
	SysCall(1, 2, mutex, 0);
}

uint DestroyMutex(uint mutex)
{	
	uint ret = 0;
		
	SysCall(1, 3, mutex, &ret);
	
	return ret;
}

mutex.c


#include "mutex.h"
#include "memory.h"
#include "task.h"

extern volatile Task* gCTaskAddr;

static List gMList = {0};

void MutexModInit()
{
	List_Init(&gMList);
}

void MutexCallHandler(uint cmd, uint param1, uint param2)
{
	if(cmd == 0)
	{
		uint* pRet = (uint*)param1;
		
		*pRet = (uint)SysCreateMutex();
	}
	else if(cmd == 1)	
	{
		SysEnterCritical((Mutex*)param1, (uint*)param2);
	}
	else if(cmd == 2)
	{
		SysExitCritical((Mutex*)param1);
	}
	else
	{
		SysDestroyMutex((Mutex*)param1, (uint*)param2);
	}
}

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;
	}
	
	return ret;
}

void SysDestroyMutex(Mutex* mutex, uint* result)
{
	if(mutex)
	{
		ListNode* pos = NULL;
		
		List_ForEach(&gMList, pos)
		{
			if(IsEqual(pos, mutex))
			{
				if(IsEqual(mutex->lock, 0))
				{
					List_DelNode(pos);
					Free(pos);
					
					*result = 1;
				}
				else
				{
					*result = 0;
				}
				break;
			}
		}
	}
}

void SysEnterCritical(Mutex* mutex, uint* wait)
{
	if(IsMutexValid(mutex))
	{
		if(mutex->lock)
		{
			if(IsEqual(mutex->lock, gCTaskAddr))
			{
				*wait = 0;
			}
			else
			{
				MtxSchedule(WAIT);
			
				*wait = 1;
			}
		}
		else
		{
			mutex->lock = (uint)gCTaskAddr;
			
			*wait = 0;
		}
	}
}

void SysExitCritical(Mutex* mutex)
{
	if(IsMutexValid(mutex))
	{
		if(IsEqual(mutex->lock, gCTaskAddr))
		{
			mutex->lock = 0;
		
			MtxSchedule(NOTIFY);
		}
		else
		{
			KillTask();
		}
	}
}

首先我们修改了 EnterCritical 函数,内核会根据互斥锁是否已经占用而修改 wait 的值,为1的话,说明当前已经有任务进入临界区,等互斥锁被释放,它需要循环去争夺互斥锁。

我们解决了同一个任务重复想去占用互斥锁的情形,不会阻塞。

如果没有占用互斥锁的任务想要释放锁,则内核会将这个任务销毁。

只有在互斥锁被释放时,才能销毁互斥锁。

小结

互斥锁是一种特殊的内核变量,用于保护临界资源

多个任务在互斥锁的协调下,能够有序互斥的执行

互斥锁的具体实现需要在内核中完成

互斥锁的使用需要遵循预定的规则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值