Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[라즈베리파이] ksoftirqd 쓰레드란 [라즈베리파이]인터럽트후반부

ksoftirqd이란 per-cpu 타입 프로세스입니다. 즉 CPU 개수만큼 생성해서 정해진 CPU 내에서만 실행합니다. ksoftirqd 프로세스는 커널 쓰레드로 Soft IRQ 서비스를 쓰레드 레벨에서 처리합니다. 

리눅스 커널을 탑재한 어떤 시스템에서도 볼 수 있는 친근한 프로세스입니다. 먼저 ksoftirqd 쓰레드를 같이 확인하겠습니다.

다음 사이트를 방문하면 다른 리눅스 시스템에서 ksoftirqd 쓰레드를 확인할 수 있습니다.
[출처: https://zetawiki.com/wiki/Ksoftirqd]
[root@zetawiki ~]# ps -ef | grep ksoftirqd | grep -v grep
root         3     2  0 Jan08 ?        00:00:07 [ksoftirqd/0]
root        13     2  0 Jan08 ?        00:00:10 [ksoftirqd/1]
root        18     2  0 Jan08 ?        00:00:08 [ksoftirqd/2]
root        23     2  0 Jan08 ?        00:00:07 [ksoftirqd/3]
root        28     2  0 Jan08 ?        00:00:07 [ksoftirqd/4]
root        33     2  0 Jan08 ?        00:00:06 [ksoftirqd/5]
root        38     2  0 Jan08 ?        00:00:06 [ksoftirqd/6]
root        43     2  0 Jan08 ?        00:00:07 [ksoftirqd/7]

“ps –ef” 명령어와 grep 명령어를 조합해서 프로세스 목록 중 ksoftirqd 쓰레드만 출력했습니다. Ksoftirqd란 쓰레드 이름 뒤에 숫자가 보이죠? 각각 ksoftirqd 쓰레드가 실행 중인 CPU번호입니다.

이번에는 라즈비안에서 ksoftirqd 쓰레드를 확인해 보겠습니다.
root@raspberrypi:/home/pi# ps axl | grep ksoftirq
1     0     7     2  20   0      0     0 smpboo S    ?          0:00 [ksoftirqd/0]
1     0    14     2  20   0      0     0 smpboo S    ?          0:00 [ksoftirqd/1]
1     0    19     2  20   0      0     0 smpboo S    ?          0:00 [ksoftirqd/2]
1     0    24     2  20   0      0     0 smpboo S    ?          0:00 [ksoftirqd/3]

라즈비안은 CPU4개인 쿼드코어 시스템이므로 CPU번호는 0~3번까지 보입니다.
ksoftirqd 는 시스템 CPU 개수만큼 생성되는데 커널은 다음 규칙으로 ksoftirqd 프로세스 이름을 짓습니다.
"ksoftirqd/[CPU 번호]"

ksoftirqd/0 쓰레드는 CPU0에서만 돌고 ksoftirqd/1, ksoftirqd/2 그리고 ksoftirqd/3 쓰레드도 마찬가지로 해당 CPU(CPU1/CPU2/CPU3)에서 수행합니다. 조금 전문적인 용어로 이런 프로세스를 per-cpu 쓰레드라고 합니다. 라즈베리안에서는 4개 ksoftirqd 프로세스가 보이니 CPU가 4개인 쿼드코어 시스템이라는 사실을 유추할 수 있습니다.

그럼 ksoftirqd 프로세스는 언제 생성할까요? 다음과 같이 spawn_ksoftirqd 함수에서 생성합니다. spawn_ksoftirqd 함수 앞에 __init란 매크로가 보이니 커널이 부팅할 때 실행합니다.
static __init int spawn_ksoftirqd(void)
{
cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, "softirq:dead", NULL,
  takeover_tasklets);
BUG_ON(smpboot_register_percpu_thread(&softirq_threads));

return 0;
}

ksoftirqd 프로세스 선언부를 보면 ksoftirqd 프로세스가 실행하면 run_ksoftirqd 함수가 수행한다는 사실을 알 수 있습니다.
static struct smp_hotplug_thread softirq_threads = {
.store = &ksoftirqd,
.thread_should_run ksoftirqd_should_run,
.thread_fn run_ksoftirqd,
.thread_comm = "ksoftirqd/%u",
};

ksoftirqd와 같은 per-cpu 타입 쓰레드는 smp 핫플러그 쓰레드로 등록해서 실행합니다. 

