AUTOSAR OS 基于Tricore的Spinlock的实现原理

  一、什么是自旋锁?

        自旋锁的定义:当一个线程尝试去获取某一把锁的时候,如果这个锁已经被另外一个线程占有了,那么此线程就无法获取这把锁,该线程会等待,间隔一段时间后再次尝试获取。这种采用循环加锁,等待锁释放的机制就称为自旋锁(spinlock)。

        Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的,即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到,只能够原地“打转”(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制 —— 自旋锁不应该被长时间的持有(消耗 获取自旋锁的CPU 资源)。 

        spinlock是专为防止多处理器并发而引入的一种锁。

二、为什么需要自旋锁?

        在多线程和多核程序里,会有一些资源需要互斥访问。例如共享RAM区的读写操作,当RAM区数据量大时,一个指令周期无法完成读写操作,然而中断或者其他核也来读写此RAM区,就会导致数据的一致性被破坏,严重的情况下会影响实际的功能。为了避免出现这种情况,操作系统引入了自旋锁和互斥锁

        互斥锁是一种用于多线程编程的同步机制,用于保护临界区资源免受多个线程同时访问和修改的影响。它确保在任何给定的时间点只有一个线程可以获得对临界区资源的访问权。它的特点是当一个线程需要访问临界区资源时,首先需要调用加锁操作来获取互斥锁。如果互斥锁已经被其他线程占用,则当前线程会被阻塞,直到锁可用。它会频繁引用线程被阻塞,操作系统会发生频繁的系统调度,所以应尽量减少互斥锁的使用。

        针对多核系统,互斥锁会阻塞所有使用资源的线程,带来线程的切换的额外开销。为了解决这个问题引入了自旋锁,自旋锁的主要优势在于减少线程切换的开销和快速响应。由于自旋锁避免了线程切换和进入阻塞状态,因此在锁争用情况下,自旋锁的响应时间通常比阻塞锁更短。此外,自旋锁适用于短暂竞争的情况,如果持有锁的线程能够在短时间内释放锁,那么等待竞争锁的线程就不需要做内核态与用户态之间的切换,从而进入阻塞状态。这种机制在Linux内核中得到了广泛应用,其中自旋锁是保护临界区的一种常用手段,通过忙等待的方式快速响应锁的释放,减少不必要的资源消耗和延迟‌。

三、AUTOSAR OS 自旋锁的实现原理

        AUTOSAR OS提供了GetSpinlock和ReleaseSpinlock接口来占用和释放锁。当程序获锁时,会进入死循环,一直等待获锁成功,待实际的操作完成后立即释放锁。

如下图

3.1 多核同时获锁操作问题

        自旋锁实现的基本原理比较简单,自旋锁实际上是一个变量,当变量为0时,代表锁是释放的状态,不为0时代表上锁状态。获锁操作即对锁变量写非0值。获锁操作需要执行以下几个步骤:

        a.判断锁变量是否是释放状态

        b.如果是,则上锁写1;否则一直等待

        c.如果获锁成功,则对资源进行写操作

        假设在第一步有两个核同时读取锁的状态,两个核都认为锁是释放状态,他们会同时对锁变量写不同的值,后续两个核同时对共享资源进行写操作,这样就出现了共享资源无法互斥访问的问题。

3.2 多核同时获锁的问题解决

        那么如何解决多核同时获锁异常问题呢?

        以上问题产生的根本原因是,判断锁状态与上锁的过程被两个核同时执行导致的,如果将ab两个步骤的操作变成一步操作,即一个时钟周期完成,这样就不会被其他核打断,当其他核获取锁状态时,已经被上锁了。如此就完美解决了两个核对一个锁同时上锁的问题。

        那么tricore是如何实现的呢?它使用了原子指令CMPSWAP.W。

        这条指令的格式:

        cmpswap.w Ax, Dm, Dn

        功能是当Ax指向的内容与Dn相等时将Dm内容赋值给Ax指向的内容.同时返回Ax指向的内容。这条指令属于原子操作,原子操作的特点是它将强制此指令前的所有指令执行完成,任何缓存的数据都会在这条指令前被写入存储器。如果多核同时执行此指令,总线仲裁器会按优先级来处理各个核的执行请求,并且总是有先后顺序。从而保证了指令操作的原子性。

3.3 AUTOSAR OS的自旋锁实现原理

        以下是AUTOSAR OS自旋锁实现的基本步骤,基于tricore芯片,使用了内置库函数compswap,它实际调用了汇编 cmpswap.w指令。

        由示例程序可以看出,当判断锁状态无释放状态时,即使多个核同时尝试获锁,只有首选执行获锁操作的核将核ID赋值给锁变量,后续尝试获锁的核都将显示获锁不成功状态,从而处于一直等待状态,直到锁被释放才能成功获锁。在此过程中,程序一直于死循环,将消息CPU资源。等待的时间取决于占用锁的核对资源访问的时间。


Os_SpinlockGet,
(
   Spinlock
))                                                                                                                      
{

  Os_Hal_SpinlockResultType result;


  /*  Loop. */
  do
  {
    /* Spin while the spinlock is locked. */
    while(Os_SpinIsLocked(&(spinlock->Spinlock)) != 0u)                                                          
    {
      Os_Hal_CoreNop();
    }

    /*Try to get the lock. */
    result = Os_SpinlockTryGet(Spinlock);                                                                       

  /*  Loop while locking failed. */
  } while(result != OS_HAL_SPINLOCKSUCCEEDED);                                      
}



Os_Hal_SpinTryGet,
(
  Spinlock
))
{
  Os_SpinlockResultType retVal = Os_CmpSwap(Spinlock, Os_CoreGetId(), 
  return retVal;
}

实际案例,一个共享变量会被封装成RTE接口,在RTE接口内访问自旋锁:

-----------------------------------------END--------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值