Gidhub BE Developer

스핀락(Spin Lock)

2018-10-07
goodGid
OS

락에 대한 기본 지식

  • 락킹은 내가 배타적으로 데이터를 갱신/사용 하고 싶을 때 사용된다.

  • 예를 들어보자.
    워터파크에는 k개의 구명조끼가 있다.
    실제 사용자가 k명까지 동시에 사용 할 수 있는 자원이 있는데 N명이 왔다.
    이 경우 k명을 제외한 사람은 밖에서 기다려야 한다.

  • 또다른 예를 들어보자.
    화장실에 일을 보러 가야 하는데 변기는 혼자서만 사용해야 한다.
    그러면 누군가가 사용하고 있다면 다른 사람은 사용할 수 가 없다.
    이 때 문을 잠금다면(=Locking) 다른 사람이 들어가고 싶어도 못들어가게(=Waiting) 된다.
    안에 있던 사람이 일을 보고 나오면 문을 열고(=Unlocking) 나와야 다른 사람이 들어갈 수 있게 된다.

  • 이런 부분을 임계 영역(Critical section)이라고 한다.

  • 근데 이게 OS와 만나면서 몇 가지 락 구현 개체로 나뉘게 된다.

  • 이를 이해하기 위해서 프로세스쓰레드 구현에 대해 알아야한다.

  • 기본적으로 현재 일반 유저가 사용하는 대부분의 OS는 선점형 시분할 운영체제이다.

  • 간단하게 말해서 크롬을 띄우고 메모장을 같이 띄워도
    두개다 동작하는 것은 각각 일정 시간동안만 CPU를 선점하여 사용하기 때문이다.

  • 이 때 할당되는 시간 단위타임 슬라이스(Time Slice)라고 한다.

  • 일단 여기서 각각의 프로그램은 프로세스라는 단위로 움직인다.
    그리고 프로세스 안에서 동작하는 실행 단위인 쓰레드가 존재한다.

  • 크롬으로 스포츠 영상을 보면 크롬이 프로세스가 되고
    크롬안에서 영상을 처리해주는 부분과 네트워크로 영상을 받아오는 부분이 각각 쓰레드가 된다.

  • 그런데 프로세스 or 쓰레드가 Wait라는 상태가 걸리는 때가 있다.

  • 이걸 이해하기 위해선 H/W를 살펴볼 필요가 있다.

  • H/W는 CPU보다 느리다.

  • 그렇기 때문에 디스크에서 데이터를 읽는 작업처럼 느린 동작을 처리하기 위해서 작업이 잠깐 중단될 필요가 생긴다.

  • 그러면 CPU는 디스크에서 데이터를 읽어 달라는 명령을 요청하고 다른 작업을 계속한다. (=Task Switch)


Sleep 가능한 락

  • 락 중에는 이런식으로 자원에 접근 하는 락이 있다.

  • 앞에서 살펴본 워터파크에는 k명만 입장이 가능하다.

  • k명에 들어간다면 그냥 입장 가능하겠지만 k명에 해당되지 않는다면 대기자는 그냥 졸면서 기다린다.

  • 그리고 자원을 다 사용한 사람들이 나가면 졸고 있는 사람들을 깨워서 들여보낸다.

  • 이게 바로 세마포어(Semaphore) 개념이다.

  • 세마포어는 보통 자원에 관계된 락이다.

  • 그래서 락을 걸때 특정 수만큼의 카운트를 갖고 빼주는 형식으로 처리된다.

  • 이런 락을 슬립(Sleep)가능한 락이라고 한다.

  • 또한 뮤텍스(Mutex)라는 락이 있는데 이는 Mutualy Exclusive라는 락이다.

  • 결국 이것도 세마포어긴 한데, 특별히 카운트가 1인 락으로 봐도 무방하다.

  • 이와 반대로 스핀락은 자지 않고 게속해서 버틴다고 볼 수 있다.


스핀락이란 무엇인가?

  • 스핀락은 이름이 뜻하는대로
    만약 다른 쓰레드가 lock을 소유하고 있다면 그 lock이 반환될 때까지 계속 확인하며 기다리는 것이다.

  • 조금만 기다리면 바로 쓸 수 있는데 굳이 문맥 교환으로 부하를 줄 필요가 있나? 라는 컨셉으로 개발됐다.

  • 일반적으로 프로세스를 컨트롤 하는 입장에서
    Sleep을 시키고 태스크를 스위칭 하는 일련의 작업은 그다지 작은 연산이 아니다.

  • Sleep된 프로세스를 깨우기 위해선 일단 데이터를 메모리에다가 쓰고 스위칭 작업을 하고 모든 작업이 다 끝났을때 가능하다.

  • 그렇기 때문에 아주 작은 작업에 대해서는 세마포어 or 뮤텍스를 사용하지 않고 스핀락을 사용하는 게 효율적일 수 있다.

