티끌모아 태산

Chapter 7 : Kernel Synchronization 본문

카테고리 없음

Chapter 7 : Kernel Synchronization

goldpig 2023. 12. 25. 22:45
728x90

Kernel Synchronization

  • 커널 프로그래밍은 본질적으로 "공유 메모리 모델"을 사용합니다.
    • '크리티컬 섹션'(또는 크리티컬 리전)이 존재합니다. 이는 완전히 그리고 "독점적으로" 실행되어야 하는 코드 섹션을 의미합니다. 즉, 여러 프로세스 혹은 스레드가 동시에 접근할 수 없는 영역입니다.
    • '경쟁 상태'(race condition)는 피해야 합니다.
  • 유니프로세서(단일 프로세서) 시대(예전)
    • 비교적 단순했습니다. 동시 데이터 접근은 인터럽트가 발생했을 때만 가능했거나 커널 코드가 명시적으로 다른 작업을 재스케줄링했을 때만 일어났습니다.
  • 멀티프로세서(SMP, Symmetric Multiprocessing) 지원(2.0~ 버전부터)
    • 복잡합니다. 서로 다른 프로세서에서 실행되는 커널 코드가 정확히 동시에 공유 데이터에 접근할 수 있습니다.
  • 선점형 커널(preemptive kernel) 지원(2.6~ 버전부터)
    • 더 복잡합니다. 스케줄러가 커널 코드를 어느 시점에서든 선점할 수 있고 다른 작업을 재스케줄링할 수 있습니다.

Kernel Control Path(KCP)

  • Kernel Control Path는 커널 모드에서 실행되는 명령어의 순서로서, 커널 요청을 처리하기 위해 사용됩니다.
  • 커널 요청에는 시스템 호출(system call), 예외(exception), 인터럽트 등이 포함됩니다.
  • 이러한 요청에 대응하는 핸들러들이 바로 Kernel Control Path입니다.

Interleaving Kernel Control Paths

  • CPU는 KCP를 중첩시켜 실행합니다.
    • 컨텍스트 스위치는 schedule() 함수가 호출될 때 발생합니다.
    • CPU가 인터럽트를 허용한 상태에서 커널 제어 경로를 실행하는 도중 인터럽트가 발생하면 중첩 인터럽트가 일어납니다.
  • 커널 제어 경로의 중첩은 중요합니다.
    • 멀티프로세싱을 구현하기 위해서입니다.
    • PIC(Programmable Interrupt Controller)와 장치 컨트롤러의 처리량을 향상시키기 위해서입니다.
  • 문제점
    • 커널 제어 경로를 중첩시킬 때, 커널 데이터 구조에 대한 동시 접근이 고려되어야 합니다.
    • 경쟁 상태(race condition)를 방지하기 위해 커널 동기화가 필요합니다.
  • 경쟁 상태
    • 두 개 이상의 중첩된 커널 제어 경로가 어떻게 중첩되는지에 따라 어떤 계산의 결과가 달라질 때 발생합니다.

커널 내부에서 여러 작업이 동시에 수행될 때 발생할 수 있는 복잡한 상황들과 이를 관리하기 위해 필요한 동기화 메커니즘에 대한 중요성을 강조합니다. 경쟁 상태는 여러 프로세스나 스레드가 동시에 데이터에 접근할 때 일관성 없는 결과를 초래할 수 있으며, 이를 방지하기 위한 적절한 동기화가 필수적입니다.

커널 제어 경로(kernel control path)를 중첩하는 것은 멀티프로세싱을 구현 + PIC and 장치 컨트롤러의 처리량을 증가시키기위해 중요합니다. 그러나 커널 제어 경로의 중첩은 커널 데이터 구조에 대한 동시 접근으로 인해서 race condition이 발생할 수 있습니다. 그래서 커널 동기화가 이를 막기위해 필요합니다. Kernel synchronization is required to prevent race condition. 즉, 커널 동기화는 불안전한 동시성을 예방하고 race condition이 발생하지 않도록 보장한다.

  • 리눅스 커널에서는 경쟁 상태(race condition)가 발생할 수 있는 충분한 가능성이 있습니다.
  • 커널은 인터럽트 안전(interrupt-safe), SMP 안전(SMP-safe), 선점 안전(preemption-safe)이어야 합니다.
    • Interrupt-safe: safe from concurrent access from interrupt handler
    • SMP-safe: safe from concurrency on multiprocessors; the kernel code can run simultaneously on multiple CPUs
    • Preemption-safe: safe from concurrency with kerenl preemption

