IT/Linux Kernel

디버깅을 통해 배우는 리눅스 커널의 구조와 원리 1, 도서 공부하기 30 - Soft IRQ 처리 시점 확인

변화의 물결1 2025. 6. 24. 00:04

 

 

 

안녕하세요.

 

 Soft IRQ 이어서 확인해 보겠습니다. 어떤 프로세스가 언제 처리하는지 확인하는 내용입니다.

 


 

 

1. Soft IRQ 서비스 실행 요청 점검

 

 이전 내용을 확인하면, Soft IRQ 서비스는 인터럽트 핸들링을 마무리한 후 처리했고 커널에서 인터럽트 처리를 시작하고 종료하는 __handle_domain_irq() 함수를 보면 내용을 확인할 수 있었습니다.

 

  __handle_domain_irq() 내에서 generic_handle_irq() 함수 호출(인터럽트 핸들러) 처리를 마치고 irq_exit() 함수를 호출합니다. 바로 Soft IRQ 서비스를 처리하는 시작점이라고 볼 수 있습니다.

 

1) irq_exit() 함수 확인

 

 Soft IRQ 서비스 실행의 시작점인 내용으로 생각하고 확인해 보겠습니다.

 

 

 410번 줄을 보면 preempt_count_sub() 함수를 호출해 프로세스의 thread_info 구조체의 preempt_count 필드에서 HARDIRQ_OFFSET을 빼는 연산을 합니다. 이는 인터럽트 컨텍스트가 아니라고 설정하는 것입니다.

 

 411번 줄은 Soft IRQ 서비스를 요청한 적이 있는지 점검합니다. 이후 invoke_softirq() 함수를 호출합니다.

  

linux# vim kernel/softirq.c

 

 

 

2) invoke_softirq() 함수 확인

 

 362줄 내용을 보면 ksoftirqd가 실행 중인데 Soft IRQ 서비스를 요청한 내용이 내역을 검사해서 실행함수를 종료합니다.

 

 365줄은 force_irqthread 변수를 1로 설정했을 wakeup_softirqd() 함수를 호출해서 ksoftirqd 스레드를 깨웁니다.

 

 

  

 force_irqthread 변수는 리눅스 커널을 부팅하기 전 부트로더에서 threadirqs 커멘드라인으로 전달하면 설정됩니다. 35번 줄에 threadirqs 설정되어 있는 것을 확인할 수 있습니다.

 

linux# vim kernel/irq/manage.c

 

 

 

 

 invoke_softirq() 함수로 다시 돌아가서 366번 줄의 CONFIG 조건에 따라 컴파일됩니다. 그런 보통 라즈베리 파이 커널에서는 CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK 비활성화 돼 있어 do_softirq_own_stack() 함수를 호출합니다.

 

linux# vim include/linux/interrupt.h

 

 

 

2. Soft IRQ 서비스 실행

 

 Soft IRQ 서비스를 실행하다는 것은 Soft IRQ 서비스 핸들러를 호출한다는 의미이며, __do_softirq() 함수에서 이를 처리합니다.

 

1) __do_softirq() 함수 확인

  

 251줄의 변수를 보면, jiffies에 MAX_SOFTIRQ_TIME을 더해 end지역변수에 저장합니다.

 MAX_SOFTIRQ_TIME 매크로는 msecs_to_jiffies(2) 코드로 치환됩니다.

 

  msecs_to_jiffies() 함수는 밀리초 단위의 상수를 입력으로 받아 1/HZ 단위의 시간 정보로 바꾸는 기능입니다. 즉, 2 밀리초를 1/HZ 단위의 시간 정보로 바꾸는 목적의 코드입니다.

 

 234번 줄은 현재 시각을 기준으로 2밀리 초에 해당하는 시간 정보를 end 지역변수에 저장합니다. end 변수는 다음 조건에 따라 _do_softirq() 함수의 실행 시간을 제한하려는 용도입니다.

 

 이렇게 시간을 설정하는 것은 __do_softirq() 함수에서 Soft IRQ 서비스 핸들러를 실행한 시간이 end를 초과하면 함수 실행을 종료하기 위해서입니다.

 

253번 줄을 보면 MAX_SOFTIRQ_RESTART 매크로로 대치됩니다. 이 매크로는 10으로 되어 있습니다. 이는 restart 레이블을 실행할 때마다 --max_restart; 연산으로 max_restart 감소시킵니다. 0이 되면 restart 레이블은 실행되지 못하고 __do_softirq() 함수는 실행 종료 됩니다.

 

