Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

848
469
422438


[라즈베리파이] 스케줄링: 프로세스 상태 변화 관리 10. 프로세스 스케줄링

프로세스 상태 변화
프로세스 상태 변화에 대해 알아보겠습니다. 프로세스 상태를 아는 것 보다 프로세스 상태가 변경될 때 세부 동작을 이해하는 것이 더 중요합니다.
 

[1] 실행 대기 -> CPU실행 중
프로세스가 생성된 다음 바로 TASK_RUNNING(실행 대기) 상태로 바뀌면서 자신을 런큐에 Enqueue 합니다. 스케줄러가 RUNNING_TASK(실행 대기) 프로세스 중에서 우선 순위가 가장 높은 프로세스를 선택한 다음 CPU에서 실행시킵니다.

프로세스가 CPU를 점유하면서 실행하기 위해서 먼저 TASK_RUNNING 상태로 변경해야 합니다. 실행 후보 프로세스 리스트가 되는 것입니다. 스케줄러에 의해 선택되기 위해 런큐에 Enqueue된 다음 실행 대기 상태를 관리하는 연결 리스트에 등록하는 것입니다.

여기서 한 가지 생각을 해봅시다. 프로세스가 생성된 다음 스스로 자신을 실행할 수 있을까요?
프로세스는 스스로 자신이 실행하고 싶을 때 실행할 수 없습니다. 프로세스는 스케줄러에 의해 실행을 당하는 것입니다. 달리 말하면 스케줄러가 프로세스를 실행하는 것입니다.

스케줄러가 런큐에 있는 실행 대기 목록에 있는 프로세스 중 우선순위가 가장 높은 프로세스를 선택하고 난 다음 CPU로 데리고 갑니다. 기존에 CPU에서 실행 중인 프로세스를 CPU에서 비우고 선택한 프로세스를 CPU에서 실행시킵니다.

CPU 레지스터 세트에서는 새롭게 실행할 프로세스 실행 정보(프로그램 카운터, 스택, 입력 인자) 등등으로 채워지게 됩니다. 스케줄러 현재 CPU를 점유하면서 실행 중인 프로세스보다 우선 순위가 높은 프로세스를 실행하려고 판단을 내립니다. 이 때 CPU를 점유하면서 실행 중인 프로세스를 CPU에서 비우고 새로운 프로세스를 CPU에 채워 줍니다. 이 때 프로세스는 current 프로세스라고 부릅니다.

[2] CPU 실행(TASK_RUNNING) -> 휴면(TASK_INTERRUPTIBLE)
보통 프로세스는 이 흐름으로 휴면에 진입합니다. 프로세스가 다시 깨어나길 기다리거나 지정된 시간 동안 기다리는 상태입니다.

프로세스가 다시 깨어날 조건이 되면 프로세스는 다시 TASK_RUNNING(실행 대기) 상태로 변경하면서 런큐에 Enqueue를 합니다. TASK_RUNNING(실행 대기) 상태로 설정된 프로세스는 스케줄러에 의해 선택되면 CPU에서 실행할 수 있습니다.

[3] CPU 실행 중(TASK_RUNNING) -> 휴면(TASK_UNINTERRUPTIBLE)
프로세스가 특정 조건에서 깨어나 실행하고 싶을 때 TASK_UNINTERRUPTIBLE 상태로 변경합니다. 대부분 프로세스는 다음과 같은 순서로 동작합니다.
+ 자신이 깨어날 조건을 설정
+ TASK_UNINTERRUPTIBLE 상태로 변경
+ schedule() 함수 호출로 휴면에 진입

스케줄링 관점으로 분석하면 명시적 스케줄링 혹은 비선점형 스케줄링 동작이 일어나는 상황이라고 볼 수 있습니다.

이번에 프로세스 동작 관점으로 TASK_UNINTERRUPTIBLE 상태 변경을 바라보면 프로세스가 데이터 입출력과 관련된 I/O 실행 중이거나 프로세스가 뮤덱스를 획득하지 못했을 때 진입하는 상태입니다.

I/O 실행
+ 프로세스가 I/O 관련 동작을 수행 중일 때 CPU를 쓸 필요가 없기 때문에 다른 프로세스가 CPU를 점유할 수 있도록 스스로 TASK_UNINTERRUPTIBLE 상태로 변경합니다. I/O 동작이 끝나면 다시 TASK_RUNNING(실행 대기) 상태로 변경한 후 다시 실행을 재개합니다.

뮤텍스 획득을 못했을 때
+ 프로세스가 뮤텍스를 획득하지 못했을 때 자신을 TASK_UNINTERRUPTIBLE 상태로 바꾼 다음에 휴면에 진입합니다.

[4] 휴면(TASK_INTERRUPTIBLE) -> 실행 대기(TASK_RUNNING)
wake_up_process() 함수를 호출해서 프로세스를 깨울 때 TASK_INTERRUPTIBLE 에서 TASK_RUNNING(실행 대기) 상태로 변경합니다.