⭐️Kernel Synchronization Primitives

  • 커널 동기화는 안전하지 않은 동시성이 방지되고 경쟁 상태가 발생하지 않도록 보장하는 것입니다.
  • 동기화 기본 요소(Primitive)는 커널 제어 경로가 중첩될 수 있게 하면서 공유 데이터에 대한 경쟁 상태를 피하기 위해 커널 데이터 구조를 보호하는 메커니즘입니다.
  • 리눅스 커널에서 사용되는 커널 동기화 기본 요소는 다음과 같습니다:
    • 원자적 연산(Atomic operations), 장벽(Barrier)
    • ⭐️잠금(Locking)
      • Non-blocking lock: 스핀락(Spin lock), 읽기-쓰기 락(RWLock)
      • blocking lock: 세마포어(Semaphore), 읽기-쓰기 세마포어(RWSemaphore), 뮤텍스(Mutex), 완료(Completion), 빅 커널 락(BKL)
    • 읽기 복사 갱신(RCU, Read-Copy-Update)
    • 인터럽트 비활성화, 선점 비활성화

⭐️Locking

Kernel locking (more general mechanism): When a kernel control path must access a shared data structure or enter a critical region, it must acquire a 'lock' for it.

  • Linux offers two kinds of locking
    • Spin locks: busy-waiting (i.e., non-blocking) lock; useful on multiprocessors systems
    • Kernel semaphores: blocking lock; Widely used both on uniprocessor and multiprocessor systems

Spin Locks

  • 스핀락은 다중 프로세서 시스템(Symmetric Multiprocessing, SMP)에서 사용되는 잠금 메커니즘입니다.
    • 작동 원리: 공유 변수를 설정하여 잠금을 획득하고, 변수가 해제될 때까지 바쁜 대기(busy-wait) 루프에서 "스핀"합니다.
    • 구현: 아키텍처에 종속적이며 어셈블리 언어로 구현됩니다.
    • 특성: 잠금은 커널의 선점을 비활성화합니다.
    • 인터럽트 컨텍스트에서의 사용: 커널은 인터럽트 컨텍스트에서 공유되는 데이터 구조체에 대한 특수한 스핀락 API를 제공합니다. 프로세스 컨텍스트에서는, 커널 선점이 비활성화된 상태에서 스핀락을 들고 있는 동안 프로세스가 대기 상태에 들어가서는 안 됩니다.
  • 단일 프로세서 시스템에서는 스핀락이 "무용지물"입니다. 왜냐하면 대기 중인 프로세스가 계속 실행을 이어가고 잠금을 해제할 다른 프로세스가 없기 때문입니다. 이 경우 잠금은 컴파일 과정에서 제거될 수 있으며, 단지 커널 선점을 비활성화하고 활성화하는 마커로 작동할 수 있습니다(커널 선점이 활성화되어 있는 경우).

스핀락은 특히 멀티프로세서 환경에서 중요한 동기화 도구로, 다른 프로세서가 잠금을 해제할 때까지 대기하는 CPU 사이클을 사용합니다. 이는 컨텍스트 스위치 오버헤드를 피하기 위해 적은 지연시간 동안 데이터에 대한 접근을 기다리는 데 유용하지만, 오랜 기간 동안 잠금을 유지할 경우 시스템의 성능에 부정적인 영향을 미칠 수 있습니다.

Read/Write Spin Locks

  • Allow multiple readers, but only one writer

Spin Locks in Interrupt Handlers

  • 스핀락은 SMP(대칭형 멀티프로세싱) 시스템의 인터럽트 핸들러에서 사용될 수 있습니다. 스핀락은 블록하지 않는 잠금 메커니즘이기 때문에 인터럽트 핸들러에서 적합합니다.
  • 잠금을 획득하기 전에 "로컬 인터럽트를 비활성화"하는 것이 필요합니다. 그렇지 않으면 중첩된 인터럽트 핸들러가 잠금을 보유한 커널 제어 경로를 중단시킬 수 있습니다. 인터럽트 핸들러가 스핀을 시작하지만, 중단된 커널 제어 경로는 재스케줄링되지 않고 잠금을 해제할 수 없습니다.
  • 로컬 인터럽트만 비활성화되어야 합니다. 다른 CPU에서 발생하는 인터럽트는 현재 CPU의 커널 제어 경로를 방해하지 않으므로, 이 제어 경로는 계속 실행될 수 있고 결국 잠금을 해제할 수 있습니다.