위의 시간과 횟수 체크를 한다는 것을 알아 둡니다.

 

 

 

 266줄을 보면 percpu 타입의 irq_stat 변수에서 __softirq_pending값을 지역변수로 읽어 옵니다.

 __do_softirq() 함수가 실행되는 동안 irq_stat이라는 percpu 타입의 전역변수가 업데이트될 수 있기 때문에 전역변수는 지역변수에 저장한 후 연산하게 해서 동기화 문제를 피하게 합니다.

 

 

 

 280번 줄 while문을 조금 유념해서 봅니다. 커널에서 제공하는 ffs라는 라이브러리 함수를 사용해서 pending 변수에서 Soft IRQ 서비스를 설정한 비트를 찾습니다.

 

 ffs() 함수는 비트 위치를 1~32 범위로 계산해 반환합니다. 예로 ffs(20)이라면 2진수로는 10100이 됩니다. 여기서 가장 먼저 1이 세팅된 비트위치인 3을 알려줍니다.

 

 

 284번 줄에서 softirq_bit에서 1만큼 뺀 결과를 저장합니다. 이유는 Soft IRQ 서비스 핸들러 함수를 저장한 soft_irq_vec 배열 위치보다 1만큼 크기 때문입니다.

 

 

 

 이전 글에서 본 softirq_vec를 참조할 수 있습니다.

 

 

 292번 줄에서 softirq_vec 변수에서 저장된 Soft IRQ 서비스의 인덱스에 해당하는 Soft IRQ 서비스 핸들러 함수를 호출합니다. ftrace를 설정했다면 291,293번 줄에 의해 ftrace 메시지를 출력합니다.

 

 

 

300,301번 줄은 pending에서 1로 설정된 비트를 없애서 다음에 1로 설정된 비트를 읽을 수 있게 합니다.

 

 

 

 307번 줄에서 pending 된 Soft IRQ 서비스 있었는지 확인하는데, 이유는 __do_softirq() 함수에서 Soft IRQ서비스 핸들러를 호출하는 동안 누군가 Soft IRQ 서비스를 요청할 수도 있기 때문입니다.

 

 309번 줄에서  jiffies와 end를 인자로 time_before() 타이머 함수를 호출해서 __do_softirq() 함수를 실행한 시간이 2밀리 초를 넘지 않았으면 true, 2초를 넘었으면 false를 반환합니다.

 

 위에서 10번 restart 레이블 카운트한다고 했습니다. 여기서 1 감소와 10번 횟수 검사를 합니다.

 

 313번 줄에서 wakeup_softirqd() 함수에서 ksoftirqd 스레드를 깨웁니다.

 

 

 

3. ksoftirqd 스레드 깨우기

 

  wakeup_softirqd() 함수와 ksoftirqd 스레드 핸들러인 run_ksoftirqd() 함수를 확인합니다.

 

1) wakeup_softirqd() 함수 확인

 

74번 줄에서 CPU별로 생성된 ksoftirqd 스레드의 태스크 디스크립터를 가져옵니다.

76번 줄에서 tsk지역변수가 유효한 태스크 디스크립터 주소인지, ksoftirqd 스레드가 TASK_RUNNING 상태인지 검사해서 예외 처리하도록 합니다.

 

 예외상황이 아니라면 wake_up_process함수를 호출해서 ksoftirqd 프로세스를 깨웁니다.

 ksoftirqd 프로세스가 깨어나면 run_ksoftirqd() 함수가 수행됩니다.

 

 

 

2) run_ksoftirqd() 함수 확인

 

650번 줄을 보면 local_softirq_pending() 함수를 호출해서 Soft IRQ 서비스 요청이 있었는지 검사한 후 __do_softirq() 함수를 호출합니다.

 

 

 

 전체적으로 요약해 보면, 인터럽트 핸들러 수행이 끝나면 요청한 Soft IRQ 서비스가 있었는지 확인하고 __do_softirq() 함수가 2ms 이상이거나 Soft IRQ 서비스 핸들러를 10번 이상 호출했다면 ksoftirqd 스래드를 깨웁니다.

 

 irq_exit() -> invoke_softirq() -> __do_softirq()

 

 

감사합니다.

 

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p473 ~ 490, wikibook

 

 

 

반응형