[5] 휴면(TASK_UNINTERRUPTIBLE) -> 실행 대기(TASK_RUNNING)
특정 조건에서 프로세스가 깨어난 후 변경하는 상태입니다. 깨어난 프로세스는 다시 실행 대기(TASK_RUNNING) 상태로 변경됩니다.

프로세스 동작 관점에서 다음 상황에서 TASK_UNINTERRUPTIBLE 상태에서 실행 대기(TASK_RUNNING) 상태로 변경됩니다.
+ I/O 실행 완료
+ 뮤텍스를 해제한 프로세스가 뮤텍스 획득을 위해 휴면에 진입한 프로세스를 깨울 때


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

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

Reference(프로세스 스케줄링)

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


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

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

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


 













핑백

덧글

  • 2020/12/11 15:26 # 답글 비공개

    비공개 덧글입니다.
  • AustinKim 2020/12/11 16:08 #

    언급하신 2개 문장은 모두 프로세스의 상태 변화에 대한 이해를 돕기 위해 완곡하게 표현한 것입니다. 리눅스 커널에 대한 내용을 설명할 때 모든 기술적인 내용을 언급하면 쉽게 설명하기 어렵거든요. 이 점 양해 부탁드립니다.

    프로세스를 UNINTERRUPTIBLE 상태로 바꾸는 주인공에 대해 조금 더 자세히 설명드리면요. 프로세스를 UNINTERRUPTIBLE 상태로 바꾸는 것은 커널의 서브 시스템일 수도 있고 커널 스레드에서 구현된 커널 스레드 함수의 구현부일 수 있습니다.

    아래와 같이 답신을 드리니 참고하셨으면 좋겠습니다.

    1. 만약 I/O 관련 동작을 처리하는 커널 스레드나 드라이버를 직접 구현하셨다면, 프로세스를 UNINTERRUPTIBLE 상태로 직접 변경해 슬립에 진입하고, 프로세스가 깨어나는 조건의 코드를 작성하셔야 합니다.

    2. 프로세스를 UNINTERRUPTIBLE 상태로 변경해주는 커널 함수를 사용하셨으면, 커널 함수의 구현 방식을 조금 더 자세히 파악할 필요가 있습니다. 관련 내용은 아래 포스트를 참고하세요.
    http://rousalome.egloos.com/10003493

    3. 프로세스를 UNINTERRUPTIBLE 상태로 바꾸면 스케줄러의 런큐에서 컨텍스트 스위칭의 대상에서 빼줍니다.
    프로세스의 상태가 UNINTERRUPTIBLE 이면 "이 프로세스는 깨어날 어떤 조건이 있다"라고 판단하는 것이죠.

    혹시 책의 내용을 읽다가 더 궁금한 점이 있으면 댓글로 남겨주세요. 아는 한 성실히 답신드리겠습니다.
  • 2020/12/11 16:09 # 답글 비공개

    비공개 덧글입니다.
  • 꼬질꼬질한 허스키 2020/12/12 13:12 # 답글

    TAS_RUNNKNG(실행 대기)으로 상태가 바뀌거나 TASK_INTERRUPTIBLE 상태로 바뀔 때도 do_nanosleep()을 통해 수행된다고 하셨는데, 둘의 차이가 무엇이며 책에는 이에 대한 설명이 없는거 같습니다.
  • AustinKim 2020/12/13 08:44 #

    do_nanosleep() 함수는 hrtimer 서브 시스템에서 사용되는 호출되는 난이도가 높은 함수라 자세히 설명드리지 못한 점 양해부탁드립니다.

    프로세스의 상태 관점으로 do_nanosleep() 함수를 분석하면 다음과 같으니 참고하시면 좋겠습니다.

    https://elixir.bootlin.com/linux/v4.19.30/source/kernel/time/hrtimer.c
    01 static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
    02 {
    03struct restart_block *restart;
    04
    05hrtimer_init_sleeper(t, current);
    06
    07do {
    08set_current_state(TASK_INTERRUPTIBLE);
    09hrtimer_start_expires(&t->timer, mode);
    10
    11if (likely(t->task))
    12freezable_schedule();
    13
    14hrtimer_cancel(&t->timer);
    15mode = HRTIMER_MODE_ABS;
    16
    17} while (t->task && !signal_pending(current));
    18
    19__set_current_state(TASK_RUNNING);
    20
    21if (!t->task)

    소스 분석:

    * 08번째 줄: 프로세스의 상태롤 TASK_INTERRUPTIBLE 로 변경
    * 11~12번째 줄: 슬립(휴면 상태)에 진입
    * 19번째 줄: 프로세스가 깨어나면 프로세스의 상태롤 TASK_RUNNING 로 변경

    감사합니다.
  • 2020/12/13 08:44 # 답글 비공개

    비공개 덧글입니다.
댓글 입력 영역