간단히 설명을 드리면, 리눅스 커널에선 시스템 부하가 떨어졌을 때는 여러 개의 CPU가 실행할 필요가 없습니다. 예를 들어 라즈베리파이에서 음악이나 동영상 재생을 안 하고 아무 프로그램도 실행을 안 한 상태로 두면 1개 CPU만 실행합니다. 시스템 부하에 따라 CPU를 끄고 키는 동작을 하는데 이때 smp_boot 쓰레드가 동작합니다.

ksoftirqd 쓰레드는 각 CPU마다 생성된 프로세스입니다. 예를 들면 "ksoftirqd/2" 쓰레드는 CPU2에서만 일을 합니다.  그런데 CPU2가 꺼져 있으면 안 되겠죠. CPU2가 꺼지는 동작을 유식하게 CPU Hot-plugout이라고 합니다. 

만약 smp_boot는 "ksoftirqd/2"에서 더 처리해야 할 Soft IRQ 서비스가 있는데 CPU2가 Hotplug-out될 상황이면 이 Soft IRQ 서비스를 "ksoftirqd/3”와 같이 깨어 있는 다른 ksoftirqd 쓰레드가 실행하게 처리합니다.

smpboot를 관리하는 함수는 smpboot_thread_fn() 이며 다음 코드와 같습니다.
1 static int smpboot_thread_fn(void *data)
2 {
3 struct smpboot_thread_data *td = data;
....
4 if (!ht->thread_should_run(td->cpu)) {
5 preempt_enable_no_resched();
6 schedule();
7 } else {
8 __set_current_state(TASK_RUNNING);
9 preempt_enable();
10 ht->thread_fn(td->cpu);
11 }

smpboot 에 핫플러그인으로 등록된 ksoftirqd 쓰레드의 thread_fn 멤버로 등록한 run_ksoftirqd() 함수는 smpboot_thread_fn() 함수의 다음 10번 줄 코드에서 실행합니다.

다음에 볼 코드는 ksoftirqd 프로세스가 실행될 때 호출되는 run_ksoftirqd 함수입니다
1 static void run_ksoftirqd(unsigned int cpu)
2 {
3 local_irq_disable();
4 if (local_softirq_pending()) {
5 __do_softirq();
6 local_irq_enable(); 
7 cond_resched_rcu_qs();
8 return;
9 }
10 local_irq_enable();
11 }

4 번째 줄 코드를 보면 local_softirq_pending 함수를 호출해서 요청한 Soft IRQ 서비스가 있는지 점검합니다. 만약에 Soft IRQ 서비스 요청이 있으면 __do_softirq() 함수를 호출해서 Soft IRQ 서비스 핸들러를 실행합니다.

이번에는 ksoftirqd 쓰레드를 제어하는 ksoftirqd_should_run 함수를 살펴보겠습니다.
static int ksoftirqd_should_run(unsigned int cpu)
{
return local_softirq_pending();
}

함수 이름과 같이 ksoftirqd 쓰레드를 실행 여부를 알려주는 임무를 수행합니다.
이번에도 local_softirq_pending 함수를 호출해서 Soft IRQ 서비스 요청이 있으면 ksoftirqd 쓰레드를 실행 여부를 알려줍니다.

전체 Soft IRQ 구조에서 ksoftirqd 쓰레드는 어떤 역할을 할까요? 다음 그림에서 검은색으로 된 블록을 보시면 됩니다.

인터럽트가 발생한 후 irq_exit 함수로 시작해서 __do_softirq 함수에서 Soft IRQ 서비스 핸들러를 실행합니다. __do_softirq 함수에서 Soft IRQ 서비스 핸들러 실행 시간이 2ms 초를 넘어서면 하던 일을 멈추고 ksoftirqd 프로세스를 깨웁니다. Soft IRQ 서비스는 프로세스 실행을 멈춘 상태에서 동작하므로 실행 시간이 길면 시스템 응답 속도가 느려지거나 오동할 수 있기 때문입니다.

여기까지 ksoftirqd 쓰레드가 어떻게 생성되고 Soft IRQ 구조에서 어떤 역할을 수행하는지 알아봤습니다. ksoftirqd 쓰레드는 IRQ Thread와 비슷한 역할을 합니다. ksoftirqd 쓰레드는 프로세스 레벨에서 Soft IRQ 서비스를 처리하는 임무를 수행합니다. 다른 관점으로 Soft IRQ가 인터럽트 후반부 처리를 할 때의 주인공이 ksoftirqd 쓰레드인 것입니다.

#Reference 시스템 콜


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



핑백

덧글

댓글 입력 영역