스핀락을 사용할 때는 인터럽트 핸들러 내에서 데드락을 방지하기 위해 세심한 주의가 필요합니다. 로컬 인터럽트를 비활성화함으로써 현재 CPU에서 실행 중인 코드가 중단되지 않도록 보장하여, 잠금을 안전하게 관리할 수 있습니다.

Kernel Semaphores

  • 커널 세마포어는 "sleeping locks"입니다.
    • 프로세스가 이미 점유 중인 세마포어를 획득하려고 시도할 때, 해당 프로세스는 대기 큐에 들어가며 이 상태를 '블록' 상태라고 합니다.
    • 세마포어를 점유하고 있는 프로세스가 세마포어를 해제하면, 세마포어의 대기 큐에 있는 프로세스 중 하나가 깨어납니다.
  • 커널 세마포어는 잠재적으로 작업을 차단할 수 있습니다:
    • 커널 세마포어는 잠들 수 있는(sleep) 함수에 의해서만 획득될 수 있습니다.
    • 인터럽트 컨텍스트에서 사용할 수 없습니다. 즉, 인터럽트 핸들러와 연기 가능한(deferrable) 함수(softirq, tasklet)에서는 사용할 수 없습니다.
    • 짧은 기간 동안만 점유되는 잠금에 최적화되지 않았습니다(오버헤드가 있기 때문에).
  • 세마포어는 여러 홀더를 허용할 수 있습니다.
    • 그러나 커널에서 사용되는 대부분의 세마포어는 이진 세마포어(또는 뮤텍스)입니다.

커널 세마포어는 잠금을 획득할 수 없을 때 프로세스를 잠자게 하여 커널 리소스에 대한 접근을 동기화하는 데 사용됩니다. 이것은 특히 공유 리소스에 대한 동시 접근을 관리할 때 중요한 역할을 합니다. 그러나 인터럽트 핸들러와 같이 대기할 수 없는 컨텍스트에서는 사용할 수 없으며, 이 경우 다른 동기화 메커니즘이 사용됩니다.

Read/Wite Semaphores

  • Enfoce mutual exclusion only for writers, not readers

Mutexes

  • 뮤텍스는 소유권 제한이 있는 binary 세마포어입니다.
    • 뮤텍스는 락을 건 스레드(또는 프로세스)에 의해서만 해제될 수 있습니다(대기 연산 후의 게시 연산).
    • 이진 세마포어와 유사하지만 인터페이스가 더 간단하고, 성능이 더 효율적이며, 사용에 있어서 추가적인 제약 조건이 있습니다.
    • 뮤텍스는 binary 세마포어보다 더 엄격하고 사용 범위가 좁습니다:
      • 한 번에 한 스레드만 뮤텍스를 소유할 수 있습니다.
      • 뮤텍스를 잠근 스레드가 반드시 해제해야 합니다.
      • 재귀적으로 락을 걸고 풀 수 없습니다.
      • 뮤텍스를 소유한 상태에서 스레드가 종료될 수 없습니다.
      • 인터럽트 컨텍스트에서 뮤텍스를 획득할 수 없습니다.
      • 뮤텍스는 공식 API를 통해서만 관리할 수 있습니다.

뮤텍스는 리소스에 대한 독점적인 액세스를 제공하고, 리소스를 사용하고자 하는 다른 스레드들을 대기 상태로 만들어, 동시에 같은 리소스에 대한 접근을 시도하는 여러 스레드 간의 경쟁을 방지하는 데 사용됩니다. 이는 커널 프로그래밍에서 중요한 동기화 메커니즘 중 하나입니다

