Skip to content

Latest commit

 

History

History
55 lines (29 loc) · 4.71 KB

suo.md

File metadata and controls

55 lines (29 loc) · 4.71 KB

信号量(Semaphore)

Linux中的信号量是一种睡眠锁。如有一个任务试图获得一个已被持有的信号量时,信号量会将其推进等待队列,然后让其睡眠。当持有信号量的进程将信号量开释后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

信号量分为二元信号量和多元信号量,所谓二元信号量就是指该信号量只有两个状态,要么被占用,要么空闲;而多元信号量则允许同时被N个线程占有,超出N个外的占用请求将被阻塞。信号量是“系统级别”的,即同一个信号量可以被不同的进程访问。

TODO 了解一下当年操作系统中学习的PV操作。读者写者、生产者消费者、哲学家就餐问题。

互斥量 (Mutex)

和二元信号量类似,不同的是互斥量的获取和释放必须是在同一个线程中进行的。如果一个线程不能去释放一个并不是它所占有的互斥量。而信号量是可以由其它线程进行释放的。

临界区(Critical Section)

把获取临界区的锁称为进入临界区,而把锁的释放称为离开临界区。Spinlock就是为了保护这临界区。

读写锁(Read-Write Lock)

如果程序大部分时间都是在读取,使用前面的锁时,每次读也要申请锁的,会导致其他线程就无法再对此段数据进行同步读取。我们知道对数据进行读取时,不存在数据同步问题的,那么这些读锁就影响了程序的性能。读写锁的出现就是为了解决这个问题的。

读写锁,有两种获取方式:共享(Shared)或独占 (Exclusive)。如果当前读写锁处于空闲状态,那么当多个线程同时以共享方式访问该读写锁时,都可以成功;而如果一个线程以独占的方式访问该读写锁,那么它会等待所有共享访问都结束后才可以成功。在读写锁被独占的过程中,再次共享和独占请求访问该锁,都会进行等待状态。

条件变量(Condition Variable)

条件变量相当于一种通知机制。多个线程可以设置等待该条件变量,一旦另外的线程设置了该条件变量(相当于唤醒条件变量)后,多个等待的线程就可以继续执行了。

Spin Lock

我们来看下spinlock, spinlock叫做自旋锁,最初针对SMP系统,实现在SMP多处理器情况下临界区保护。

主要作用是给临界数据加锁,从而保护临界数据不被同时访问,实现多任务的同步。如果临界数据当前不可访问,那么就自旋直到可以访问为止。

自旋锁和互斥锁存在差异的是自旋锁不会引起调用者睡眠,如果自旋锁无法获取,那么调用者一直循环检测自旋锁直到释放。

spinlock的工作方式本身就体现了它的优缺点,优点是执行速度快,不涉及上下文切换;缺点是耗费CPU资源。

在Linux内核中,自旋锁通常用于包含内核数据结构的操作,可以看到在许多内核数据结构中都嵌入有spinlock,这些大部分就是用于保证它自身被操作的原子性(原子操作atomic operation为"不可被中断的一个或一系列操作",最后其实是通过底层硬件来保证的),在操作这样的结构体时都经历这样的过程:上锁-操作-解锁。

因为在现代处理器系统中,考虑到中断、内核抢占以及其他处理器的访问,所以spinlock自旋锁应该阻止在代码运行过程中出现的其他并发干扰。

spin lock的特点

  • spin lock是一种死等的锁机制。当发生访问资源冲突的时候,可以有两个选择:一个是死等,一个是挂起当前进程,调度其他进程执行。spin lock是一种死等的机制,当前的执行thread会不断的重新尝试直到获取锁进入临界区。
  • 只允许一个thread进入。semaphore可以允许多个thread进入,spin lock不行,一次只能有一个thread获取锁并进入临界区,其他的thread都是在门口不断的尝试。
  • 执行时间短。由于spin lock死等这种特性,因此它使用在那些代码不是非常复杂的临界区(当然也不能太简单,否则使用原子操作或者其他适用简单场景的同步机制就OK了),如果临界区执行时间太长,那么不断在临界区门口“死等”的那些thread是多么的浪费CPU啊(当然,现代CPU的设计都会考虑同步原语的实现,例如ARM提供了WFE和SEV这样的类似指令,避免CPU进入busy loop的悲惨境地)
  • 可以在中断上下文执行。由于不睡眠,因此spin lock可以在中断上下文中适用。

reference