ARM Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

0239
1625
172602


[리눅스커널] thread_info 구조체의 preempt_count - 선점 스케줄링 여부 확인 4. 프로세스(Process) 관리

리눅스 커널의 핵심 동작은 스케줄링입니다. 프로세스는 schedule() 함수를 호출해서 명시적으로 스케줄링할 수도 있지만 인터럽트 처리 후 선점 스케줄링될 수 있습니다. 커널은 스케줄링 동작 중에 thread_info 구조체의 preempt_count 필드에 저장된 값을 보고 선점 스케줄링 실행 여부를 판단합니다.

그런데 여기서 한 가지 의문이 생깁니다. preempt_count 필드가 어떤 값일 때 선점 스케줄링을 시작할까요? preempt_count 필드가 0이면 프로세스가 선점될 수 있는 조건이라고 판단합니다. 그럼 선점 스케줄링 관련 코드를 보면서 세부 동작을 배워봅시다.

https://elixir.bootlin.com/linux/v4.19.30/source/arch/arm/kernel/entry-armv.S
1 __irq_svc:
2 svc_entry
3 irq_handler
4
5 #ifdef CONFIG_PREEMPT
6 ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
7 ldr r0, [tsk, #TI_FLAGS] @ get flags
8 teq r8, #0 @ if preempt count != 0
9 movne r0, #0 @ force flags to 0
10 tst r0, #_TIF_NEED_RESCHED
11 blne svc_preempt
12 #endif

다소 낯선 어셈블리 코드지만 차근차근 코드의 의미를 분석해 봅시다. 먼저 1~3번째 줄을 보겠습니다.

1 __irq_svc:
2 svc_entry
3 irq_handler

__irq_svc 레이블은 인터럽트가 발생했을 때 실행되는 인터럽트 벡터입니다. 2~3번째 줄에서는 인터럽트 핸들링을 수행합니다. 즉, 인터럽트가 발생하면 인터럽트에 대응하는 인터럽트 핸들러 함수를 실행하는 동작을 수행합니다.

6~11번째 줄은 스케줄링을 실행할지 결정하는 어셈블리 코드입니다.

6 ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
7 ldr r0, [tsk, #TI_FLAGS] @ get flags
8 teq r8, #0 @ if preempt count != 0
9 movne r0, #0 @ force flags to 0
10 tst r0, #_TIF_NEED_RESCHED
11 blne svc_preempt

어셈블리 코드를 이해하기 쉽게 C 코드로 표현하면 다음과 같습니다.

01    struct thread_info *current_thread_info = task_thread_info(current);
02
03   if (current_thread_info.preempt_count == 0 ) {
04    if (current_thread_info.flags & _TIF_NEED_RESCHED) {
04 svc_preempt();  // 스케줄링 실행
05       }
06   else {
06          current_thread_info.flags = 0;
06   }

변환된 C 코드의 1번째 줄에서는 프로세스 스택의 최상단 주소에 접근해 thread_info 구조체에 접근하며, 이를 current_thread_info라는 변수에 저장합니다.

03~04번째 줄에서는 다음 두 가지 조건을 검사합니다.

preempt_count 필드가 0인지 여부
flags 필드와 _TIF_NEED_RESCHED(0x2)와의 AND 연산을 수행해 연산 결과가 true인지 여부

두 조건을 모두 만족하면 svc_preempt() 함수를 호출해 선점 스케줄링을 실행합니다.

---
선점 스케줄링의 자세한 동작 방식은 10장 ‘프로세스 스케줄링’의 10.6절에서 상세히 설명합니다.
---

이번 절에서는 thread_info 구조체의 preempt_count 필드를 어떻게 처리하는 살펴봤습니다. 코드 분석으로 다음과 같은 내용을 알게 됐습니다.

인터럽트 컨텍스트 설정

시작: preempt_count += HARDIRQ_OFFSET;
종료: preempt_count -= HARDIRQ_OFFSET;

Soft IRQ 컨텍스트 설정

시작: preempt_count += SOFTIRQ_OFFSET;
종료: preempt_count -= SOFTIRQ_OFFSET;

프로세스 스케줄링 가능 여부

preempt_count가 0이면 선점 스케줄링되는 조건임
 
프로세스의 세부 실행 상태를 preempt_count 필드로 관리하므로 잘 알아둘 필요가 있습니다. 다음 절에서는 thread_info 구조체의 cpu 필드가 어떤 흐름으로 바뀌는지 살펴보겠습니다. 

* 유튜브 강의 동영상도 있으니 같이 들으시면 좋습니다. 




#프로세스

프로세스 소개 
프로세스 확인하기  
프로세스는 어떻게 생성할까?  
유저 레벨 프로세스 실행 실습  
커널 스레드  
커널 내부 프로세스의 생성 과정   
프로세스의 종료 과정 분석  
태스크 디스크립터(task_struct 구조체)  
스레드 정보: thread_info 구조체  
프로세스의 태스크 디스크립터에 접근하는 매크로 함수  
프로세스 디버깅  
   * glibc의 fork() 함수를 gdb로 디버깅하기  


"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2






핑백

덧글

댓글 입력 영역