Completions

  • 완료는 세마포어의 특별한 경우로, 멀티프로세서 시스템에서 up()과 down()이 다른 CPU에서 동시에 실행될 때 발생할 수 있는 미묘한 경쟁 상태를 해결합니다.
  • 커널에서 태스크 간의 동기화를 쉽게 하기 위해 도입되었으며, 리눅스 2.6 버전에서 소개되었습니다.
  • 작동 방식은 다음과 같습니다:
    • 프로세스들은 특정 작업을 완료하는 프로세스 P를 기다립니다.
    • P가 작업을 마치면, 대기 중인 프로세스들을 깨웁니다.
    • 완료 변수는 DECLARE_COMPLETION(cmp);를 사용하여 정적으로 선언됩니다.
    • 이 변수를 기다리고자 하는 프로세스들은 wait_for_completion(&cmp);를 호출합니다. 이는 down() 연산과 유사합니다.
    • 프로세스 P는 complete(&cmp);를 호출함으로써 작업의 완료를 신호합니다. 이는 up() 연산과 유사하며, 모든 대기 중인 프로세스들을 동시에 깨웁니다.

완료는 커널 내부의 동기화에 사용되는 기법 중 하나로, 특정 작업이 완료될 때까지 다른 프로세스들을 대기하게 만들고, 그 작업이 완료되면 대기하고 있는 모든 프로세스들을 일시에 깨우는 역할을 합니다. 이는 커널 작업의 순서와 완료를 효율적으로 관리하는 데 중요한 도구입니다.

더보기

⭐️kernel의 blocking lock들을 나열하고 설명하시오.

Semaphore: 프로세스가 이미 점유 중인 세마포어를 획득하려고 시도할 때, 해당 프로세스는 대기 큐에 들어가며 이 상태를 '블록' 상태라고 합니다. 세마포어를 점유하고 있는 프로세스가 세마포어를 해제하면, 세마포어의 대기 큐에 있는 프로세스 중 하나가 깨어나 세마포어를 획득하게 됩니다. 그리고 인터럽트 컨텍스트에서 사용할 수 없습니다. 따라서 프로세스 컨텍스트에서만 사용가능 합니다. 그리고 짧은 기간 동안만 점유되는 잠금에 최적화되지 않고(오버헤드가 있기 때문에) 세마포어는 여러 홀더를 허용할 수 있습니다. 게다가 세마포어를 보유하고 있어도 커널 선점을 비활성화하지 않기 때문에 세마포어를 보유한 프로세스는 선점될 수 있습니다.

 

RWSemaphore: Enfoce mutual exclusion only for writers, not readers(이 세마포어는 쓰기 작업에 대해서만 상호 배제를 실행하지만, 동시에 여러 읽기 작업을 허용합니다.)

 

Mutex: 뮤텍스는 binary semaphore와 유사하지만 단순한 인터페이스와 더 효율적인 성능을 제공하며, 뮤텍스를 잠근 스레드만이 뮤텍스를 해제할 수 있습니다. 그리고 재귀적인 잠금과 해제 작업이 허용되지 않으며 뮤텍스는 한 번에 하나의 스레드만이 소유할 수 있습니다. 그리고 인터럽트 컨텍스트에서는 사용할 수 없습니다.

 

Completion: 완료는 커널 내부의 동기화에 사용되는 기법 중 하나로,특정 작업이 완료될 때까지 다른 프로세스들을 대기하게 만들고, 그 작업이 완료되면 대기하고 있는 모든 프로세스들을 일시에 깨우는 역할을 합니다. 이는 커널 작업의 순서와 완료를 효율적으로 관리하는 데 중요한 도구입니다.

 

BKL: BKL은 Linux 커널에서 한 번에 하나의 CPU만이 커널 모드에서 실행될 수 있도록 보장하는 전역 커널 락입니다.

Avoiding Deadlocks on Semaphores

  • (Deadlock)데드락: 프로그램이 두 개 이상의 세마포어를 사용할 때 발생합니다. 서로 다른 두 경로가 서로의 세마포어 해제를 기다리는 상황에서 발생할 수 있습니다. 리눅스는 세마포어 요청에 따른 데드락 문제가 거의 없는데, 이는 각 커널 제어 경로가 대개 한 번에 하나의 세마포어만을 요구하기 때문입니다.
  • 데드락 피하기:
    • 락 순서(Lock ordering): 중첩된 락은 항상 같은 순서로 획득되어야 합니다. 즉, 두 개 이상의 세마포어를 요청할 때는 특정 순서를 지켜야 합니다.

이는 여러 리소스에 대한 접근을 제어하는 프로그램에서 중요한 원칙입니다. 올바른 락 순서를 유지함으로써, 두 개 이상의 프로세스 또는 스레드가 서로를 기다리는 무한 대기 상태, 즉 데드락에 빠지는 것을 방지할 수 있습니다.

