Step by Step 커널 프로그래밍 강좌④
작성자 정보
- 웹관리자 작성
- 작성일
컨텐츠 정보
- 20,242 조회
- 0 추천
- 목록
본문
Step by Step 커널 프로그래밍 강좌④
커널의동기화에관하여
동기화 문제는 커널 프로그래밍에 있어 가장 까다롭고 어려운 부분이다. 처음부터 제대로 이 문제를 고려해 설계하지 않은 경우 가장 발생하기 쉽지만 정작 해결점을 찾기는 가장 어려운 문제다. 이번 호에서는 동기화 문제를 겪지 않기 위해 커널에 사용되는 동기화 기법 중 대표적인 몇 가지를 살펴보기로 한다.
글 _ 김민찬 KLDP 멤버, 전문 프로그래머
연재 순서
① 커널 프로그래밍 환경 구축하기와 특징
② 모듈 구현하기
③ 리눅스 커널의 메모리 관리
④ 커널의 동기화에 관하여
⑤ 커널의 시간관리 및 지연 함수에 대하여
⑥ 파일시스템과 proc file system 사용하기
까다롭고 어려운 동기화 문제
리눅스 커널이 2.0으로 발전하면서 SMP(SymmtricMultiprocessing)을 지원하기 시작했고 이로 인하여 커널의 공유 자원에 대한 Lock은 복잡해지기 시작했다. SMP를 지원하기 시작하면서 어떤 커널 코드이건 2개 이상의 CPU에서 수행될 수 있기 때문에 스택에 할당된 자원을 제외하고는 동시에 하나 이상의 CPU가 접근할 수 있게 됐으며 그로 인해 프로그래머는 여러 가지 고려해야 할 것이 많아졌다. 반면, 여전히 하나의 CPU만을 갖는 환경의 개발자들은 SMP
를 고려하지 않아도 되기 때문에 사실 동기화에 대해서는 큰 관심이 가지지 않아 왔다. 아직도 임베디드 환경에서는 일반적으로 CPU가 하나이기 때문에 동기화에 관하여 큰 신경을
쓰지 않기도 한다. 하지만 지금은 다 옛말이 됐다. 리눅스 커널이 2.6으로 발전하면서 선점형 커널(Preemptible Kernel)을 지원하기 시작했기 때문이다. 선점형 커널이란 커널 자체가
선점될 수 있다는 것이다. 즉, 커널이 특정 코드를 수행하고 있는 도중 선점되어 커널의 다른 부분의 코드를, 또는 전에 선점되기 직전에 실행하고 있던 코드를 다시 수행할 수 있다는 것
이다. 전에 수행하던 코드로 재진입한다는 것은 결국 SMP와 다를 바 없게 만든다. 이로 인하여 Up(Uniprocessor)환경에서의 Lock 또한 복잡해지기 시작했다. 그렇다. 이번 호에서 다룰
주제는 리눅스 커널의 동기화에 관한 것이다. 동기화 문제는 커널 프로그래밍에 있어 가장 까다롭고 어려운 부분이다. 처음부터 동기화 문제를 고려하고 제대로 설계하지 않으면 가장
발생하기는 쉬운 반면, 해결점을 찾기는 가장 어려운 것이 바로 이 동기화 문제이다. 이번 호에서는 동기화 문제를 겪지 않기 위해 커널에 사용되는 동기화 기법 중 대표적인 몇 가지를
살펴보기로 한다
1. 동시성(Concurrency) 문제
일반적으로 공유된 자원을 조작하는 코드가 있는 부분을 경쟁구간(Critical Region)이라고 한다. 경쟁구간의 코드는 원자적(Atomic)으로 수행되어야만 한다. 어떤 코드가 원자적으로 수
행되어야 한다는 것은 경쟁구간의 코드가 다른 코드에 의해 방해받지(Interrupted) 않고 최초 소유주가 계속해서 제어권(Control)을 갖고 실행하여야 한다는 것이다. 이런 상황이 지
켜지지 않으면 경쟁상태(Race Condition) 문제가 발생하여 예상치 못한 결과를 발생시키게 된다. 현재의 리눅스 커널은 많은 동시성 문제를 가지고 있다. 이런 문제를 일으키는 원인들은 다음과 같다.
● 인터럽트(interrupt)
● 선점가능한 커널(preemptible kernel)
● smp
● 지연 함수(delayed function)
각각을 살펴보면 다음과 같다.
인터럽트는 비동기적인 이벤트이다. 그러므로 인터럽트가 disable되어 있지 않는 한 언제든지 커널 코드의 수행도중 인터럽트는 발생될 수 있다. 이때 문제가 되는 것은 인터럽트가
발생하는 시간에 커널에서 수행되고 있던 코드가 커널의 공유자원을 사용하고 있을 경우이다. 또는 수행중이었던 함수가 재진입 가능하게 설계되어 있지 않은 경우이다. 인터럽트 핸
들러에서 선점되었던 함수로 재진입하거나 또는 인터럽트 핸들러에서 호출한 함수가 선점되었던 코드가 사용중이었던 커널의 공유자원에 대한 업데이트를 할 경우 자원의 안정성을
깨뜨리는 문제가 발생할 수 있다.
커널 버젼이 2.6으로 올라가면서 커널 자체가 선점 가능하게 바뀌었다. 커널이 선점 가능하다, 하지 않다는 것은 다음과 같은 차이가 있다. 먼저 커널이 선점 가능하지 않다는 것은 커널
의 코드를 수행 도중 자신이 직접 제어권을 양보하지 않는 한 계속해서 제어권을 가지고 수행하는 것이다. 반면 커널이 선점 가능하다는 것은 커널의 코드 수행 중이라도 자신의 의지
와는 상관없이 다른 프로세스로 제어권을 양보할 수 있다는 것이다. 이 차이는 커널 프로그래머의 입장에서는 큰 변화로 느껴질 수 밖에 없다. 왜냐하면 자신이 만든 코드가 언제 선점되어 재진입되거나 또는 공유 자원이 불안정(Inconsistency)하게 될지 모르기 때문이다.
리눅스 커널이 2.0으로 발전하면서부터 SMP를 지원하기 시작했다. 초창기에는 SMP를 지원한다 하더라도 많은 Lock을 가지고 있지는 않았지만 점차 성능문제가 나타나면서 많은
Lock들이 더 잘게(Fine-Grained) 쪼개지며 현재는 1000여개 이상의 Lock들이 커널 내에 존재하고 있다. SMP에 관한 문제의 근본원인은 특정 경쟁구간이 2개 이상의 프로세서에
서 동시에 실행될 수 있다는 문제에서 비롯된다.
커널은 빠른 응답성을 보장하기 위해 많은 지연(Delayed) 함수(workqueue, softirq, tasklet, timer)들을 지원하고 있다. 지연 함수들의 사용은 특정 태스크의 코드를 수행하는 도중
수행되고 있던 태스크와 전혀 관련되지 않은 코드들이 언제나 호출될 수 있다는 것을 의미한다. 이 또한 앞에서 본 것들과 마찬가지로 재진입이나 공유 자원의 불안정성에 문제를 일으킬
소지를 가지고 있다. 앞으로 이러한 문제들을 막기 위하여 리눅스 커널은 어떤 기법들을 제공하는지 알아보도록 하자. (앞으로 강좌를 진행하면 Lock을 얻는 것을“잡았다”, Lock을 해지하는 것을“풀었다”라는 단어로 사용할 것이다.)
2. semaphore
리눅스에서 세마포어는 Sleeping Lock이다. Sleeping Lock이 의미하는 것은 하나의 태스크가 이미 Lock을 잡고 있는 상태에서 다른 태스크가 Lock을 다시 잡으려고 한다면 세마포
어는 나중에 Lock을 잡으려고 했던 태스크를 wait queue에 넣고 sleep상태로 만들어 버린다는 것을 의미한다. 그리고 세마포어의 lock을 먼저 잡고 있던 태스크가 세마포어를 풀게되
면 세마포어의 wait queue에 대기하고 있는 태스크 중 하나를 깨워서 세마포어를 잡게 만든다. 이러한 특성으로 인해 세마포어는 인터럽트 컨텍스트에서는 사용할 수 없다. 왜냐하면
인터럽트 컨텍스트에서는 태스크 스케줄링이 일어나서는 안되기 때문이다(이 부분은 나중에 자세히 설명하도록 한다.) 그러므로 세마포어를 사용할 수 있는 상황은
관련자료
-
이전
-
다음