kernel - semaphore 그리고 spinlock

숲사람·2022년 7월 20일
2

Linux Kernel

목록 보기
9/10

Semaphore

  • task A 가 이미 lock을 잡고 있는 상태에서 task B가 lock을 잡으려고 할때, 세마포어는 B를 wait queue에 넣고 sleep상태로 만듦

  • 인터럽트 컨텍스트에서는 세마포어를 사용할 수 없다. 컨택스트에서는 태스크 스케줄링이 일어나면 안되기 때문. (ISR 이 sleep이 일어나면 안됨)

  • spinlock 보다 긴 시간을 기다려야하는 상황에서 자주 사용됨.

  • counter 수 만큼의 프로세스가 동시에 자원에 접근 가능. 화장실 칸이 4개 이고 키가 4개이면 4명까지는 대기없이 바로 사용. 뮤텍스는 키가 1개.

  • 세마포어가 mutual exclusion을 위해 사용될때 Mutex라고 부른다. (리눅스에서 사용되는 거의모든 세마포어는 뮤텍스로 사용됨) Mutex 는 상태가 0, 1 두 개 뿐인 binary Semaphore.

  • Mutex는 동기화 대상이 오직 하나뿐일 때, Semaphore는 동기화 대상이 하나 이상일 때 사용.

static declare_semaphore_generic(name, count);

count를 1로 하면 뮤텍스가 됨. static declare_mutex(name) 아얘 뮤텍스용 인터페이스도 있음

  • 질문: 인터럽트 핸들러에서는 mutex를 사용하면 안된다. 그런데 만약 핸들러를 request_threaded_irq()로 등록했다면? mutex로 보호해도 되나? 어차피 인터럽트 핸들러가 스케줄링되기 때문이다.
    어쩌면 사이즈가 큰 threaded irq handler는 spinlock이아니라 무조건 mutex로 보호해야할지도? spinlock으로 보호하면 lock을 기다리는 다른 스레스의 cpu를 그만큼 사용할 수 없기 때문이다.

Spinlock

Spinlock의 등장 배경

mutex는 쓰레드 A가 lock을 잡고 있는경우 쓰레드 B는 sleep된다. Critical section이 매우 짧은 경우, 퍼포먼스 낭비가 있다. B가 sleep을 하기 위한 작업을 하는 시간에 lock이 풀릴 수 있기 때문.

커널의 많은 함수들은 태스크가 sleep상태로 들어가면 안 되는 부분이 많이 있다. 커널 코드가 인터럽트 컨텍스트에서 수행되고 있을 때가 바로 그 때이다. 이때 sleep 상태로 들어가면 안 되는 이유는 현재의 태스크의 커널 스택에 nesting된 인터럽트의 복귀 주소와 문맥(CPU register set)이 들어가 있기 때문이다. (인터럽트 Nesting: 인터럽트 문맥에서 또 다른 인터럽트 실행되는것)

이 때 spinlock사용. 한 CPU가 특정 플래그의 상태를 보며 루프를 돌며 busy-wating 하고 있는것. 장점은 세마포어보다 가볍다.물론 기다리고 있는 동안의 CPU 사용을 유용한 곳에 쓰지 못한다는 단점이 있긴 하지만 짧은 시간 동안(짧은 코드 수행영역)의 lock이라면 spinlock을 사용하는 것이 효율적이다.

기다리고 있는 동안의 CPU 사용을 유용한 곳에 쓰지 못한다는 단점의 이유는 무엇일까?
lock을 얻기전에 preempt_diable() 을 수행하기 때문이다. 다른 프로세스가 해당 cpu 를 선점해서 사용(preempt)할 수가 없다. spin_lock을 요청한 프로세스는 해당 cpu를 독점하며 기다리고 있다.

As should be obvious from the above, using spin locks can gum up the whole
machine so spin locks should just be used for **very short periods of time**
and you should never do anything that might cause a reschedule whilst holding a
lock.

The case with mutex_lock is totally different - only threads attempting to
access the lock are affected and if a thread hits a locked mutex then a
reschedule will occur. For this reason mutex_locks cannot be used in interrupt
(or other atomic) contexts.

(https://stackoverflow.com/questions/6555358/linux-kernel-preemption-during-spin-lock-and-mutex-lock)

spin_lock_irq()

Thread A가 spinlock을 잡고 일을 하고 있다. 그 도중 인터럽트가 발생했다. 그 인터럽트 핸들러가 동일한 spinlock을 잡으려고 하는 경우. deadlock이 발생한다. 어떻게 처리하나? 이 문제를 해결하기 위해 spin_lock_irq() 사용. spinlock을 잡으면서 인터럽트를 disable 시켜준다.

spin_lock_irq_save()

하지만 함수 call path가 깊어지고 nesting된 spin_lock_irq가 사용되는 경우 spin_lock_irq()사용이 어렵다. 인터럽트는 몇번을 disable 했던지 한번만 enable하면 인터럽트가 다시 복구된다. 그러면 spin_lock_irq()를 수행하려고 할때, 만약 현재 인터럽트가 enable인지 disable상태인지 모르면, spin_unlock()을 할때 enable을 할지말지 알 수가 없다.

이 때, spin_lock_irq_save() 사용. irq를 disable함과 동시에 이전에 irq가 enable/disable인지 여부를 변수에 저장해 놓는다. (저장할 변수를 인자로 받음)

다음의 경우에 이함수를 사용하는게 좋다.

  • spinlock을 이용하는 시점에서 interrup enable을 확신할 수 없는 경우
  • call path가 깊은경우
  • spin_lock_irq를 nesting해서 사용할 경우

spin_lock_irq_save() > spin_lock_irq() > spin_lock() 순으로 퍼포먼스 오버헤드가 크다.

참고

https://www.linux.co.kr/home2/board/bbs/board.php?bo_table=lecture&wr_id=1642&sca=1&sca2=3://www.linux.co.kr/home2/board/bbs/board.php?bo_table=lecture&wr_id=1642&sca=1&sca2=32
http://egloos.zum.com/nimhaplz/v/5301468 (spin_lock_irq 참고)

profile
기록 & 정리 아카이브용

0개의 댓글