Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

15192
888
89788


[라즈베리파이] 동기화 - 스핀락(spinlock): spin_unlock_irq() 소개 9장. 커널 동기화 소개

이번에는 spin_lock_irq() 함수와 같이 쓰는 spin_unlock_irq() 함수를 점검합시다.

spin_unlock_irq() 함수는 spin_unlock() 함수에서 스핀락 기능을 그대로 물려 받았습니다. 대신 스핀락을 해제 한 후 인터럽트를 다시 활성화하고 Preemption을 실행하는 동작만이 추가된 것입니다.

spin_unlock_irq() 함수 동작을 3단계로 나눠서 점검합시다

1단계: 스핀락 해제
스핀락을 획득하면 스핀락 인스턴스의 next를 +1만큼 증감합니다. 이와 마찬가지로 owner도 +1만큼 증감시키는 동작을 수행합니다. spin_unlock() 함수와 같은 동작입니다.

2단계: 인터럽트 활성화
spin_lock_irq() 함수를 실행할 때 인터럽트를 비활성화했습니다. 이번에 스핀락을 해제한 후 “cpsie i" 어셈블리 명령어를 실행해서 다시 인터럽트를 활성화합니다.

3단계: Preemption 활성화
spin_lock_irq() 함수 마지막 코드에서 preempt_disable() 함수를 실행해서 Preemption을 잠시 비활성화했습니다. 이번에는 preempt_enable() 함수 실행으로 Preemption을 다시 활성화합니다.

spin_unlock_irq() 동작을 분류하면 1단계는 기존 spin_unlock() 함수 동작과 같고 나머지 2~3단계는 인터럽트와 Preemption 관련 설정 동작입니다.

단계별로 spin_unlock_irq() 함수 실행에 대해 알아봤으니 이제 spin_unlock_irq() 함수 코드 분석을 할 차례입니다.

spin_unlock_irq() 함수 코드는 다음과 같습니다.
1 static inline  void spin_unlock_irq(spinlock_t *lock)
2 {
3  _raw_spin_unlock_irq(&lock->rlock);
4 }
5
6 static inline  void __raw_spin_unlock_irq(raw_spinlock_t *lock)
7 {
8 do { } while (0);
9 do_raw_spin_unlock(lock);
10 do { do { } while (0); arch_local_irq_enable(); } while (0);
11 do { __asm__ __volatile__("": : :"memory"); if (__builtin_expect(!!(__preempt_count_dec_and_test()), 0)) preempt_schedule(); } while (0);
}

3번째 줄 코드를 보면 spin_unlock_irq() 함수는 _raw_spin_unlock_irq() 함수를 호출합니다.

__raw_spin_unlock_irq() 함수 코드를 보면 spin_unlock_irq() 함수 실제 구현부를 볼 수 있습니다.

arch_local_irq_enable() 함수 코드를 보겠습니다.
1 static inline   void arch_local_irq_enable(void)
2{
3 asm volatile(
4   " cpsie i @ arch_local_irq_enable"
5  :
6 :
7 : "memory", "cc");
8 }

arch_local_irq_enable() 함수는 “cpsie i” ARM 어셈블리 명령어를 실행합니다. 
이 명령어는 어떤 동작을 수행할까요? 

“cpsie i" 이란 어셈블리 명령어는 인터럽트를 활성화하는 동작입니다.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/BABHBAAB.html

위 사이트에서 “cpsie i” 명령어에 대한 상세 설명을 확인할 수 있습니다.

다음 11번째 줄 코드를 보겠습니다.
11 do { __asm__ __volatile__("": : :"memory"); if (__builtin_expect(!!(__preempt_count_dec_and_test()), 0)) preempt_schedule(); } while (0);
}

__preempt_count_dec_and_test() 함수를 호출해서 current_thread_info()->preempt_count를 -1만큼 증감합니다. 만약 결과값이 0이고 Preemption을 실행할 조건이면 preempt_schedule() 함수를 실행해서 Preemption 을 수행 합니다.


#Reference 시스템 콜


Reference(워크큐)
워크큐(Workqueue) Overview


덧글

댓글 입력 영역