Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

48111
637
415424


[리눅스커널][스케줄링] 컨택스트 스위칭 관련 자료 구조 알아보기 10. 프로세스 스케줄링

컨택스트 스위칭 관련 자료 구조 알아보기
이전 소절에서 컨택스트는 프로세스가 실행 중인 그 자체라고 소개했습니다. 프로세스 실행 그 자체는 레지스터 세트 표현할 수 있습니다. 어떤 프로세스가 CPU에서 실행하면 CPU 레지스터 세트에 프로세스가 실행 중인 코드와 함수 정보가 채워집니다.

컨택스트 스위칭으로 CPU 레지스터 세트를 어딘가에 저장할 것입니다. 그러면 CPU 레지스터 세트를 표현하는 자료구조는 무엇일까요?

다음 8 번째 줄 struct thread_info 구조체에서 cpu_context 필드입니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/arch/arm/include/asm/thread_info.h]
1 struct thread_info {
2 unsigned long flags; /* low level flags */
3 int preempt_count; /* 0 => preemptable, <0 => bug */
4 mm_segment_t addr_limit; /* address limit */
5 struct task_struct *task; /* main task structure */
6 __u32 cpu; /* cpu */
7 __u32 cpu_domain; /* cpu domain */
8 struct cpu_context_save cpu_context; /* cpu context */

커널은 프로세스를 생성할 때 스택 공간을 생성합니다. 이 스택 공간내에서 함수를 호출하고 실행할 수 있습니다. 이 프로세스 스택 최상단 주소 공간에 struct thread_info 필드 데이터가 있습니다. 이 공간에 레지스터 세트를 백업하거나 로딩합니다.

8 번째 줄 코드 오른쪽 주석문을 보면 역시 /* cpu context */ 이란 주석문을 볼 수 있습니다. struct cpu_context_save 구조체 cpu_context 필드가 컨택스트인 레지스터 세트를 저장합니다.

이번에는 struct cpu_context_save 구조체 선언부를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/arch/arm/include/asm/thread_info.h]
1 struct cpu_context_save {
2 __u32 r4;
3 __u32 r5;
4 __u32 r6;
5 __u32 r7;
6 __u32 r8;
7 __u32 r9;
8 __u32 sl;
9 __u32 fp;
10 __u32 sp;
11 __u32 pc;
12 __u32 extra[2]; /* Xscale 'acc' register, etc */
13 };

struct cpu_context_save 구조체 필드를 보겠습니다. 

2~7번째 줄 코드를 보면 r4, r5, r6, r7, r8, r9 이란 필드와 8~11번째 줄 코드에서 sl, fp, sp, pc 란 필드가 있습니다.

각각 필드들은 CPU에서 실행했던 레지스터 세트를 의미합니다. 

struct cpu_context_save 구조체 각각 필드는 레지스터 세트를 저장합니다. 컨택스트 스위칭되는 prev 프로세스는 실행 중인 레지스터 세트를 백업해야 합니다. 컨택스트 스위칭으로 다음에 실행할 next 프로세스 입장에서 자신의 스택 최상단 주소에 저장된 레지스터를 로딩해야 합니다.

이번엔 Trace32로 본 struct cpu_context_save 구조체 필드 데이터를 소개합니다.
01 (static struct thread_info) (struct thread_info)0x80C00000 = (
02  (long unsigned int) flags = 0x0,
03  (int) preempt_count = 0x1,
04  (mm_segment_t) addr_limit = 0x0,
05  (struct task_struct *) task = 0x80C06C80,
06  (__u32) cpu = 0x0,
07  (__u32) cpu_domain = 0x0,
08  (struct cpu_context_save) cpu_context = (
09    (__u32) r4 = 0xA6B46780,
10    (__u32) r5 = 0x828EA000,
11    (__u32) r6 = 0x9EB07000,
12    (__u32) r7 = 0x81709294,
13    (__u32) r8 = 0x9DB8B200,
14    (__u32) r9 = 0x9F89EA00,
15    (__u32) sl = 0x96B46780,
16    (__u32) fp = 0x9B7DFD64,
17    (__u32) sp = 0x9B7DFCE8,
18    (__u32) pc = 0x80F65224,
19    (__u32 [2]) extra = (0x0, 0x0)),

08 번째 줄에 struct thread_info 구조체 cpu_context 필드가 보이며 struct cpu_context_save 타입입니다. 09~18 번째 줄을 보면 레지스터 세트를 볼 수 있습니다. 세부 필드가 CPU에서 실행했던 레지스터입니다.

위에서 분석한 내용을 정리하면 컨택스트 정보인 레지스터 세트를 struct thread_info 구조체 cpu_context 필드에 저장하고 로딩합니다. 그러면 이 cpu_context 필드는 어디에 저장하고 로딩할까요?

다음 테이블을 보면 다시 prev와 next 프로세스 입장에서 cpu_context 필드를 어떤 방식으로 로딩하고 저장하는지 알 수 있습니다.
 

프로세스 자신의 스택 최상단 주소에 있는 struct thread_info 구조체 cpu_context 필드에 접근해 레지스터를 로딩하고 저장하는 것입니다.

세부 동작은 다음 그림에서 확인할 수 있습니다.
 

prev와 next 각자 프로세스 스택 최상단 주소 공간에 접근해 struct thread_info 구조체 cpu_context 필드에 레지스터를 로딩하고 저장하는 것입니다.

스케줄러에 의해 CPU를 비울 prev 프로세스는 다음 콜스택으로 실행 중이었습니다.
__schedule
schedule_hrtimeout_range_clock
sys_epoll_wait
sys_epoll_pwait
ret_fast_syscall

다음에 실행할 next 프로세스는 이전에 다음과 같은 콜스택으로 실행 중 컨택스트 스케줄링 됐습니다.
__schedule
schedule
futex_wait_queue_me
futex_wait
do_futex
sys_futex
ret_fast_syscall
이번 소절에서 다룬 내용을 한 문장으로 정리할 할 수 있습니다. 
컨택스트를 표현하는 자료구조는 struct cpu_context_save 이며 프로세스 스택 최상단 주소에 저장됩니다.

다음 소절에선 context_switch() 함수 코드 분석으로 컨택스트 스위칭 세부 동작을 살펴보겠습니다.


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

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

Reference(프로세스 스케줄링)

스케줄링 소개
프로세스 상태 관리
   어떤 함수가 프로세스 상태를 바꿀까?
스케줄러 클래스
런큐
CFS 스케줄러
   CFS 관련 세부 함수 분석  
선점 스케줄링(Preemptive Scheduling)   
프로세스는 어떻게 깨울까?
스케줄링 핵심 schedule() 함수 분석
컨택스트 스위칭
스케줄링 디버깅
   스케줄링 프로파일링
     CPU에 부하를 주는 테스트   
     CPU에 부하를 주지 않는 테스트


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

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

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






핑백

덧글

댓글 입력 영역