Spinlock
title: 2025-08-26 author: 강병호 date: 2025-08-26 category: TIL/강병호/2025/08 layout: post —
프로세스와 스레드가 Lock
을 획득하기 위해 기다리는 대기 방식은 다음 두 가지 방식으로 크게 나눌 수 있다.
Blocking
:Lock
을 획득할 수 없는 스레드를 운영체제 스케줄러에 의해 대기 큐로의 전환, 이 과정으로 인해 상태변환된 스레드는 CPU를 사용하지 않게 되지만Context Switching
을 수행하여 오버헤드가 발생(스레드 상태 저장, 복원)Busy Waiting
: 스레드가 대기 상태로 전환되지 않고 실행 상태를 유지하며 반복하여 락의 해제 유무를 확인하는 방식으로 CPU를 계속 점유하지만 오버헤드를 피할 수 있다.
Spin Lock의 경우 락을 사용할 수 있을 때까지 프로세스가 회전
(반복)하여 Context Switching에 대한 비용이 클 때 Context Switching 을 생략하여 프로세스를 처리할 수 있다는 장점이 존재한다. 실제로 최근의 CPU들은 멀티코어로 여러 프로세스가 처리될 수 있다. 그 과정에서 Contet Switching의 비용보다 놀고 있는 CPU를 그대로 두지 않고 Spin Lock을 통해 Lock
을 점유하지 않은 스레드는 회전하다가 락이 해제될 때 즉시 작업을 이어받아 처리할 수 있다.
그러나 이는 물론 현대적인 멀티코어에서의 작업이지 현재 코딩과정에서 싱글 코어를 사용한다면 Busy Waiting
하는 스레드가 하나뿐인 CPU를 독점하는 과정에서 Lock
을 해제해야하는 프로세스/스레드가 독점하지 못하는 문제가 발생할 수 있다.
물론 멀티코어 상황에서도 CPU 자원의 낭비와 Starvation
의 단점이 존재한다. Lock
이 예상보다 길게 유지되는 경우에는 스핀하는 프로세스/스레드는 아무런 작업을 하지 않고 CPU를 사용하기에 시스템의 전반적인 성능에 문제가 발생한다. 또한, 공정성을 지닌 스핀락 구현이 없이는 여러 프로세스/스레드가 Lock
획득을 기다리는 경우인 높은 경합 상태에서 무한정 대기하는 프로세스/스레드가 존재할 수 있다.
System Call를 통해 사용자의 유저 레벨 코드에서 커널 공간에서 접근가능한 처리들을 수행하게 됩니다. 이는 유저 프로세스에서의 Lock
을 잘못 다루는 상황을 방지하고
이는 동기화 과정에서 Lock
의 과정에서도 이루어지게 되는데 이는 ContextSwitch 로 인한 커널 작업 이전에 현재 유저 스레드에서의 레지스터 값 등을 저장 및 복귀 과정의 오버헤드가 발생하게 된다.
하지만 Lock
을 얻기 위한 중점적인 목표는 공유 자원에 동시에 접근하여 발생하는 동시성의 문제를 해결하기 위해 등장하였는데 경쟁상대 즉, 공유 자원을 사용할 다른 프로세스/스레드가 없는 상황에서 이러한 Lock
연산을 계속하는 것은 시스템 콜의 남발로 불필요한 오버헤드 발생이다. 이는 곧 시스템 자원의 심각한 낭비로 이어지게 되는 문제점이 존재한다.
이러한 단점을 보완하기위해 리눅스에서의 Futex는 낙관적 락의 설계를 기반으로 하여 경쟁이 없는 경우에는 시스템 콜이 없이 사용자 공간에서 처리되도록 하고 경쟁이 있는 경우에는 커널 공간에서 처리되도록 한다.
이는 다음 2단계의 메커니즘이 존재한다.
-
사용자 공간 : 사용자 공간에서의 공유 메모리에 위치한 integer값을 두어 락을 획득하려는 실행단위가 시스템 콜 호출 전 해당 정수 값에 대한 연산을 수행한다. 이를 성공하면 커널의 개입 없이 유저 스페이스에서 임계 영역으로 접근한다.
-
커널 공간 : 유저 스페이스의 공유 메모리에 위치한 integer값 획득에 실패한 경우 시스템 콜을 통해 커널공간에서 처리한다.