⭐️Semaphores vs Spin Locks

  • 프로세스는 세마포어를 획득하려 할 때 잠들 수 있습니다. 세마포어는 프로세스 컨텍스트(system call, workqueue 등)에서만 사용될 수 있으며, 인터럽트 컨텍스트(인터럽트 핸들러, 소프트 인터럽트, 태스클릿)에서는 사용할 수 없습니다.
  • 프로세스는 세마포어가 사용 가능해질 때까지 잠듭니다. 세마포어는 오랫동안 유지되는 락에 적합합니다(스핀 락에서 CPU 사이클을 낭비할 수 있음).
  • 세마포어는 매우 짧은 시간 동안 유지되는 락에는 적합하지 않습니다. 대기 큐를 유지하는 오버헤드가 락을 유지하는 총 시간보다 클 수 있습니다.
  • 락과 달리, 세마포어를 보유하고 있어도 커널 선점을 비활성화하지 않습니다. 즉, 세마포어를 보유한 프로세스는 선점될 수 있습니다. 이는 스케줄링 지연에 영향을 주지 않습니다.
  • 카운팅 세마포어(Counting Semaphores)는 동시에 여러 개의 락을 보유할 수 있게 합니다. 반면에 스핀 락은 한 번에 하나의 프로세스만 락을 보유할 수 있게 합니다.

세마포어와 스핀 락은 커널 동기화의 두 가지 기본적인 메커니즘으로, 사용되는 컨텍스트와 락이 유지되는 시간에 따라 적합한 도구를 선택하는 것이 중요합니다. 세마포어는 대기하는 동안 프로세스가 잠들게 하여 CPU 자원을 낭비하지 않도록 하지만, 스핀 락은 CPU가 락을 얻을 때까지 능동적으로 기다리게 합니다.

BKL: The Big Kernel Lock

BKL은 Linux 커널에서 한 번에 하나의 CPU만이 커널 모드에서 실행될 수 있도록 보장하는 전역 커널 락입니다. 이는 SMP(대칭형 멀티프로세싱) 환경에서 커널의 동시 접근을 제어하는 데 사용되었습니다.

  • lock_kernel()과 unlock_kernel() 함수는 BKL을 획득하고 해제하는 데 사용됩니다.
  • Linux 2.6.10 버전까지는 스핀 락으로 구현되었으며, 2.6.11 버전부터는 kernel_sem 세마포어로 구현되었습니다.
  • BKL은 단순한 세마포어보다 약간 더 복잡한 구조를 가지고 있으며, 세마포어와는 다른 몇 가지 흥미로운 특성을 가지고 있습니다.

  그러나 BKL사용이 권장되지 않습니다. 커널이 점점 더 복잡해지고 유연해짐에 따라 단일 스핀 락에 의존하지 않게 되었기 때문입니다. 신규 코드에서는 BKL을 사용하는 락을 도입해서는 안 되며, Linux 2.6 커널에서는 주로 VFS(가상 파일 시스템)와 여러 파일 시스템에 관련된 오래된 코드를 보호하는 데 BKL이 여전히 사용되고 있습니다.

Preemption Disabling

  When a KCP involves spin locks: if a spin lock is held, the kernel is not preemptive (i.e., preemption is disabled). Even when a KCP does not. involve spin locks: some situations need to disable kernel preemption

Local Interrupt Disabling

  • Disabling all interrupt in local CPU: 이는 커널 문장의 연속이 중요한 섹션으로 작동하도록 하여 인터럽트 핸들러가 현재 코드를 선점하지 못하게 하는 데 사용됩니다. 인터럽트를 비활성화하면 커널 선점도 비활성화됩니다. 커널은 인터럽트가 비활성화된 동안에는 절대로 블로킹 작업을 실행해서는 안 됩니다. 이러한 작업은 시스템을 동결시킬 수 있기 때문입니다.
  • Multiprocessor systems: 멀티프로세서 시스템에서는 로컬 인터럽트 비활성화가 다른 CPU에 영향을 주지 않으므로, 다른 프로세서로부터의 동시 접근으로부터 보호하는 역할을 하지 않습니다. 대신, 스핀 락과 같은 잠금 장치를 로컬 인터럽트 비활성화와 함께 사용하여 현재 중요 코드를 보호함과 동시에 다른 프로세서로부터의 보호를 구현합니다.
  • Interrupt can execute in a nested fashion
