본문 바로가기

Linux Kernel

[kernel/locking] spinlock (1) - spinlock 사용법

반응형

앞선 글에서 lock이란 무엇이고,  lock의 필요성에 대해서 알아보았다. 이번 글에서는 가장 기본적인 spinlock을 리눅스 커널에서 어떻게 구현했는지 알아볼 것이다. 우선 관련된 개념을 몇 가지 살펴보자. 참고로, 이 문서에서 다루는 spinlock은 커널에서 사용하는 spinlock에 대해 다루고 있다. userspace에서의 spinlock은 pthread spinlock을 사용해야 한다.

자료구조

spinlock은 include/linux/spinlock_types.h에 spinlock_t로 정의되어있다. 한 번 살펴보자.

/* include/linux/spinlock_types.h */
typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

/* 디버깅과 관련된 부분 */
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;

#ifdef ~ #endif 부분은 config에 따라서, 디버깅 관련 기능을 활성화한 경우에 추가되는 코드이다. 그 부분을 제외하면 spinlock_t는 raw_spinlock_t만을 갖고 있다.

/* include/linux/spinlock_types.h */

typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;

/* 디버깅과 관련된 부분 */
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

raw_spinlock_t는 arch_spinlock_t를 갖고 있으며 이는 아키텍처에 종속적인 부분이다. 

선점 비활성화

linux kernel에서 preempt_disable은 선점을 비활성화하는 함수이다. 프로세스 선점이 가능하면 lock을 들고있는 도중에 다른 프로세스가 실행되어 critical section에 동시에 여러 프로세스가 접근할 수 있게 된다. 정확히 말하면 프로세스 A가 spinlock을 획득하고, 휴면 상태로 전환한 뒤 프로세스 B가 실행되면 A로 다시 돌아왔을 때 데이터의 무결성을 보장할 수 없다. 따라서 spinlock을 획득하기 전에 반드시 선점을 비활성화해야한다.

인터럽트 비활성화

spinlock으로 보호하는 자원이 인터럽트 핸들러에서도 사용되는 경우, 인터럽트를 비활성화해야만 한다. 선점을 비활성화해도 여전히 인터럽트는 발생할 수 있다. spinlock을 획득한 상태에서 인터럽트가 발생하고, 인터럽트 핸들러에서 해당 lock이 필요한 경우에는 데드락이 발생하게 된다. 이를 위해 커널 단에서 spin_lock_irqsave, spin_lock_irqrestore 함수를 통해 인터럽트를 비활성화하고, spinlock을 획득하는 함수를 지원한다. 만약 인터럽트 핸들러에서 절대로 해당 spinlock을 사용하지 않는단걸 알고 있다면 spin_lock, spin_unlock을 사용해도 된다. 이 경우엔 인터럽트가 비활성화되지 않는다.

softirq 비활성화

spinlock으로 보호하는 자원을 softirq에서 사용하는 경우에는 spin_lock_bh / spin_unlock_bh 함수를 사용할 수 있다.

사용법

spinlock을 사용한다면 다음과 같이 사용할 수 있다.

#include <linux/spinlock.h>

/* 정적으로 spinlock 정의 */
DEFINE_SPINLOCK(lock);

spin_lock(&lock);

/* Critical Section! */

spin_unlock(&lock);

spinlock을 얻으면서 인터럽트도 비활성화시키려면 spin_lock_irq / spin_unlock_irq를 사용할 수 있다.

#include <linux/spinlock.h>

/* 정적으로 spinlock 정의 */
DEFINE_SPINLOCK(lock);

spin_lock_irq(&lock);

/* Critical Section! */

spin_unlock_irq(&lock);

그런데, 인터럽트를 비활성화 하려면 현재 인터럽트가 활성화되어있다는 걸 알아야한다. 따라서 현재 인터럽트 활성화 여부에 관계 없이, 활성화된 경우에만 인터럽트를 비활성화하려면 spin_lock_irqsave / spin_unlock_irqrestore 를 사용하면 된다. 

#include <linux/spinlock.h>

/* 정적으로 spinlock 정의 */
DEFINE_SPINLOCK(lock);
unsigned long flags; // 현재 상태에 대한 플래그

spin_lock_irqsave(&lock, flags);

/* Critical Section! */

spin_unlock_irqrestore(&lock, flags);

spinlock의 장단점

장점

인터럽트 컨텍스트에서도 사용할 수 있다. 인터럽트 컨텍스트에선 휴면이 불가능하므로 뮤텍스, 세마포어 같은 휴면 가능한 락은 사용할 수 없다.

단점

spinlock을 들고있는 동안은 선점, 인터럽트를 비활성화하므로 오랫동안 잡고있으면 안된다. 단, 휴면을 하고, 다시 돌아오는 시간 (context switching 2번)이 락을 잡고있는 시간보다 길다면 spinlock을 사용하는게 좋다.

참고자료

 

spinlocks.rst - Documentation/locking/spinlocks.rst - Linux source code (v5.11.10) - Bootlin

=============== Locking lessons =============== Lesson 1: Spin locks ==================== The most basic primitive for locking is spinlock:: static DEFINE_SPINLOCK(xxx_lock); unsigned long flags; spin_lock_irqsave(&xxx_lock, flags); ... critical section he

elixir.bootlin.com

 

using spinlocks in user-space application

I am trying to create a kernel-module which has an structure & i want to use the same structure in user-space application and this application works with the given module. the main problem is ...

stackoverflow.com

 

리눅스 커널 심층 분석

리눅스 커널의 핵심을 간결하면서도 심도있게 다루고 있다. 일반적인 운영체제에 대한 이해를 넘어, 여타 유닉스 시스템과 다른 리눅스만의 특징적인 부분에 대한 설계, 구현, 인터페이스 등을

www.aladin.co.kr

 

Spinlock

In software engineering, a spinlock is a lock which causes a thread trying to acquire it to simply wait in a loop ("spin") while repeatedly checking if the lock is available. Since the thread remains active but is not performing a useful task, the use of s

en.wikipedia.org

 

Introduction to spinlocks · Linux Inside

 

0xax.gitbooks.io