제가 이 글을 쓰는 목적은
첫째, 제가 이해한 바에 대해 옳은 부분에 대해서는 선배님들의 동의를 구하고 틀린 부분에 대해서는 지적을 부탁드리기 위함입니다.
둘째, 동기화에 대해 이해 하고자 노력하는 열정적인 개발자들과 지식을 공유하기 위함입니다.
1. [UP system] 프로세스 컨텍스트간의 동기화
semaphore, preempt_disable
semaphore는 CS를 공유하는 프로세스 컨텍스트들을 휴면 시켜서 동기화를 달성합니다.
preempt_disable은 CS를 공유하는 프로세스 컨텍스트들은 물론이고 다른 모든 프로세스 컨텍스트들 또한 선점을 금지시켜
동기화를 달성합니다. CS 구간이 긴경우 semaphore를 짧은 경우 preempt_disable을 사용하는게 효율적이라 생각합니다.
local_irq_disable을 사용 할 수도 있겠지만 인터럽트 컨텍스트가 없기 때문에 불필요합니다.(불가능이 아님.)
프로세스 컨텍스트는 휴면 가능 하므로 spin_lock은 사용 불가능 합니다. (데드락 발생)
2. [UP system] 프로세스 컨텍스트와 인터럽트 컨텍스트간의 동기화
local_irq_disable
local_irq_disable은 모든 인터럽트를 금지 시키기 때문에(심지어 타이머 인터럽트에 기반한 스케줄러까지)
CS를 공유하는 프로세스 컨텍스트와 인터럽트 컨텍스트간 동기화를 달성합니다.
인터럽트는 스케줄링 대상이 아니므로 semaphore, preempt_disable로는 프로세스 컨텍스트와 동기화를 달성 할 수 없습니다.
또한 semaphore는 휴면 가능 하므로 인터럽트 컨텍스트에서는 아예 사용이 불가능합니다.
인터럽트 컨텍스트는 항상 프로세스 컨텍스트에 우선 하므로 spin_lock은 사용 불가능합니다. (데드락 발생)
3. [UP system] 인터럽트 컨텍스트간의 동기화
local_irq_disable
인터럽트 컨텍스트는 스케줄링 대상이 아니므로 local_irq_disable로 동기화를 달성합니다.
spin_lock를 사용 할 수도 있겠지만 프로세서가 하나이기 때문에 불필요합니다.(불가능이 아님.)
심지어 인터럽트는 우선순위에 따라 중첩이 가능하므로 spin_lock은 데드락의 원인이 됩니다.
그래도 의미 없는 spin_lock을 사용하고자 한다면 반드시 local_irq_disable을 먼저 호출하고 락을 잡아야 합니다.
semaphore, preempt_disable는 인터럽트 컨텍스트간의 동기화에 아무런 역할을 하지 못하며
semaphore는 휴면 가능 하므로 인터럽트 컨텍스트에서는 아예 사용이 불가능합니다.
4. [SMP system] 프로세스 컨텍스트간의 동기화
spin_lock
프로세스 컨텍스트가 하나의 프로세서에서 유사동시성을 가짐과 동시에 다수의 프로세서에서 진정한 동시성을 가지게 됩니다.
CS가 다수의 프로세서에 의해 공유 되므로 반드시 spin_lock을 사용해야 다수의 프로세서간에 동기화가 달성 됩니다.
semaphore, preempt_disable
이 두 녀석은 오직 유사 동시성에서의 동기화를 위해 사용한다고 생각합니다.
예를 들어 A, B 두개의 프로세서가 있습니다. 그리고 CS를 공유하는 1, 2, 3, 4 네개의 태스크가 있습니다.
어떠한 원인인지는 몰라도(로드 밸런싱을 포함한 다수의 경우가 있을 수 있겠죠.) 태스크 1, 2는 A 프로세서에서
실행 되고 있고 태스크 3, 4은 B 프로세서에서 실행 된다고 가정 합니다. semaphore는 전역으로 하나가 생성
되겠지만 세마포어의 대기큐는 프로세서별로 하나씩 생기게 될거라 생각합니다. 그래서 프로세서 A에서는 세마포어의
대기큐 A에 의해 태스크 1, 2간의 동기화를 달성하고 프로세서 B에서는 세마포어의 대기큐 B에 의해 태스크 3, 4간의
동기화를 달성 하지 않을까 합니다. 물론 태스크 (1, 2)와 (3, 4)간의 동기화는 spin_lock으로 달성 될것입니다.
위와 같은 정황을 고려해서 판단할 때 semaphore, preempt_disable은 유사 동시성에서의 동기화를 위해서만 사용 될거라는
결론을 개인적으로 내려봅니다. SMP에서 프로세스 컨텍스트간의 완벽한 동기화를 위해서는다음과 같은 코드 모양이
될거라고 봅니다.
1) 2)
preempt_disable -->유사 동시성 동기화 semaphore_wait -->유사 동시성 동기화
spin_lock -->진정한 동시성 동기화 spin_lock -->진정한 동시성 동기화
CS CS
spin_unlock spin_unlock
preempt_enable semaphore_post
이렇게 코드가 작성 된다면 SMP에서 UP로 갈 경우 spin_lock만 걷어내면 될것입니다.
5. [SMP system] 프로세스 컨텍스트와 인터럽트 컨텍스트간의 동기화
local_irq_disable + spin_lock
local_irq_disable은 로컬 프로세서의 모든 인터럽트를 금지 하므로 유사 동시성 에서의 프로세스 컨텍스트간의
동기화를 달성함과 동시에 프로세스 컨텍스트와 인터럽트 컨텍스트간의 동기화를 달성 할 수 있습니다.
하지만 인터럽트는 다른 프로세서에의해 핸들링 될 수 있으므로 반드시 spin_lock을 더해줘야 합니다.
이 조합을 하나의 함수로 처리한 것이 spin_lock_irqsave(), spin_lock_irqstore()입니다.
UP에서와 마찬가지로 유사동시성에서의 데드락을 방지하기위해 반드시 인터럽트를 먼저 금지하고 락을 잡아야 합니다.
물론 preempt_disable을 사용해 프로세스 컨텍스트간의 동기화를 해결 할 수 있겠지만
local_irq_disable이 이를 해결 해주므로 사용 할 필요가 없겠습니다. 또한 semaphore는 휴면 가능 하므로
인터럽트 컨텍스트에서는 아예 사용이 불가능합니다.
preempt_disable -->프로세스 컨텍스트간의 동기화 (유사 동시성) => 불필요함.
local_irq_disable -->프로세스 컨텍스트간의 동기화 + 프로세스 컨텍스트와 인터럽트 컨텍스트간의 동기화 (유사 동시성)
spin_lock -->진정한 동시성 동기화
CS
spin_unlock
local_irq_enable
preempt_enable
6. [SMP system] 인터럽트 컨텍스트간의 동기화
local_irq_disable + spin_lock
local_irq_disable은 유사 동시성에서의 동기화를 위해 사용하는 것이며 (단일 프로세서에서의 인터럽트 컨텍스트간의 동기화)
진정한 동시성에서의 동기화는 spin_lock을 통해 달성 됩니다. 또한 유사 동시성에서의 데드락을 방지하기 위해 반드시
인터럽트를 먼저 금지하고 락을 잡아야 합니다.
semaphore, preempt_disable는 인터럽트 컨텍스트간의 동기화에 아무런 역할을 하지 못하며
semaphore는 휴면 가능 하므로 인터럽트 컨텍스트에서는 아예 사용이 불가능합니다.
쓰다보니 내용이 상당히 길어진것 같습니다.
선배님들의 많은 조언 부탁 드립니다.
댓글 13
-
백창우
2011.04.25 23:16
너무 많으니깐 조금씩 할께요.
1번
local_irq_disable을 사용 할 수도 있겠지만 인터럽트 컨텍스트가 없기 때문에 불필요합니다.(불가능이 아님.)
- 짧은 구간에서 사용할 수 있습니다. interrupt latency가 늘어나서 문제지만요.
spin_lock를 사용 할 수도 있겠지만 프로세서가 하나이기 때문에 불필요합니다.(불가능이 아님.)
- 일반적인 spin_lock에서 불가능 합니다.2번
인터럽트는 스케줄링 대상이 아니므로 semaphore, preempt_disable로는 프로세스 컨텍스트와 동기화를 달성 할 수 없습니다.
- 스케줄링 대상이 아니라 interrupt를 막을수 있는것은 interrupt disable 밖에 없기 때문입니다. ISR이 우선순위가 더 높다는 말도 하는데, 가장 정확한 대답은 interrupt를 막을수 있는 방법은 interrupt disable 밖에 없다는 것입니다.
spin_lock를 사용 할 수도 있겠지만 프로세서가 하나이기 때문에 불필요합니다.(불가능이 아님.)
- 위에서 설명했고요.
오늘은 여기까지...
마음 내키면 다시 지적 하겠습니다. 마음 안내키면 안해줄 수도 있고요.
-
홍문화
2011.04.26 00:04
먼저 답글에 진심으로 감사드립니다.
어떻게 해야 마음 내키게 해드릴 수 있을까요? ㅋ ^^;
유닉스 리눅스 프로그래밍 필수 유틸리티 개정판도 읽었습니다.
같은팀 동료가 구매를 했더라구요. 구판은 직접 구매해서 소유하고 있습니다.ㅋ
Kernel스터디 8차(ARM 중)멤버이구요.
스터디를 할 수 있는 루트를 만들어 주셔서 진심으로 감사하고 있습니다. ㅋ ^^; -
홍문화
2011.04.26 09:18
넵! 알겠습니다. ^^;
-
백창우
2011.04.26 01:26
ㅎㅎ 그냥 내키면 하겠습니다.
답변이 일이 되는게 싫어서요. ^^
그냥 즐겁게 답변 달고 싶으면 달고, 달기 싫으면 안달려고 합니다.
위 글은 너무 길어서, 답변 달기가 부담스럽더군요. ㅋㅋ
-
이종인
2011.04.26 00:22
아 읽다가 궁금증이 생겼는데 위에서 말씀 하신 프로세스 context가 정확이 어떤 상태 인가요? 제가 생각해볼 때는 프로세스 code영역 실행중 시스템 콜로 커널 code영역을 실행 중인 것 같은데요?.. (유저단에서 preempt disable() 같은건 없지 않나요? )
-
이종인
2011.04.26 00:31
5번에서 preempt_disable 은 필요 없고 local_irq_disable()을 통해서 프로세스 context간에 동기화가 이루어진다고 했는데요,
제 생각으로는 preempt_disable은 커널 재진입이 있을 수 있으므로 필요할 것 같습니다...
혹시 local_irq_disable()을 함수를 사용하면 preempt_disable효과도 있는 건가요?;; 어떤식으로 프로세스 context 간에 동기화가 이루어지는건지...잘모르겠습니다.
-
홍문화
2011.04.26 09:31
두개의 질문에 모두 해당하는 답변이 될거 같습니다.
유저 선점, 커널 선점이 어떻게 이루어지냐 하는 문제인데.
유저 선점은 need_resched 변수(정확히는 비트)가 1이 될 때 이루어집니다.
need_resched가 1이 되는 경우는 현재 실행 되고 있는 태스크의 타임슬라이스가 0이 될 때, 우선 순위가 높은
태스크가 wait 상태에서 깨어날 때 입니다.
커널 선점은 need_resched가 1이 되고 preempt_count가 0이 되어야 이루어집니다.
preempt_count가 0이라는 것은 락을 잡고 있지 않다는 의미 입니다.
local_irq_disable()은 시스템 타이머 인터럽트를 금지 시킬테고 이는 곧 이를 호출한 프로세서의 runqueue에 있는
태스크의 타임 슬라이스가 줄어들지 않게 할 것이고 타임 슬라이스가 줄어들지 않으니 need_resched가 계속 0이
될것 입니다. 즉, need_resched 계속 0이 될테니 schedule()이 호출되지 않을테고 선점이 발생하지 않을 것입니다.
모두 우리 교재에 나오는 내용입니다. ^^;
-
홍문화
2011.04.27 20:35
백창우님의 지적을 바탕으로 고민한 끝에 UP든 SMP든 유사동시성에서의 동기화에서는 spin_lock을
사용 할 수 없다는 결론을 내렸습니다. 글 수정 하였습니다.
-
백창우
2011.04.27 21:41
그렇군요. 정말 처음 듣는 용어네요. ^^
-
홍문화
2011.04.27 20:54
저희 교재에서 단일 프로세서에서 컨텍스트 스위칭으로 인해 발생하는 동기화 문제를 유사 동시성 문제라 부르고 있습니다.
-
백창우
2011.04.27 20:45
"유사동시성에서의 동기화"가 뭔지를 모르겠네요.
뭔지는 모르지만 어쨌던 그런뜻으로 이야기한것 같지는 않은데요.
-
백창우
2011.04.28 00:55
simple하게 생각하셨으면 합니다. ^^
spinlock은 그렇게 이해하기 어려운 synchronization primitive가 아닙니다.
지금 사용하시는 책에 문제가 있지는 않은지 생각해봅니다.
-
홍문화
2011.04.28 09:47
요즘 저희 스터디에서 이슈가 되고 있는 문제이고 아직까지 확실하게 정리가 되지 않아
여러모로 고심을 하고있는데 소스를 분석하고 실행 해봐야 정말 확실하게 정리가 될 것
같습니다. 이제 시작인데 하나 해결하면 다시 난관에 봉착하게 되는 것 같습니다. ^^;
한바퀴 돌로 코드 분석까지 마치면 웃으면서 오늘을 회상 할 날이 오리라 믿습니다. ㅋㅋ
.
preempt_disable은 특정 프로세서에서의 선점을 금지 합니다. 이렇게 하여 각 프로세서별 유사 동시성을 달성 하게
될 것입니다. 예를 들어 프로세서 A의 태스크 1, 2든 프로세서 B의 태스크 3, 4든 CS 진입 시에 preempt_disable을 호출하면
프로세서 A의 태스크 1, 2간의 유사 동시성에 의한 동기화가 달성 되고 프로세서 B의 태스크 3, 4간의 유사 동시성에 의한
동기화도 달성 될것입니다. 하지만 이것만으로는 프로세서간의 진정한 동시성에 의한 동기화는 달성 할 수 없으므로
태스크 (1, 2)와 (3, 4)간의 동기화는 spin_lock으로 달성 될것입니다.