greendrm
다음 문서는
![]() 1.1.1. 원자적인 정수 연산 ¶원자적 정수 연산은 특별한 자료 구조인 atomic_t를 사용한다.
리눅스 커널 2.6.11의 include/asm/atomic.h에 선언된 내용은 다음과 같다.
typedef struct { volatile int counter; } atomic_t;
책의 내용중 sparc에서는 24비트만을 사용한다는 부분은 초기 커널 버전에서의 이야기이다. 현 커널 2.6.11에서는 앞서 보는 것과 같은 자료형으로 선언되어 있으며 spin_lock_irqsave/spin_unlock_irqrestore를 이용하여 원자적 동작을 처리하고 있다. 이에 대한 부분은 커널 소스 include/asm-sparc/atomic.h와 arch/sparc/lib/atomic32.c를 보라.
atomic_t는 다음과 같이 정의한다.
atomic_t u; /* 초기화 하지 않은 상태로 u를 정의 */
특히 다음과 같이 초기화 하지 않도록 주의해야 한다.
atomic_t v = ATOMIC_INIT(0); /* v를 정의하고 0으로 초기화 */ atomic_t v = 0; /* 잘못된 초기화다 */
함수들은 모두 간단한 형태로 돼 있다.
atomic_set(&v, 4); /* v->counter = 4 (atomically) */
관련 함수는 <asm/atomic.h>에서 찾을 수 있다.
관련 함수 목록은 책 p119의 표 8.1 원자적 정수 연산 목록을 참고하라.
atomic_add(2, &v); /* v->counter = v->counter + 2 = 6 (atomically) */ atomic_inc(&v); /* v->counter = v->counter + 1 = 7 (atomically) */ 1.1.2. 원자적인 비트 연산 ¶커널은 원자적인 정수 연산과 함께, 비트 단위로 동작하는 함수들을 제공한다. 이들 함수는 <asm/bitops.h>에 정의되어 있다.
자세한 내용은 책 p120, 121을 참고한다. 관련 함수 목록은 책 p121의 표 8.2 원자적 비트 연산 목록을 참고하라. 1.2. 스핀록 ¶모든 위험구역(critical region)을 하나의 변수를 늘리는 것과 같은 같은 간단한 동작으로 항상 보존할 수 없다. 이러한 경우에 충분한 보호를 제공할 필요가 있으며 이를 위해 록(lock)이 필요하다.
리눅스 커널에서는 이를 위해 가장 많이 사용하는 것이 스핀록(spin lock)이다. 스핀록은 최대한 하나의 스레드에 의해 잠길 수 있는 록을 말한다. 만약 어떤 스레드가 이미 잠겨진 스핀록을 다시 잠그려 시도한다면 그 스레드는 루프(busy loop)를 돌면서(spin) 록을 잠글 수 있을 때까지 기다린다. 스핀록은 가볍고 소유자가 하나인 록으로서 짧은 기간 동안만 소유 돼야 한다. 스핀록과 관련된 인터페이스는 <linux/spinlock.h>에 정의되어 있다. 스핀록의 기본적인 사용법은 다음과 같다.
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
스핀록은 SMP에서만 유효하다. UP 기기에서는 컴파일시 록이 제거되어 포함되지 않는다. 그러나 커널의 선점이 가능한가를 나타내는 표지로 사용될 수 있다. 만약 커널 선점이 비활성화 되어 있는 경우에는 컴파일 시 완전히 제거된다.
spin_lock(&mr_lock); /* critical region ... */
spin_unlock(&mr_lock);
주의: 스핀록은 재귀적이지 않다 스핀록은 인터럽트 핸들러에서도 사용 가능하다. 인터럽트 핸들러에서 록을 사용할 경우에, 커널 내의 데이터를 인터럽트 핸들러에서 공유하는 경우 록을 얻기 전에 반드시 로컬 인터럽트를 비활성화해야 한다. 이때 로컬 인터럽트의 활성화 여부를 알 수 없다면 다음과 같은 인터페이스를 이용해야 한다. spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
unsigned long flags;
만약 인터럽트 활성화 상태임을 알고 있다면 다음의 인터페이스를 사용해도 된다.
spin_lock_irqsave(&mr_lock, flags); /* critical region ... */
spin_unlock_irqrestore(&mr_lock, flags);
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
스핀록 디버깅
spin_lock_irq(&mr_lock); /* critical region ... */
spin_unlock_irq(&mr_lock);
설정 옵션의 하나인 CONFIG_DEBUG_SPINLOCK은 스핀록 코드의 많은 디버깅 검사를 활성화시킨다. 1.2.2. 스핀록과 보톰하프 ¶
1.3. 리더-라이터 스핀록 ¶록의 사용이 리더(reader)와 라이터(writer)로 확연히 구분되는 경우에 사용한다.
읽는 중인 경우에는 쓰기만 방지해야 하며, 쓰기 중일 때는 읽기와 쓰기 모두 방지해야 한다. 리더-라이터 스핀록을 초기화하려면 다음과 같이 한다. rwlock_t mr_rwlock = RW_LOCK_UNLOCKED;
리더인 경우에는 다음과 같이 사용한다.
read_lock(&mr_rwlock);
라이터인 경우에는 다음과 같이 사용한다.
/* critical section (read only) ... */
read_unlock(&mr_rwlock);
write_lock(&mr_lock);
리더록을 한 상태에서 라이트록을 할 수 없다.
/* critical section (read and write) ... */
write_unlock(&mr_rwlock);
read_lock(&mr_rwlock);
write_lock(&mr_rwlock);
이와 같은 경우엔 데드록에 빠지게 된다.
만약, 리더와 라이터가 섞이는 경우가 있다면 이때는 스핀록을 사용한다.
그리고 인터럽트 핸들러가 있을 때 프로세스 컨텍스트에서는 리더 스핀록을 인터럽트 핸들러에서는 라이터 스핀록을 사용한다면 인터럽트를 비활성화 시키는 록을 사용해야만 한다. 관련 함수는 책 p127의 표 8.4 리더-라이터 스핀록 함수를 참고하라. 1.4.1. 세마포어를 생성하고 초기화 ¶세마포어 구현은 <asm/semaphore.h>에 정의돼 있다.
자료형은 struct smeaphore 이다.
세마포어는 정적과 동적으로 생성할 수 있다.
1.4.2. 세마포어 사용 ¶세마포어를 얻는 방법으로는 두가지가 있다.
up(&mr_sem); 관련함수는 책 p130 표 8.5 세마포어 함수를 참고하라. 1.4.3. 리더-라이터 세마포어 ¶스핀록과 같은 방식으로 스핀록보다는 세마포어를 이용해야 하는 경우에 사용한다.
리더-라이터 세마포어는 <linux/rwsem.h>에 정의된 struct rw_semaphore 자료형에 의해 표현된다.
선언 방법은 다음과 같다.
1.5. 완료 변수 ¶완료 변수(completion variable)는 커널의 두 태스크를 동기화시키기 위해 사용되는 편리한 방법 중 하나이다.
완료 변수는 <linux/completion.h> 정의된 struct completion 자료형에 의해 표현된다.
선언은 다음과 같다.
관련 함수는 책 p132 표 8.7 완료 변수 함수을 참고하라. 1.6. 큰 커널 록 ¶큰 커널 록(Big Kernel Lock(BKL)은 전역 스핀록이다.
1.7. Seq 록 ¶커널 2.6에서부터 도입된 새로운 종류의 록이다. 이 록은 시퀀스 카운터(sequence counter)를 사용하여 구현된다. 록은 0부터 시작하여 록을 잠그면 홀수가 되고 다시 록을 풀면 짝수가 된다.
seqlock_t mr_seq_lock = SEQLOCK_UNLOCKED; write_seqlock(&mr_seq_lock); /* write lock is obtained ... */
write_sequnlock(&mr_seq_lock);
unsigned long seq; do { seq = read_seqbegin(&mr_seq_lock);
} while (read_seqretry(&mr_seq_lock, seq));
/* read data here ... */
|
Everybody ought to have a friend. |