더보기

a. single processor 환경의 interrupt context에서 lock을 걸 경우, 왜 local interrupt disable을 하는지 쓰시오.

  단일 프로세서 환경의 인터럽트 컨텍스트에서 잠금(lock)을 사용할 때, 로컬 인터럽트를 비활성화(local interrupt disabling)하는 것은 중요합니다. 만약 로컬 인터럽트를 비활성화 하지 않으면, 이는 인터럽트 핸들러가 현재 실행 중인 코드를 중단할 수 있기 때문입니다. 하지만 로컬 인터럽트를 비활성화하면, 중요한 코드 구간에서 다른 인터럽트에 의해 방해받지 않도록 보장합니다. 이는 데드락(deadlock)이나 데이터 손상을 방지하는 데 중요합니다.

b. multiple processor 환경의 interrupt context에서 lock을 걸 경우에는, global 대신 local interrupt를disable 하는 이유를 쓰시오.

  멀티프로세서 시스템에서는 로컬 인터럽트 비활성화가 다른 CPU에 영향을 주지 않으므로, 다른 프로세서로부터의 동시 접근으로부터 보호하는 역할을 하지 않습니다. 대신, 스핀 락과 같은 잠금 장치를 로컬 인터럽트 비활성화와 함께 사용하여 현재 중요 코드를 보호함과 동시에 다른 프로세서로부터의 보호를 구현합니다.

Which Synchronization Primitive?

  • 예외(exception)에 의해서만 접근되는 데이터 구조를 보호하기 위해서는 세마포어(semaphore)를 사용하는 것이 좋습니다. 이는 시스템 호출(service call) 루틴 내에서 주로 접근되며, 예외에 의해 접근되는 데이터 구조는 하나 이상의 프로세스에 할당될 수 있는 자원을 나타냅니다. 세마포어는 유니프로세서와 멀티프로세서 환경 모두에 사용됩니다.
  • 인터럽트에 의해서만 접근되는 데이터 구조를 보호하는 경우, 각 인터럽트 핸들러는 자체적으로 직렬화되어 있으므로, 단일 유형의 인터럽트에 대해 추가적인 동기화는 필요하지 않습니다. 다중 인터럽트 핸들러에서의 접근이 있는 경우, 한 인터럽트 핸들러가 다른 인터럽트 핸들러를 중단할 수 있습니다(예: 중첩된 인터럽트), 그리고 다른 인터럽트 핸들러는 멀티프로세서 환경에서 동시에 실행될 수 있습니다.
  • In uniprocessor: disabling local interrupt is the only way
  • In multiprocessor: disabling local interrupt + spin locks

이러한 동기화 메커니즘은 시스템의 데이터 무결성을 보장하고 경쟁 상태나 데이터 손상을 방지하기 위해 필수적입니다.

  • 연기 가능한(deferrable) 함수에 의해서만 접근되는 데이터 구조를 보호하기 위한 동기화 원시 메커니즘
    • 유니프로세서(uniprocessor) 시스템에서는 연기 가능한 함수가 CPU에서 순차적으로(serialized) 실행되기 때문에 경쟁 상태(race condition)가 존재하지 않습니다.
    • 멀티프로세서(multiprocessor) 시스템에서는 여러 연기 가능한 함수가 동시에 실행될 수 있으므로 경쟁 상태가 발생할 수 있습니다.

더보기

a. single processor 환경에서 deferrable function에 대한 동기화를 하려면 어떻게 해야 하는가?

  단일 프로세서 환경에서는 deferrable function들이 CPU에서 순차적으로 실행되므로, 일반적으로 race condition이 발생하지 않습니다. 따라서 특별한 동기화 메커니즘을 사용할 필요가 없습니다

b. multiple processor 환경에서도 동일한가?

  멀티 프로세서(multiple processor) 환경에서는 동기화 방식이 다릅니다. 멀티 프로세서 환경에서는 여러 deferrable function이 동시에 실행될 수 있으므로 race condition이 발생할 가능성이 있습니다. 이러한 환경에서는 spin lock을 사용하여 동기화를 수행합니다.

요약하면, 단일 프로세서 환경에서는 deferrable function에 대한 동기화가 필요하지 않지만, 멀티 프로세서 환경에서는 spin lock을 사용하여 동기화가 필요합니다.

728x90