Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

44111
637
415420


[라즈베리파이] 스케줄링(scheduling)이란 무엇일까? 10. 프로세스 스케줄링

여러분들은 리눅스 시스템이 탑재된 휴대폰이나 라즈베리파이를 쓰면 동시에 여러 프로그램을 실행할 수 있습니다. 휴대폰을 보면 다양한 프로그램이 동시에 실행하는 것을 확인할 수 있습니다. 예를 들면 브라우저를 실행하면서 음악을 듣거나 메신저를 하면서 어플리케이션을 다운로드 할 수 있습니다.

그래서 사람들은 여러 프로세스들이 동시에 CPU에서 실행한다고 느낄 수 있습니다. 하지만 CPU는 여러 개의 프로세스를 절대로 동시에 실행할 수는 없습니다. 리눅스 커널을 포함한 다양한 운영체제에서 스케줄링과 멀티 태스킹 기법이 생겨난 이유는 다음과 같습니다.
CPU는 한 순간에 한 개의 프로세스의 코드만을 실행할 수 있습니다.
 
여러 개의 프로세스들이 효율적으로 번갈아 CPU에서 실행할 수 있게 규칙을 부여하고 프로세스들을 관리하는 소프트웨어 모듈을 스케줄러라고 말합니다.

하나의 프로세스는 CPU에서 실행을 시작하면 계속 CPU에서 실행하는 것이 아니라 실행을 하다가 잠깐 멈추고 다시 실행하는 방식으로 동작합니다. 즉, 프로세스는 CPU를 점유하면서 실행 중인 상태와 실행 대기하는 상태로 계속 변경하는 것입니다.

메모리에 존재하는 여러 프로세스 중에서 실제 CPU에서 실행될 프로세스를 선택하는 일을 스케줄링이라고 말합니다. 이 때 어떤 프로세스를 어떤 방식으로 선택할지를 결정해야 합니다. 

스케줄링 동작은 다음 그림으로 표현할 수 있습니다.
 

CPU에서 실행하려고 대기 중인 Process A ~ Process D 프로세스 중 하나를 선택해서 CPU에서 실행시키는 동작입니다.

스케줄링 동작을 다른 각도에서 살펴보겠습니다. 다음 그림은 프로세스 상태 변화 다이어그램입니다.
 

커널은 프로세스에게 프로세스 상태를 부여합니다. 프로세스가 생성 및 실행된 후 종료할 때까지 위와 같은 상태 변화로 동작합니다.

프로세스가 CPU에서 실행하기 위해서는 실행 대기(TASK_RUNNING) 상태로 변경한 다음 커널 스케줄링에 의해 CPU 실행(TASK_RUNNING) 상태로 변경되어야 합니다.

대부분 보통 프로세스 실행 상태 변화 흐름을 프로세스 1인칭으로 바라볼 때가 많습니다.
이번에는 시스템 전체 관점으로 프로세스 상태 변화 다이어그램을 살펴봅시다.
 

커널에서 실행 중인 전체 프로세스가 각각 어떤 상태로 실행 중인지를 보여주는 그림입니다. 원형으로 표시된 A~N는 각각 프로세스를 의미합니다.

A~D 프로세스들은 실행 대기(TASK_RUNNING) 상태에 있습니다. CPU에서 실행하기 위해 대기 중인 프로세스입니다.

CPU 실행(TASK_RUNNING) 상태를 보면 E 프로세스가 CPU에서 실행 중입니다.

다른 관점으로 스케줄링 동작을 다음과 같이 설명할 수 있습니다.
실행 대기(TASK_RUNNING) 상태에 있는 프로세스 중 하나를 선택해서 CPU 실행(TASK_RUNNING) 상태로 바꿔주는 동작

커널 스케줄링은 프로세스 상태 기준으로 실행 대기 중에 있는 프로세스를 어떤 방식으로 실행할지를 결정합니다. 따라서 프로세스 상태 정보는 매우 중요합니다.

대부분 드라이버 코드를 작성할 때 프로세스 상태를 변경하는 코드를 작성할 필요는 없습니다. 하지만, 커널 스레드 핸들 함수를 구현할 때 프로세스 상태를 변경하는 코드를 입력할 때가 있습니다. 이 때 반드시 set_current_state() 함수를 써서 프로세스 상태를 변경해야 합니다.

그 이유는 다음과 같이 set_current_state() 함수 코드를 보면 알 수 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/sched.h]
1 #define set_current_state(state_value)
2 do {
3 WARN_ON_ONCE(is_special_task_state(state_value));
4 current->task_state_change = _THIS_IP_;
5 smp_store_mb(current->state, (state_value));
6 } while (0)

