spinlock 종류, 차이점

 

Semapore, Mutex, Spin-Lock 차이점은 다음 글에 정리되어 있다.

 

Spin-Lock의 종류를 알아보자.

* spin_lock_irqsave()와 spin_lock_irq()
둘다 interrupt를 disable 시킨 이후에 spin lock을 획득하는 것은 동일하지만,
spin_lock_irqsave()는 CPU의 flag 레지스터를 보관했다가
spin_unlock_irqrestore()로 복구할 수 있다.

* spin_lock()과 spin_lock_irq()의 차이는
spin_lock()은 interrupt를 disable하지 않기 때문에
interrupt handler(top half)에서는 사용할 수 없다.
대신 이때는 spin_lock_irq()나 spin_lock_irqsave()를 사용해야 한다.

* spin_lock_bh()
같은 종류의 softirq는 여러 CPU에서 동시에 실행될 수 있기 때문에
([ksoftirqd/CPUn] 커널 쓰레드가 softirq의 실행을 담당한다)
softirq를 disable한 이후에 spin lock을 획득한다.
원래 이 함수의 이름은 spin_lock_softirq() 정도가 되어야 맞겠지만
예전 커널의 흔적이 아직 남아있다.

* 그리고, spin_lock()
spin lock으로 보호해야 할 데이터가
interrupt handler(top half)에서도, softirq handler에서도 접근하지 않고
단지 그 외의 일반적인 커널 코드에서만 접근한다면
interrupt disable, softirq disable 같은 overhead 없이 spin lock을 얻을때 사용한다.

조금 보태자면, spin_lock()이 interrupt handler / softirq에서 사용할 수 없는 건 아닙니다.

Thread context 실행 중 임의의 시점에 중지되고 softirq나 irq가 실행될 수 있고, softirq 실행중엔 임의의 시점에 irq가 실행될 수 있습니다. 만약 같은 락을 thread context와 irq에서 사용한다면 thread context에서 락을 잡고 있는 중에 irq가 실행되고 irq가 다시 같은 락을 획득하려고 할 수 있습니다. 데드락이죠. spin_lock_{irq[save]|bh}()는 이런 데드락을 피하기 위해서 사용합니다.

하나의 락이 irq에서도 사용되고 softirq나 thread context에서도 사용된다면, softirq/thread context에선 spin_lock_irq()를 irq 코드에선 spin_lock()을 사용합니다. 같은 코드를 irq와 다른 컨택스트에서 공유하거나해서 irq 상태를 알 수 없으면 spin_lock_irqsave()를 쓰면 됩니다. irq handler안에서는 spin_[un]lock_irq()를 쓰면 곤란하구요. 마찮가지로 softirq와 thread context가 공유하는 락은 thread context에서 spin_lock_bh()를 쓰면 됩니다. softirq disable은 카운팅이 되기 때문에 softirq안에서도 그냥 써도 되구요 (bh_save가 없는 이유).

정리하면 하나의 컨택스트 (irq, softirq, thread context 중) 안에서만 사용되는 락은 spin_lock()을 쓰면 되고, 하나 이상의 컨택스트에서 사용되는 락은 낮은 우선순위의 컨택스트에서 락을 잡을 때 락을 사용하는 컨택스트 중 가장 높은 우선순위를 막고 잡으면 됩니다.


spinlock은 일단 lock 변수를 선언하고, 항상 사용하기 전에는 초기화를 해주어야 하는데, 초기화 해주는 방법은 두가지가 있습니다. 정적으로 하는 방법과 동적으로 하는 방법입니다.

spinlock_t example_lock = SPIN_LOCK_UNLOCKED; <== 정적으로 하는 방법.
spinlock_t example_lock;
spin_lock_init( &example_lock ); <== 동적으로 하는 법.


그 이후에 사용방법은 공유하는 데이터 영역에서 lock 걸려고 하시는 구간에

spin_lock( &example_lock );
shared data area
spin_unlock( &example_lock );