답변:
스핀 잠금은 공유 리소스가 둘 이상의 프로세스에 의해 동시에 수정되지 않도록 보호하는 방법입니다. 리소스를 수정하려는 첫 번째 프로세스는 잠금을 "획득"하고 계속 진행하면서 리소스에 필요한 작업을 수행합니다. 잠금을 획득하려고 시도하는 다른 프로세스는 중지됩니다. 그들은 첫 번째 프로세스에 의해 잠금이 해제 될 때까지 기다리면서 "스핀 인 제스 (spin in place)"라고 불린다.
Linux 커널은 데이터를 특정 주변 장치로 전송할 때와 같은 여러 가지 용도로 스핀 잠금을 사용합니다. 대부분의 하드웨어 주변 장치는 여러 개의 동시 상태 업데이트를 처리하도록 설계되지 않았습니다. 서로 다른 두 가지 수정이 필요한 경우 하나는 다른 하나를 엄격하게 따라야하며 겹칠 수 없습니다. 스핀 잠금 장치는 필요한 보호 기능을 제공하여 한 번에 하나씩 수정이 이루어 지도록합니다.
스핀 잠금은 스레드의 CPU 코어가 다른 작업을 수행하지 못하게 차단하기 때문에 문제가됩니다. Linux 커널은 그 아래에서 실행되는 사용자 공간 프로그램에 멀티 태스킹 서비스를 제공하지만이 범용 멀티 태스킹 기능은 커널 코드로 확장되지 않습니다.
이러한 상황은 변화하고 있으며 대부분의 Linux 환경에서 발생했습니다. Linux 2.0을 통해 커널은 거의 순전히 단일 작업 프로그램이었습니다 .CPU가 커널 코드를 실행할 때마다 BKL이라는 모든 공유 리소스를 보호하는 단일 스핀 잠금 장치가 있었기 때문에 하나의 CPU 코어 만 사용되었습니다. ). Linux 2.2부터 BKL은 각각 더 집중된 리소스 클래스를 보호하는 여러 개의 독립적 인 잠금으로 천천히 분할됩니다. 오늘날 커널 2.6에서는 BKL이 여전히 존재하지만, 좀 더 세부적인 잠금으로 쉽게 이동할 수없는 오래된 코드에서만 사용됩니다. 이제 멀티 코어 박스에서 모든 CPU가 유용한 커널 코드를 실행하는 것이 가능합니다.
Linux 커널에는 일반적인 멀티 태스킹이 없기 때문에 BKL을 분해하는 유틸리티에는 제한이 있습니다. CPU 코어가 커널 스핀 잠금에서 회전을 차단하면 다시 해제하여 잠금이 해제 될 때까지 다른 작업을 수행 할 수 없습니다. 자물쇠가 풀릴 때까지 앉아서 회전합니다.
작업 부하가 모든 코어가 항상 단일 스핀 잠금을 기다리고있는 경우 스핀 잠금은 몬스터 16 코어 상자를 단일 코어 상자로 효과적으로 전환 할 수 있습니다. 이것은 리눅스 커널의 확장성에 대한 주요 한계입니다. CPU 코어를 2에서 4로 두 배로 늘리면 Linux 상자의 속도가 거의 두 배가되지만 16에서 32로 두 배로 늘리면 대부분의 워크로드에서는 그렇지 않습니다.
스핀 잠금은 프로세스가 지속적으로 폴링하여 잠금을 제거하는 것입니다. 프로세스가 (일반적으로) 불필요하게 사이클을 소비하기 때문에 나쁜 것으로 간주됩니다. Linux에 국한된 것이 아니라 일반적인 프로그래밍 패턴입니다. 그리고 그것은 일반적으로 나쁜 습관으로 여겨지지만, 실제로는 올바른 해결책입니다. 스케줄러 사용 비용이 스핀 록이 지속될 것으로 예상되는 몇주기의 비용보다 높은 경우 (CPU주기 기준)가 있습니다.
스핀 락의 예 :
#!/bin/sh
#wait for some program to clear a lock before doing stuff
while [ -f /var/run/example.lock ]; do
sleep 1
done
#do stuff
스핀 잠금을 피하는 방법이 종종 있습니다. 이 특정 예제에는 inotifywait 라는 Linux 도구가 있습니다 (일반적으로 기본적으로 설치되지는 않음). C로 작성된 경우 Linux가 제공 하는 inotify API를 사용하면됩니다.
inotifywait를 사용하는 동일한 예는 스핀 잠금없이 동일한 작업을 수행하는 방법을 보여줍니다.
#/bin/sh
inotifywait -e delete_self /var/run/example.lock
#do stuff
스레드가 잠금을 얻으려고 시도하면 실패하면 세 가지 일이 발생하고 시도하고 차단 할 수 있으며 시도하고 계속할 수 있으며 일부 이벤트가 발생하면 OS가 잠 들어 깨우도록 절전 모드로 전환 할 수 있습니다.
이제 시도와 계속은 시도와 차단보다 훨씬 적은 시간을 사용합니다. "시도하고 계속"하는 데 시간이 걸리고 "시도하고 차단"하는 데는 백 시간이 걸린다고 가정 해 봅시다.
이제 평균적으로 스레드가 잠금을 유지하는 데 4 단위 시간이 걸린다고 가정합니다. 100 대를 기다리는 것은 낭비입니다. 대신 "try and continue"루프를 작성하십시오. 네 번째 시도에서는 일반적으로 잠금을 획득합니다. 이것은 스핀 잠금입니다. 스레드가 잠금을 얻을 때까지 제자리에서 계속 회전하기 때문에 호출됩니다.
추가 된 안전 조치는 루프가 실행되는 횟수를 제한하는 것입니다. 따라서이 예제에서는 for 루프를 6 번 실행합니다. 실패하면 "시도하고 차단"합니다.
스레드가 항상 200 단위의 잠금을 유지한다는 것을 알고 있다면 각 시도에 대한 컴퓨터 시간을 낭비하고 계속합니다.
결국 스핀 락은 매우 효율적이거나 낭비가 될 수 있습니다. 잠금을 유지하는 "일반적인"시간이 "시도 및 차단"하는 데 걸리는 시간보다 높으면 낭비입니다. 잠금을 유지하는 일반적인 시간이 '시도 및 차단'시간보다 훨씬 작은 경우 효율적입니다.
추신 : 스레드에서 읽을 책은 여전히 찾을 수 있다면 "A Thread Primer"입니다.
잠금 동기화 두 가지 이상의 작업 (프로세스, 스레드)를위한 방법입니다. 특히, 두 작업 모두 한 번에 하나의 작업에서만 사용할 수있는 리소스에 대한 간헐적 인 액세스가 필요한 경우 작업이 동시에 리소스를 사용하지 않도록 정렬하는 방법입니다. 리소스에 액세스하려면 작업이 다음 단계를 수행해야합니다.
take the lock
use the resource
release the lock
다른 작업이 이미 수행 된 경우 잠금을 수행 할 수 없습니다. (물리적 토큰 오브젝트로 잠금을 생각하십시오. 오브젝트가 드로어에 있거나 누군가 손에 가지고 있습니다. 오브젝트를 보유한 사람 만 자원에 액세스 할 수 있습니다.) 따라서 "잠금 해제"는 실제로 "잠시까지 기다리십시오"를 의미합니다. 다른 사람은 자물쇠를 가지고 있지 않다.”
높은 수준의 관점에서, 잠금을 구현하는 두 가지 주요 방법 인 스핀 락과 조건이 있습니다. 함께 스핀 락 아무도 때까지 그냥 "회전"(즉, 루프에서 아무것도하지 않고) 잠금 수단을 가지고, 다른 잠금이 있습니다. 조건이있는 상태에서 작업이 잠금을 시도하지만 다른 작업이 잠금으로 인해 차단 된 경우 새로 온 사람이 대기 대기열에 들어갑니다. 릴리스 작업은 잠금을 사용할 수 있음을 대기중인 작업에 알립니다.
(이 설명은 원자성에 대해 아무 말도하지 않았기 때문에 잠금을 구현하기에 충분하지 않습니다. 그러나 원자 성은 중요하지 않습니다.)
스핀 락은 분명히 낭비입니다. 대기 작업은 잠금 해제 여부를 계속 확인합니다. 왜 그리고 언제 사용됩니까? 스핀 록은 잠금이 유지되지 않는 경우에 매우 저렴한 경우가 많습니다. 이것은 잠금이 잡힐 가능성이 적을 때 매력적입니다. 또한 스핀 록은 잠금을 얻는 데 시간이 오래 걸리지 않을 경우에만 실행 가능합니다. 따라서 스핀 록은 매우 짧은 시간 동안 유지되는 상황에서 사용되는 경향이 있으므로 대부분의 시도는 첫 번째 시도에서 성공할 것으로 예상되며 대기가 필요한 시도는 오래 기다리지 않습니다.
Linux 장치 드라이버 , 5 장 에서 Linux 커널의 스핀 록 및 기타 동시성 메커니즘에 대해 잘 설명 합니다.
synchronized
스핀 락에 의해 구현 될지 의심합니다 . synchronized
블록이 매우 오랫동안 실행될 수 있습니다. synchronized
큰 동기화 기본 요소를 빌드하기위한 기본 요소가 아닌 특정 경우에 잠금을 사용하기 쉽게 만드는 언어 구성입니다.
spinlock은 스케줄러를 비활성화하여 작동하며 잠금을 획득 한 특정 코어에서 인터럽트 (irqsave 변형)를 가능하게합니다. 스핀 록이 유지되는 동안 스레드 만 실행할 수 있도록 스케줄링을 비활성화한다는 점에서 뮤텍스와 다릅니다. 뮤텍스는 다른 우선 순위가 높은 다른 스레드를 예약 할 수 있지만 동시에 보호 된 섹션을 실행할 수는 없습니다. spinlocks는 멀티 태스킹을 비활성화하므로 spinlock을 수행 한 다음 뮤텍스를 얻으려고하는 다른 코드를 호출 할 수 없습니다. spinlock 섹션 내부의 코드는 잠자기 상태가되어서는 안됩니다 (코드는 일반적으로 잠긴 뮤텍스 또는 빈 세마포어가 발견되면 잠자기 상태입니다).
뮤텍스와의 또 다른 차이점은 스레드가 일반적으로 뮤텍스에 대해 대기하므로 아래의 뮤텍스에 큐가 있다는 것입니다. spinlock은 필요한 경우에도 다른 스레드가 실행되지 않도록합니다. 따라서 파일 외부에서 함수를 호출 할 때 잠자기 상태가 아닌지 확실하지 않은 스핀 록을 잡아서는 안됩니다.
스핀 락을 인터럽트와 공유하려면 irqsave 변형을 사용해야합니다. 이것은 스케줄러를 비활성화 할뿐만 아니라 인터럽트도 비활성화합니다. 말이 되나요? Spinlock은 다른 것이 실행되지 않도록하여 작동합니다. 인터럽트를 실행하지 않으려면 인터럽트를 비활성화하고 중요 섹션으로 안전하게 진행하십시오.
멀티 코어 머신에서 스핀 락은 실제로 잠금을 해제하는 다른 코어를 기다리면서 회전합니다. 이 회전은 단일 코어 머신에서는 발생할 수 없기 때문에 멀티 코어 머신에서만 발생합니다 (스핀 록을 잡고 진행하거나 잠금이 해제 될 때까지 실행되지 않습니다).
Spinlock은 의미가있는 곳에서 낭비되지 않습니다. 매우 작은 중요 섹션의 경우 중요한 작업을 완료하는 데 걸리는 몇 마이크로 초 동안 단순히 스케줄러를 일시 중단하는 것과 비교하여 뮤텍스 작업 대기열을 할당하는 것은 낭비입니다. 잠자거나 IO 작업 (잠자기 가능)에서 잠금을 유지 해야하는 경우 뮤텍스를 사용하십시오. 확실히 스핀 락을 잠그지 말고 인터럽트 내부에서 해제하려고 시도하십시오. 이것이 작동하는 동안, 그것은 arduino 쓰레기 같은 것입니다 (flagnotset); 이 경우 세마포어를 사용하십시오.
메모리 트랜잭션 블록에 대한 간단한 상호 배제가 필요할 때 스핀 락을 잡으십시오. 여러 스레드가 뮤텍스 잠금 직전에 중지되도록하고 뮤텍스가 해제 될 때와 동일한 스레드에서 잠금 및 해제 할 때 계속하도록 우선 순위가 높은 스레드를 선택하려면 뮤텍스를 잡습니다. 세마포어를 한 스레드 또는 인터럽트에 게시하고 다른 스레드로 가져 오려는 경우 세마포어를 가져옵니다. 상호 배제를 보장하는 약간 다른 세 가지 방법이며 약간 다른 목적으로 사용됩니다.