5 번째 줄을 보면 smp_store_mb() 함수를 호출해서 메모리 배리어를 실행합니다.
메모리 배리어 코드를 추가하면 GCC 컴파일러가 코드 최적화를 위해 코드 위치를 변경하는 것을 방지합니다.

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

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

Reference(프로세스 스케줄링)

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


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

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

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


 



 

핑백

덧글

  • wordi 2022/03/11 03:04 # 삭제 답글

    교수님 안녕하세요
    초면인데 질문을 여쭈어서 정말 죄송합니다
    스케줄링의 원리가 너무 궁금해서 몇주동안 인터넷을 검색하다가 궁금증이 해소가 안되어 교수님께 여쭙는 댓글을 올리게 되었습니다

    저는 학교에서 교수님께 스케줄링을 이렇게 배웠습니다
    프로세스가 타이머로 인한 CPU 인터럽트에 걸려서 커널모드에 진입하게 되는데
    커널모드에 진입한 후 프로세스가 스스로 스케줄링을 하고
    CPU를 물려줄 프로세스를 정하고 반납한다고 배웠습니다

    그런데, 여기서 원리가 너무 궁금했습니다
    프로그램이 실행되면 프로세스가 되고
    프로세스는 개발자가 짜놓은 소스코드를 차례대로 실행하잖아요

    그런데 개발자들이 자기가 만든 응용프로그램에, 커널 내의 스케줄러의 소스코드를 실행하게 하는 시스템콜 같은 소스코드를 직접 넣지는 않는 것 같았습니다
    http://books.gigatux.nl/mirror/kerneldevelopment/0672327201/ch04lev1sec5.html#ch04table03

    저같은 초보자가 허접하게 짠 소스코드도 컴파일 한 다음 실행하면 프로세스가 되어 스케줄링을 하니까요

    그런데, 저는 스케줄러를 호출하라는 소스코드를 적은 적이 없었습니다
    이 프로세스가, 인터럽트로 인해 커널모드로 진입하는 것 까진 이해했는데

    그다음 커널 내의 스케줄러를 어떻게 호출하는건지
    스케줄러의 스케줄링 소스코드를 어떻게 실행하는건지

    그게 이해가 안갔습니다

    파일 입출력을 시킬 때 조차 read() write() 시스템콜을 이용하고
    이건 라이브러리 함수인 fread fwrite가 호출하니
    저는 fread fwrite를 파일입출력할 때 소스코드 안에 쓰지만

    커널의 스케줄러를 사용하라는 소스코드를 적은 적은 없기 때문입니다

    그래서 그게 너무 궁금해서, 이렇게 염치없지만 여쭙고 싶었습니다
    교수님 너무 긴 질문을 읽어주셔서 정말 감사합니다
  • AustinKim 2022/03/11 21:35 #

    (저는 교수가 아니라 개발자이니, 교수라고 안 부르셔도 좋습니다.) (:

    사실 스케줄러 코드를 직접 작성하지 않아도 프로그램이 스케줄링되는 이유는 다음과 같습니다.

    1. 주기적으로 인터럽트가 발생하면 커널 모드에서(유저 모드에서 커널 모드 진입) 스케줄링 동작
    2. 시스템 프로그래밍으로 코드를 작성하면 시스템 콜이 유발돼 커널 모드에서 스케줄링 동작

    최대한 알기 쉽게 이렇게 설명드릴 수 있을 것 같고요.
    리눅스 커널에서 가장 중요한 내용 중 하나인 프로세스와 스케줄러를 배우면 궁금증이 해소될 것입니다.

    감사합니다.
  • wordi 2022/03/14 03:30 # 삭제

    교수님 제가 여쭤봐놓고 늦게 확인해서 정말 죄송합니다
    처음 뵙자마자 여쭈었는데 이렇게 친절하게 답변해주셔서 감사합니다

    교수님 제가 정말 염치없지만 궁금한 점을 하나만 더 받아주시면 정말 김사하겠습니다

    2. 시스템 프로그래밍으로 코드를 작성하면 시스템 콜이 유발돼 커널 모드에서 스케줄링 동작

    라고 가르쳐주신 말씀에서 궁금한 점이 있습니다.

    스케줄링을 동작시키는 시스템 프로그래밍 코드는
    사용자 프로세스가 아닌 다른 프로세스의 소스코드라고 이해해도 되나요?

    예를들면 제가 짠 소스코드인
    #incude<stdio.h>
    int main()
    {
    ...
    }
    이 소스코드에는 스케줄링을 동작시키는 코드를 작성되어있지 않았지만

    이 소스코드가 컴파일 후에 실행이 되다면
    (이것을 프로세스 A라고 부르겠습니다)

    프로세스 A가 인터럽트가 걸려서
    커널모드로 진입하면 스케줄링을 돌리게 됩니다

    그러나 저는 프로세스A의 소스코드를 짤 때
    커널내의 스케줄러를 호출하는 소스코드를 짠 적이 없습니다

    그렇다면 프로세스 A가 커널모드에 진입할 때
    프로세스 A가 자신의 소스코드로 커널 내의 스케줄러를 호출하는 게 아니라

    별도의 다른 프로세스B에게, 다른 사용자프로세스들이 커널모드로 진입할 때 커널 내의 스케줄러를 호출해주는 시스템 프로그래밍 코드가 있다는 말씀으로 이해해도 되는지 여쭙고 싶습니다

    교수님께서 가르쳐주신 프로세스와 스케줄러 단원도 공부하겠습니다
    염치없이 계속 여쭈어서 죄송합니다
    읽어주셔서 감사합니다
  • AustinKim 2022/03/14 17:35 #

    많은 생각을 하게 하는 훌륭한 질문을 주셨네요.
    내일까지 댓글로 답신 드리겠습니다.

    오늘도 즐거운 하루 보내세요.
  • wordi 2022/03/15 13:54 # 삭제

    교수님 초면인 저를 위해서 질문을 받아주시고, 좋게 봐주셔서 정말 감사합니다
  • AustinKim 2022/03/16 21:05 #

    주신 질문에 답을 드리려면 너무 많은 내용을 다뤄야 하므로
    예제 코드를 소개하면서 설명드리겠습니다.

    아래 코드를 예를 들면요. 유저 애플리케이션 코드인데요.

    void raspbian_proc_process(void)
    {
    int proc_times = 0;

    for(proc_times = 0; proc_times < PROC_TIMES; proc_times++) {
    printf("raspbian tracing n");
    sleep(SLEEP_DURATION);
    }

    }

    int main()
    {
    pid_t pid;
    int fork_times = 0;

    printf("About to fork process n");

    pid = fork();

    if ( pid == 0 ) {
    printf("start execution of child processn");
    raspbian_proc_process();
    }

    else if ( pid > 0 ) {
    printf("start execution of parent processn");
    raspbian_proc_process();
    }

    return 0;
    }

    main() 함수가 실행된 다음,
    아래 루틴이 실행될 때도 비동기적으로 인터럽트가 유발돼 커널 공간으로 진입합니다.

    printf("About to fork process n");

    pid = fork();

    유저 애플리케이션이 구동되면 끊임없이 커널 공간으로 진입하면서 스케줄링이 될지를
    체크되고, 스케줄링 된다고 보시면 됩니다.

    raspbian_proc_process(void) 함수 내에 있는 sleep(SLEEP_DURATION); 코드는
    슬립에 진입하는 역할인데요. 이 코드가 실행되면 시스템 콜이 유발되면서 자신을 슬립 상태로 둔
    다음에 스케줄링됩니다. 명시적으로 스케줄링을 요청한다고 볼 수 있겠죠.

    스케줄링의 동작을 전체적으로 이해하려면 먼저 배워야 하는 내용이 많습니다.

    감사합니다.
  • wordi 2022/03/18 11:14 # 삭제

    교수님 가르쳐주셔서 감사합니다
    이번에도 제가 여쭤봐놓고 늦게 확인했네요ㅠㅠ
    프로세스가 시스템콜을 호출할 때마다 커널모드에 진입하게 되는데
    프로세스가 스케줄링 관련 시스템콜을 호출한 것이 아니더라도
    프로세스가 커널모드에 진입하기만 하면 .
    커널내부의 시스템으로 인해
    프로세스가 스케줄링을 돌리게 된다는 말씀으로 이해해도 되나요?
  • AustinKim 2022/03/19 22:43 # 답글

    네, 그렇게 이해하시면 됩니다.
    즐거운 주말 보내세요.
  • wordi 2022/03/20 15:26 # 삭제

    교수님 상세하게 가르쳐주셔서 정말 고맙습니다
  • AustinKim 2022/03/21 07:02 #

    즐거운 한 주 보내세요. (:
  • wordi 2022/03/22 13:30 # 삭제

    넵 교수님도 즐거운 한 주 보내세요
    정말 감사합니다 ㅎㅎ
  • 2022/03/23 17:20 # 답글 비공개

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