인터럽트 컨택스트란
인터럽트가 발생하면 즉시 인터럽트 벡터 주소로 프로그램 카운터가 바뀝니다. 이후 인터럽트 서브 루틴이 실행합니다. 현재 수행 중인 코드가 인터럽트가 발생한 다음 호출되는 서브 루틴 중 하나라고 볼 수 있습니다. ftrace와 커널 로그로 조금 더 구체적으로 인터럽트 컨택스트에 대해서 알아보겠습니다.
.
in_interrupt 함수란
리눅스 커널은 수많은 함수들이 각자 서로를 호출하며 실행됩니다. 특정 함수는 커널 쓰레드 레벨에서만 실행되는데 대부분 함수는 인터럽트 컨택스트에서도 호출될 수 있습니다. 인터럽트 서비스 루틴은 실행 중인 프로세스를 멈추고 동작하므로 인터럽트 컨택스트 조건에서만 신속하게 코드를 실행시키고 싶을 때가 있습니다. 그럼 현재 실행 중인 코드가 인터럽트 컨택스트 구간인지 어떻게 알 수 있을까요? in_interrupt()란 매크로가 이 정보를 알려줍니다.
아래 패치는__rh_alloc() 함수가 인터럽트 컨택스트로 호출이 될 때, GFP_ATOMIC옵션으로 빨리 메모리 할당을 시도합니다. 대신 프로세스 컨택스트인 경우 GFP_KERNEL 옵션으로 메모리를 할당합니다. 참고로 GFP_ATOMIC 옵션으로 메모리를 할당하면 프로세스는 휴면하지 않고 메모리를 할당하고, GFP_KERNEL 옵션인 경우 메모리 할당 실패 시 휴면할 수 있습니다.
이제 in_interrupt 코드를 살펴보겠습니다. 아래 코드를 보면 실제 irq_count() 매크로로 매핑되어 preempt_count() 매크로로 처리하는 값과 HARDIRQ_MASK | SOFTIRQ_MASK 비트 마스크와 OR 비트 연산을 수행합니다.
아래는 코어 덤프를 로딩해서 Trace32 프로그램으로 확인한struct thread_info 자료 구조입니다. preempt_count값은 0x00010002로 현재 프로세스가 인터럽트 컨택스트에서 실행 중임을 알 수 있습니다.
irq_enter 함수는 결국 __irq_enter 함수를 호출하는데 __irq_enter 매크로 함수를 자세히 살펴보면 current_thread_info()->preempt_count란 멤버에 HARDIRQ_OFFSET 비트를 더합니다.
이후 인터럽트 핸들러가 수행되고 난 후 struct thread_info.preempt_count 멤버에서 HARDIRQ_OFFSET 비트는 아래 코드 흐름에서 해제됩니다.
아래 코드를 보면 current_thread_info()->preempt_count멤버에서 HARDIRQ_OFFSET 비트를 뺍니다.
최근 덧글