예를들어 어떤 숫자를 단순히 +1 해주는데 사용될 락이 있다 생각해보자.
이 경우는 거창하게 문맥 교환을 하면서 구현할 필요가 없다.
잠깐 밖에서 값을 검사해보고 내가 사용가능하면 바로 처리 하도록 하는게 효율적이다.
이 개념이 "스핀락" 개념이다.

그냥 단순히 적절한 시간동안 외부에서 for나 while로 루프를 돌면서 락을 검사하고 처리하게 되는 것이다.
이때 임계 영역은 굉장히 작거나 아주 빨리 처리가 가능할 경우 이런 락을 쓰게 된다. 

대신에 이런 락을 쓰고 있을 때 Sleep을 하게 되면 다른 쪽(쓰레드든, 프로세스든)에서는 
CPU는 사용하고 있는데(=루프가 돌고 있기 때문에) 락을 얻지 못하는 사실상의 데드락 상태에 빠지게 된다.

그래서 스핀락은 이런 특성 때문에 "바쁜 대기 상태(Busy Waiting)"라고도 불린다.
  • 임계 구역(Critical Section)에 진입이 불가능할 때 문맥 교환을 하지 않고 루프를 돌면서 재시도를 한다.

  • 스핀락은 운영 체제스케줄링 지원받지 않기 때문에 해당 쓰레드에 대한 문맥 교환이 일어나지 않는다.

  • 따라서 스핀락은 임계 구역에 짧은 시간 안에 진입할 수 있는 경우에 문맥 교환제거할 수 있어 효율적이다.

  • 하지만 스핀락오랜 시간소요한다면 다른 쓰레드를 실행하지 못하고 대기하게 되며
    이 경우 비효율적인 결과를 가져온다.

  • 만약 오래 걸리는 작업에 대해 스핀락을 사용한다면
    많은 쓰레드들이 락을 잡으려는 시도를 계속 하게되며
    이렇게 되면 CPU 점유율이 엄청나게 올라가게 될 것 이다.
    하지만 실질적인 작업은 수행하지 않는다.

스핀락이 갖는 특성

  1. Lock을 얻을 수 없다면, 계속해서 Lock을 확인하며 얻을 때까지 기다린다.
    이른바 바쁘게 기다리는 Busy Wating이다.

  2. 바쁘게 기다린다는 것은 무한 루프를 돌면서 최대한 다른 쓰레드에게 CPU를 양보하지 않는 것이다.

  3. Lock이 곧 사용가능해질 경우 컨택스트 스위치를 줄여 CPU의 부담을 덜어준다.
    하지만, 만약 어떤 쓰레드가 Lock을 오랫동안 유지한다면 오히려 CPU 시간을 많이 소모할 가능성이 있다.

  4. 단일 CPU or 단일 코어인 경우에는 유용하지 않다.
    그 이유는 만약 다른 쓰레드가 Lock을 가지고 있고
    그 쓰레드가 Lock을 풀어 주려면 싱글 CPU 시스템에서는 어차피 컨택스트 스위치가 일어나야 하기 때문이다.
    주의할 점은 스핀락을 잘못 사용하면 CPU 사용률 100%를 만드는 상황이 발생된다.
    스핀락은 기본적으로 무한 루프를 돌며 lock을 기다리므로
    하나의 쓰레드가 lock을 오랫동안 가지고 있다면,
    다른 Blocking된 쓰레드는 Busy Waiting을 하므로 CPU를 쓸데없이 낭비하게 된다.

  5. 스핀락을 잘 사용하면 문맥 교환을 줄여 효율을 높일 수 있다.


뮤텍스 락 vs 스핀락

  • 락을 얻을 수 없을때 쓰레드 슬립 모드로 빠지지 않고 반복문을 계속 돌며 락을 얻으려는 시도를 한다.

  • 락이 해제될 때 별도의 쓰레드 문맥교환(Context Switching)에 대한 오버헤드 없이 임계 구역에 접근을 한다.

  • 뮤텍스 락의 경우 락이 해제되더라도 운영체제에 의해 쓰레드가 다시 깨어나야하지만
    스핀락은 락을 얻을때까지 계속해서 CPU 자원을 점유하므로 CPU를 비효율적으로 낭비할 위험이 있다.

  • 그러므로 임계구역 진입을 위한 대기 시간이 짧을 때 사용하는게 바람직하다.


참고


Comments

Content